Non-blocking 在管道上读取

Non-blocking read on pipe

可以在管道上做 non-blocking I/O 吗? fcntl 无法设置 O_NONBLOCK。 Linux 编程接口的第 918 页包括 table 'Semantics of reading n bytes from pipe or FIFO (p)'。 table 列出了管道和 FIFO 的行为,其中一列标题为 O_NONBLOCK 已启用?这意味着您可以在管道上设置 O_NONBLOCK 标志。这样对吗?以下代码设置flag失败,fcntl(2)虽然不报错

#include <fcntl.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#define SLEEP 1

int
main(int argc, char *argv[]) {
    pid_t childPid;
    int pfd[2];
    int nread, flags;
    int c = 'a';    

    setbuf(stdout, NULL);

    if (pipe(pfd) == -1) {
        printf("error: pipe");
        exit(EXIT_FAILURE);
    }

    switch (childPid = fork()) {
    case -1:
        printf("error: fork");
        exit(EXIT_FAILURE);
    case 0:         /* child */
        if (close(pfd[0]) == -1) {
            printf("child: close pfd read");
            exit(EXIT_FAILURE);
        }
        sleep(SLEEP);
        _exit(EXIT_SUCCESS);
    default:
        break;
                /* parent falls through */
    }
    if (close(pfd[1]) == -1) {
        printf("parent: close pipe write");
        exit(EXIT_FAILURE);
    }

    flags = fcntl(pfd[0], F_GETFD);
    flags |= O_NONBLOCK;
    if (fcntl(pfd[0], F_SETFD, flags))
        perror("fcntl");

    /* verify flags set correctly */
    flags = fcntl(pfd[0], F_GETFD);
    if (!(flags & O_NONBLOCK))  {
        printf("failed to set O_NONBLOCK\n");
        exit(EXIT_FAILURE);
    }

    wait(NULL);
    exit(EXIT_SUCCESS);
}

管道和 O_NONBLOCK 没有什么特别之处。以下示例按预期工作。我没有检查每次调用的每个 retval 以使示例更具可读性。真实世界的应用程序必须进行检查。

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

int main()
{
    int fds[2];
    pid_t pid;
    char buf[100];

    pipe(fds);

    pid = fork();

    if ( pid )
    {
        while (1 )
        {
            memcpy( buf, "abcdefghi[=10=]",10);
            write( fds[1], buf, 10);
            sleep(2);
        }
    }
    else
    {
        int retval = fcntl( fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
        printf("Ret from fcntl: %d\n", retval);
        while (1)
        {
            ssize_t r=read( fds[0], buf, 10 );
            printf("read: %d\n", r);

            if ( r > 0 )
            {
                printf("Buffer: %s\n", buf);
            }
            else
            {
                printf("Read nothing\n");
                perror("Error was");
                sleep(1);
            }
        }
    }
}

写完我的示例后,我检查了您的代码并发现:

flags = fcntl(pfd[0], F_GETFD);
flags |= O_NONBLOCK;
if (fcntl(pfd[0], F_SETFD, flags))

请将 F_SETFD 更改为 F_SETFL 以及 get 操作。你不会改变 file descriptor flagsfile status flags :-)

来自man 3 fcntl

File descriptor flags The following commands manipulate the flags associated with a file descriptor. Currently, only one such flag is defined: FD_CLOEXEC, the close-on-exec flag. If the FD_CLOEXEC bit is 0, the file descriptor will remain open across an execve(2), otherwise it will be closed.

File status flags Each open file description has certain associated status flags, ini‐ tialized by open(2) and possibly modified by fcntl(). Duplicated file descriptors (made with dup(2), fcntl(F_DUPFD), fork(2), etc.) refer to the same open file description, and thus share the same file status flags.

F_SETFL (int) Set the file status flags to the value specified by arg. File access mode (O_RDONLY, O_WRONLY, O_RDWR) and file creation flags (i.e., O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) in arg are ignored. On Linux this command can change only the O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK flags. It is not possible to change the O_DSYNC and O_SYNC flags; see BUGS, below.