Linux C,为什么fcntl作用于STDIN也会影响STDOUT和STDERR?

stdin上执行函数fcntl时,我遇到了一个问题,当我将stdin FD状态标志设置为O_NONBLOCK ,它工作正常,但在一个副作用。 stdout和stderr的状态标志也变成了O_NONBLOCK

我调查了函数fcntlSYSCALL_DEFINE3do_fcntl的源代码,但没有得到任何帮助。 另外stackoverflow或谷歌。 我认为它可能与内核或glibc实现有关。

我的电脑是x86_64上的Ubuntu 12.04,安装了gcc 4.6.3。

  int flag = 0; int value = O_NONBLOCK; int fd = open("./tmp", O_RDONLY); if(-1 == (flag = fcntl(fd, F_GETFL))) fprintf(stdout, "%d:%s\n", errno, strerror(errno)); flag = fcntl(stdin->_fileno, F_GETFL); flag = fcntl(stderr->_fileno, F_GETFL); if(-1 == (flag = fcntl(stdout->_fileno, F_GETFL))) fprintf(stdout, "%d:%s\n", errno, strerror(errno)); flag = fcntl(stdout->_fileno, F_SETFL, flag | O_NONBLOCK); flag = fcntl(fd, F_GETFL); flag = fcntl(stdin->_fileno, F_GETFL); flag = fcntl(stdout->_fileno, F_GETFL); flag = fcntl(stderr->_fileno, F_GETFL); flag = fcntl(stdin->_fileno, F_SETFL, flag | O_APPEND); flag = fcntl(fd, F_GETFL); flag = fcntl(stdin->_fileno, F_GETFL); flag = fcntl(stdout->_fileno, F_GETFL); flag = fcntl(stderr->_fileno, F_GETFL); close(fd); 

这是我的这个问题的代码。

登录过程(或终端打开过程)传统上使用的“技巧”之一是以文件描述符0(标准输入)的读写模式打开终端,然后复制文件描述符1和2(标准输出和标准错误)。 这意味着:

  1. 所有三个标准文件描述符共享相同的打开文件描述。
  2. 您可以写入标准输入并从标准输出或标准错误读取。
  3. 更改文件描述信息为其他人更改。

fcntl()的F_GETFL和F_SETFL选项与打开的文件描述有关。 fcntl()的F_GETFD和F_SETFD选项与文件描述符有关。

一个给定的打开的文件描述可能有多个文件描述符,可能在单个进程(在dup()dup2() )或跨进程(由于fork() )。

从Jonathan的回答来看,下面是一些更为清晰的代码:

 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> void show_nonblock_status(void) { char streams[3][7] = {"stdin", "stdout", "stderr"}; for ( int i = 0; i < 3; ++i ) { int flag = fcntl(i, F_GETFL); if ( flag == -1 ) { perror("error getting flags"); exit(EXIT_FAILURE); } if ( flag & O_NONBLOCK ) { printf("O_NONBLOCK is set for %s\n", streams[i]); } else { printf("O_NONBLOCK is not set for %s\n", streams[i]); } } } int main(void) { show_nonblock_status(); int flag = fcntl(1, F_GETFL); if ( flag == -1 ) { perror("error getting flags"); exit(EXIT_FAILURE); } flag = fcntl(1, F_SETFL, flag | O_NONBLOCK); if ( flag == -1 ) { perror("error getting flags"); exit(EXIT_FAILURE); } show_nonblock_status(); return 0; } 

在各种用途下提供以下输出:

 paul@local:~/src/c/scratch$ ./fct O_NONBLOCK is not set for stdin O_NONBLOCK is not set for stdout O_NONBLOCK is not set for stderr O_NONBLOCK is set for stdin O_NONBLOCK is set for stdout O_NONBLOCK is set for stderr paul@local:~/src/c/scratch$ cat testfile | ./fct O_NONBLOCK is not set for stdin O_NONBLOCK is not set for stdout O_NONBLOCK is not set for stderr O_NONBLOCK is not set for stdin O_NONBLOCK is set for stdout O_NONBLOCK is set for stderr paul@local:~/src/c/scratch$ cat testfile | ./fct 2>/dev/null O_NONBLOCK is not set for stdin O_NONBLOCK is not set for stdout O_NONBLOCK is not set for stderr O_NONBLOCK is not set for stdin O_NONBLOCK is set for stdout O_NONBLOCK is not set for stderr paul@local:~/src/c/scratch$ 

在第一种情况下,文件描述是相同的,所以三个都设置了O_NONBLOCK

在第二种情况下,将文件传送到stdin ,以便对stdoutstderr具有不同的文件描述,两者都设置了O_NONBLOCK

在第三种情况下,文件被传送到stdin ,并且stderr被重定向到/dev/null ,因此所有3个文件描述是不同的,并且O_NONBLOCK仅被设置为stdout