LinuxC,为什么fcntl对STDIN的作用也会对STDOUT和STDERR产生影响?

LinuxC,为什么fcntl对STDIN的作用也会对STDOUT和STDERR产生影响?,c,linux,C,Linux,我在stdin上执行功能fcntl时遇到了一个问题,当我将stdinFD状态标志设置为O_NONBLOCK时,它工作正常,但存在副作用。stdout和stderr的状态标志也已更改为O_NONBLOCK 我调查了函数fcntl、SYSCALL\u DEFINE3和do\u fcntl的源代码,但没有得到任何帮助。还有stackoverflow或google。我认为它可能与内核或GLUBC实现有关。 我的电脑是x86_64上的Ubuntu 12.04,安装在gcc 4.6.3中 int fla

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

我调查了函数
fcntl
SYSCALL\u DEFINE3
do\u fcntl
的源代码,但没有得到任何帮助。还有stackoverflow或google。我认为它可能与内核或GLUBC实现有关。 我的电脑是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(标准输出和标准错误)的终端。这意味着:

  • 所有三个标准文件描述符共享相同的打开文件描述
  • 您可以写入标准输入,也可以读取标准输出或标准错误
  • 更改一个文件的文件描述信息会更改其他文件的文件描述信息
  • 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;
    }
    
    在第一种情况下,文件描述是相同的,因此为所有三个设置了
    O_NONBLOCK

    在第二种情况下,一个文件通过管道传输到
    stdin
    ,因此它与
    stdout
    stderr
    具有不同的文件描述,这两种文件都设置了
    O_NONBLOCK


    在第三种情况下,一个文件通过管道传输到
    stdin
    ,而
    stderr
    被重定向到
    /dev/null
    ,因此所有3个文件描述都是不同的,
    O_NONBLOCK
    只为
    stdout

    设置了
    stdout
    ,发布一些代码怎么样?当您不使用tty(例如
    echo”“/您的程序
    )时,您试过吗对谢谢我只是
    gdb a.out
    ,并显示标志,当设置O_NONBLOCK为stdout时,我得到了stdin和stderr都有O_NONBLOCK。它很混乱。~是的。谢谢,CSAPP说有三种类型的打开文件,文件描述符,xxx(我忘了它的名字,谁持有报价计数和偏移量)和vnode。只有一个打开的文件描述符。非常感谢,帮了很多忙。
    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$