3

fcntl我在执行函数时遇到了一个问题stdin,当我将stdinFD 状态标志设置为时O_NONBLOCK,它运行良好,但在副作用范围内。stdout 和 stderr 的状态标志也更改为O_NONBLOCK.

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

我的电脑是 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);

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

4

2 回答 2

4

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

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

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

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

于 2013-10-21T03:45:17.183 回答
1

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

#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.

于 2013-10-21T03:57:35.573 回答