linux重定向标准输入后,再重新打开标准输入为什么会失效?
首先来看一段代码:
#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这个文件的地址了。。。