【问题标题】:Non-blocking read on pipe管道上的非阻塞读取
【发布时间】:2016-08-09 00:14:25
【问题描述】:

可以在管道上进行非阻塞 I/O 吗? fcntl 未能设置 O_NONBLOCK。 Linux 编程接口的第 918 页包含一个表“从管道或 FIFO (p) 读取 n 字节的语义”。此表列出了管道和 FIFO 的行为,其中一列标题为启用了 O_NONBLOCK?这意味着您可以在管道上设置 O_NONBLOCK 标志。它是否正确?以下代码设置标志失败,但 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);
}

【问题讨论】:

  • 这看起来像是F_GETFD 操作中的一个错误。我已经确认这确实将描述符设置为非阻塞并且操作不会阻塞,但F_GETFD 仍然返回零。

标签: c linux pipe glibc


【解决方案1】:

pipe 和 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\0",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 以及获取操作。您不会更改 file descriptor flags 而是更改 file status flags :-)

来自man 3 fcntl

文件描述符标志 以下命令操作与文件关联的标志 描述符。目前,只定义了一个这样的标志:FD_CLOEXEC, 关闭执行标志。如果 FD_CLOEXEC 位为 0,则文件描述符 将在 execve(2) 中保持打开状态,否则将关闭。

文件状态标志 每个打开的文件描述都有某些相关的状态标志,初始化 由 open(2) 初始化并可能由 fcntl() 修改。重复文件 描述符(使用 dup(2)、fcntl(F_DUPFD)、fork(2) 等制作)参考 相同的打开文件描述,因此共享相同的文件状态 标志。

F_SETFL (int) 将文件状态标志设置为 arg 指定的值。文件 访问模式(O_RDONLY、O_WRONLY、O_RDWR)和文件创建标志 arg 中的(即 O_CREAT、O_EXCL、O_NOCTTY、O_TRUNC)被忽略。 在 Linux 上,此命令只能更改 O_APPEND、O_ASYNC、 O_DIRECT、O_NOATIME 和 O_NONBLOCK 标志。这不可能 更改 O_DSYNC 和 O_SYNC 标志;请参阅下面的错误。

【讨论】:

  • 很好,克劳斯,我有一种感觉,我在做一些愚蠢的事情。感谢您的宝贵时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-05
  • 2020-10-27
  • 1970-01-01
  • 1970-01-01
  • 2015-01-25
相关资源
最近更新 更多