【问题标题】:Linux C, Why fcntl act on STDIN will also affect on STDOUT and STDERR?Linux C, 为什么 fcntl 作用于 STDIN 也会影响 STDOUT 和 STDERR?
【发布时间】:2013-10-21 11:57:35
【问题描述】:

我在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);

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

【问题讨论】:

  • 发布一些代码怎么样?
  • 你试过不使用tty的时候(例如echo "" | ./your_program)吗?
  • 是的。谢谢!我只是gdb a.out,并显示标志,当将O_NONBLOCK 设置为stdout 时,我得到stdin 和stderr 都有O_NONBLOCK。很迷茫~

标签: c linux


【解决方案1】:

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

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

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

一个给定的打开文件描述可能与多个文件描述符相关联,或者在单个进程内(在dup()dup2() 之后)或跨进程(因为fork())。

【讨论】:

  • 是的。谢谢,CSAPP说打开的文件分为三种类型,文件描述符,xxx(我忘记它的名字,它保存引用计数和偏移量)和vnode。确切地说,只有一个打开的文件描述符。非常感谢,帮了大忙。
【解决方案2】:

根据乔纳森的回答,这里有一些更理智的代码:

#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,所以这三个文件的描述都是不同的,O_NONBLOCK 只为stdout 设置。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    • 1970-01-01
    • 2010-09-23
    • 1970-01-01
    • 2013-10-27
    • 2013-05-13
    相关资源
    最近更新 更多