【问题标题】:pipe2(...) vs pipe() + fcntl(...), why different?pipe2(...) vs pipe() + fcntl(...),为什么不同?
【发布时间】:2026-01-15 13:15:02
【问题描述】:

我正在尝试构建一个可再发行的二进制文件,以放在只有 glibc 2.3 的旧 NAS 上。所以pipe2() 在该机器上不可用,但我正在尝试构建的代码具有以下行:

if (pipe2(info_pipe, O_CLOEXEC | O_NONBLOCK) < 0)
    goto info_pipe_err;

我的理解是pipe2() 存在的原因是通过在打开时使用O_CLOEXEC | O_NONBLOCK 来避免竞争条件,而不是分两步进行。但是在我正在查看的案例中没有线程,所以我想我可以替换为:

if (pipe(info_pipe) < 0)
    goto info_pipe_err;

int direction; // 0=READ, 1=WRITE
for (direction = 0; direction < 2; ++direction) {
    int oldflags;
    oldflags = fcntl(info_pipe[direction], F_GETFL);

    if (oldflags < 0)
        goto info_pipe_err;

    if (fcntl(info_pipe[direction], 
        F_SETFL, oldflags | O_NONBLOCK | O_CLOEXEC) < 0)
        goto info_pipe_err;
}

但它似乎不可互换,因为代码不起作用。为什么不等价?

【问题讨论】:

    标签: c sockets pipe nonblocking


    【解决方案1】:

    (回答我自己的问题,因为我想通了,只是在这里发布以供后人使用。)

    如果您在较新的编译器上为较旧的系统构建二进制文件,则该运行时可能不知道 O_CLOEXEC 的值,因为该标志是随 pipe2() 引入的。如果它知道什么,它就知道FD_CLOEXEC。而且您没有使用F_SETFL 设置它,而是使用F_SETFD,这是一个单独的fcntl() 调用。

    以下替换应该有效:

    if (pipe(info_pipe) < 0)
        goto info_pipe_err;
    
    int direction; // 0=READ, 1=WRITE
    for (direction = 0; direction < 2; ++direction) {
        int oldflags;
        oldflags = fcntl(info_pipe[direction], F_GETFL);
    
        if (oldflags < 0)
            goto info_pipe_err;
    
        if (fcntl(info_pipe[direction],
            F_SETFL, oldflags | O_NONBLOCK) < 0)
            goto info_pipe_err;
    
        oldflags = fcntl(info_pipe[direction], F_GETFD);
        if (oldflags < 0)
            goto info_pipe_err;
    
        if (fcntl(info_pipe[direction],
            F_SETFD, oldflags | FD_CLOEXEC) < 0)
            goto info_pipe_err;
    }
    

    如前所述,这不具备 pipe2() 提供的线程安全方面的功能,无法一次性完成所有这些工作。

    【讨论】: