无法理解读取系统调用的行为

时间:2011-01-30 09:15:23

标签: c offset segment memory-address

所以这是我试图运行的代码:

#include<fcntl.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<unistd.h>

int main(){

  int ret;

  ret = read(STDIN_FILENO,(int*)2000,3);
  printf("%d--%s\n",ret,strerror(errno));

  return 0;
}

这是我在终端获得的输出

anirudh@anirudh-Aspire-5920:~/Desktop/testing$ gcc test.c 
anirudh@anirudh-Aspire-5920:~/Desktop/testing$ ./a.out 
lls
-1--Bad address
anirudh@anirudh-Aspire-5920:~/Desktop/testing$ ls
a.out  htmlget_ori.c  mysocket.cpp  Packet Sniffer.c  resolutionfinder.c  test.c
anirudh@anirudh-Aspire-5920:~/Desktop/testing$ 

问题1:当我在阅读电话read(STDIN_FILENO,(int*)2000,3);中输入地址2000时 然后地址在哪里。我认为这是我试图访问的RAM的绝对地址。我是对的还是偏移的并且被添加到堆栈段基址。我不知道。该程序没有给我SEGFAULT内存违规,而是给我Bad address

问题2:好的,当我将输入作为lls并且bash执行“lls”的“ls”部分时代码崩溃。原因是代码在读取第一个“l”后崩溃,其余的“ls”部分由bash执行。但为什么bash正在执行左“ls”部分。为什么bash这样做是因为我的代码崩溃了,即使bash是它的父进程,它也不应该从我编写的代码打开的文件描述符(STDIN_FILNO)中读取。 (我想是的)......

感谢您的时间。

3 个答案:

答案 0 :(得分:4)

您尝试用作地址的2000特定于流程的 虚拟地址。机会很好,没有任何东西映射到该范围;您可以添加此代码以查看当前的映射:

char cmd[20];

sprintf(cmd, "pmap -x %i", getpid());
printf("%s\n", cmd);
system(cmd);

如果您真的必须访问2000附近的系统 RAM(我无法想象您是),请使用iopl(2)系统调用来映射该地址进入你的进程内存空间。并注意后果。 :)

至于ls行为的其余部分,请尝试在\n格式字符串中添加printf(),我发现不正确刷新输出可能会导致令人困惑的交互,也许这只是令人困惑,而不是彻头彻尾的奇怪。 :)

答案 1 :(得分:1)

您正在使用分页的CPU上运行。您的操作系统维护从虚拟地址转换为物理地址的页表。您的流程的页面表不包含虚拟地址2000的任何内容,因此read()会发出通知,并返回-EFAULT

stdin已连接到您的终端设备(/dev/tty)。您的进程从shell继承该终端,并且shell在进程退出时将其恢复。

答案 2 :(得分:0)

我只会回答问题1.我不完全理解你在问题2中的意思,而且一旦你解决了第一个问题就可能会自行解决。

要回答您的问题1,而不是百分百肯定,我会打赌(int*)2000指定程序数据段中的位置,即2000只是偏移部分。我之所以这么认为,一般,在任何现代操作系统中,你几乎都无法无限制地访问物理内存。链接器和OS'程序加载器为您处理所有与段相关的东西 - 您的程序只能看到(虚拟 - 参见下面的P.S.)内存地址的偏移部分。与数据相关的所有事情通常都发生在数据段中;代码相关的东西(比如函数调用)通常绑定到代码段。

我认为,您无法保证任何特定数据结构位于数据段的偏移2000。因此,您的read目的地几乎总是无效的,因为它基本上意味着您将数据写入内存中的随机位置。

P.S。:通过虚拟内存地址,我的意思是您的程序段可能会被操作系统加载到不同的物理内存地址。因此,任何段的偏移2000(例如)并不总是意味着相同的绝对物理内存地址;相反,它是一个偏移量,即相对于一个段的基地址,它本身位于任意物理内存地址。