共计 1533 个字符,预计需要花费 4 分钟才能阅读完成。
首先来看一段代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd1,fd2;
fd2 = open(“p1.py”,O_RDONLY,0);
dup2(fd2,0);
char c;
while(read(0,&c,1) > 0)
printf(“%c”,c);
close(fd2);
fd1 = open(“/dev/stdin”,O_RDONLY);
printf(“%d\n”,fd1);
while(read(fd1, &c, 1) > 0)
printf(“%c”,c);
return 0;
}
先讲一下文件描述符是什么。linux 进程每打开一个文件都会返回一个文件描述符 (整数)。这个描述符实际是打开的文件在该进程的描述符表上的偏移值。比如说 p 是描述符表,1 是描述符,那么 p[1] 就能够索引到 1 描述符对应的打开文件。有了这个偏移值 (文件描述符) 就能够快速的找到并操作文件。
(当然实际的情况是这个文件描述符能够索引到打开文件表表项,然后再通过打开文件表表项索引到对应的 V -node 节点表表项,而这个 v -node 节点表表项才代表真正的文件。不过只从逻辑上来看不需要理解这个括号里的说明。)
解释一下这段程序:1、首先打开了一个叫做 p1.py 的文件。2、然后用 dup2 这个函数使得文件描述符 0 这个位置的指针指向文件描述符 fd2 这个位置的指针指向的文件。也就是说本来是这样:p[0] = &fiel1,p[fd2] = &file2,现在 p[0] = p[fd2] = &file2。而我们都知道文件描述符 0 这个位置对应的文件 file1 是标准输入文件 /dev/stdin。那么这个函数的意义就是把标准输入重定向到了 p1.py 这个文件里了。
之后再用标准输入比如说 scanf(),来读,那么都是从 p1.py 这个文件里读了。3、然后把这个文件里的东西读出来并输出到屏幕上。4、打开 /dev/stdin 这个文件,这个文件是标准输入。5、把这个文件里的东西读出来并打印到屏幕上。
预期结果是什么?执行到第 5 步应该停下来,等待键盘输入。然后把输入的东西打印到屏幕。
实际结果?没有等待键盘输入,它直接把 p1.py 的文件内容输出到屏幕上,也就是说和上面的输出是一样的!!!
why???how???我打开了一个文件,应该读取文件里的内容。而这个文件是标准输入,那么既然是标准输入(从键盘输入),我还没输入呢,怎么就输出结果了呢???而且结果还很奇特。。。
这是因为,标准输入这个文件 /dev/stdin 是个链接文件!!!它存放的是别的文件的地址!!!如果文件描述符指向的文件是个普通文件,那么把这个文件描述符指向别的文件,就是真的指向了别的文件。而这里的文件描述符指向的是个链接文件,那么把这个文件描述符指向别的文件,意味着什么???意味着它把这个链接文件里的内容 (地址) 更改了,而它仍然指向这个文件。只不过它知道这是个链接文件,因此它会访问的是这个链接文件中的地址对应的文件。
理解了上面的操作就可以说得通了,dup2 对于链接文件只是修改了文件中的地址,它并没有真正指向别的文件,这也导致了一个问题,那就是它把这个链接文件给修改了,如果进程再次打开这个链接文件,那么链接文件之前存的地址就没有了,因此执行
fd1 = open(“/dev/stdin”,O_RDONLY);
这个的时候,实际上是又一次打开了 p1.py 这个文件!!!因为 /dev/stdin 这个文件里存放的地址已经是 p1.py 这个文件的地址了。。。