STDIN_FILENO 和 STDOUT_FILENO 的非阻塞 I/O 行为很奇怪

cam*_*ino 5 linux nonblocking

我有以下代码:

void
set_fl(int fd, int flags) /* flags are file status flags to turn on */
{
    int val;

    if ((val = fcntl(fd, F_GETFL, 0)) < 0)
        err_sys("fcntl F_GETFL error");

    val |= flags;       /* turn on flags */

    if (fcntl(fd, F_SETFL, val) < 0)
        err_sys("fcntl F_SETFL error");
}

int
main(void)
{
    char buf[BUFSIZ];
    set_fl(STDOUT_FILENO, O_NONBLOCK);  //set STDOUT_FILENO to nonblock
    if(read(STDIN_FILENO, buf, BUFSIZ)==-1) { //read from STDIN_FILENO
        printf("something went wrong with read()! %s\n", strerror(errno));
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我设置STDOUT_FILENO为非阻塞模式,但读取操作似乎STDIN_FILENO立即完成。为什么?

$ ./testprog
something went wrong with read()! Resource temporarily unavailable
Run Code Online (Sandbox Code Playgroud)

谢谢

pax*_*blo 5

完全正确:在读取后立即执行 printerrno和调用会导致“资源繁忙”和错误号 11 或,如以下代码所示:perrorEAGAIN/EWOULDBLOCK

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

int main (void) {
    char buf;

    fcntl (STDOUT_FILENO, F_SETFL, fcntl (STDOUT_FILENO, F_GETFL, 0) | O_NONBLOCK);
    fprintf (stderr, "%5d: ", errno); perror("");
    read (STDIN_FILENO, &buf, 1);
    fprintf (stderr, "%5d: ", errno); perror("");
}
Run Code Online (Sandbox Code Playgroud)

生成:

    0: Success
   11: Resource temporarily unavailable
Run Code Online (Sandbox Code Playgroud)

原因是文件描述符有两种不同类型的标志(请参阅此处详细介绍重复文件描述符的部分):

您可以复制文件描述符,或分配另一个引用与原始文件相同的打开文件的文件描述符。重复的描述符共享一个文件位置和一组文件状态标志(请参阅文件状态标志),但每个描述符都有自己的一组文件描述符标志(请参阅描述符标志)。

第一个是文件描述符标志,这些标志对于每个文件描述符来说确实是唯一的。根据文件,FD_CLOEXEC(接近exec)是目前该营地中唯一的一个。

所有其他标志都是文件状态标志,并且在已复制的文件描述符之间共享。其中包括I/O 操作模式,例如O_NONBLOCK.

因此,这里发生的情况是标准输出文件描述符是从标准输入文件描述符复制的(顺序不相关,只是一个文件描述符是从另一个文件描述符复制的事实),因此在一个文件描述符上设置非阻塞模式会影响所有副本(这可能还包括标准错误文件描述符,尽管我还没有确认)。

在重复的文件描述符上使用阻塞模式通常不是一个好主意,也不是在子进程可能继承的文件描述符上使用阻塞模式 - 这些子进程并不总是善意地让它们的标准文件行为不当(从他们的角度来看)。

如果您希望对各个文件描述符进行更细粒度的控制,请考虑在尝试读取之前select使用检查描述符。