unlink() 后从 FIFO 读取

Posted

技术标签:

【中文标题】unlink() 后从 FIFO 读取【英文标题】:Reading from FIFO after unlink() 【发布时间】:2015-08-29 14:23:30 【问题描述】:

我创建了一个 FIFO,写入它并取消链接。 令我惊讶的是,我在取消链接后能够从 fifo 读取数据,这是为什么呢?

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

#define MAX_BUF 256
int main()

    int fd;
    char * myfifo = "/tmp/myfifo";

    /* create the FIFO (named pipe) */
    mkfifo(myfifo, 0666);

    int pid = fork();
    if (pid != 0)
    
        /* write "Hi" to the FIFO */
        fd = open(myfifo, O_WRONLY);
        write(fd, "Hi", sizeof("Hi"));
        close(fd);

        /* remove the FIFO */
        unlink(myfifo);
    
    else 
    
        wait(NULL);
        char buf[MAX_BUF];

        /* open, read, and display the message from the FIFO */
        fd = open(myfifo, O_RDONLY);
        read(fd, buf, MAX_BUF);
        printf("Received: %s\n", buf);
        close(fd);

        return 0;
    


    return 0;

【问题讨论】:

这是正常的 Unix 行为。只要至少一个进程具有通过内核与所述不相似内容相关联的至少一个文件描述符,不相似内容就会被保留。它不是特定于 FIFO 的。它适用于各种文件。 正确的使用方法是什么?我应该什么时候使用 unlink 调用? 打开文件后立即取消链接是完全没问题的。事实上,我认为这比不喜欢 at_exit 的名称要好,因为名称是对实际文件的一种弱引用(有人可能在您的程序运行时替换了该文件,现在您的程序可能正在删除它打开的文件之外的其他内容)。取消链接 + 处理指向未链接内容的文件描述符是 Unix 上一种非常标准的处理方式。 那么在读写器上同时打开/写入后使用unlink就可以了吗? 您只需要取消链接一次。对 unlink 的第二次调用将失败(尽管 C 不会抛出异常)。应该没有过早断开链接的风险,因为只有在对方调用 open 时,在 fifo 上的 open 才会成功。 【参考方案1】:

除非您将 O_NONBLOCK 标志传递给 open(2),否则打开 FIFO 会阻塞,直到打开另一端。来自man 7 fifo

FIFO 必须在两端(读取和写入)都打开,然后才能数据 可以通过。通常,打开 FIFO 块直到另一端 也打开了。

进程可以在非阻塞模式下打开 FIFO。在这种情况下,打开 即使没有人在写入端打开,for read only 也会成功 然而,打开只写会因 ENXIO 失败(没有这样的设备或 地址)除非另一端已经打开。

也就是说,您的父/子进程在打开 FIFO 时会隐式同步。所以当父进程调用unlink(2)时,子进程早就打开了FIFO。所以子进程总是会在父进程调用unlink(2)之前找到FIFO对象并打开它。

关于unlink(2) 的注释:unlink(2) 只是从文件系统中删除文件名;只要至少有一个进程打开了文件(在这种情况下为 FIFO),底层对象就会持续存在。只有在该进程终止或关闭文件描述符后,操作系统才会释放相关资源。 FWIW,这与这个问题的范围无关,但似乎值得注意。

其他一些(不相关的)评论:

不要给孩子打电话wait(2)。它将返回一个错误(您会立即忽略该错误),因为子进程没有派生任何进程。 mkfifo(3)fork(2)open(2)read(2)write(2)close(2)unlink(2) 都可能失败并返回 -1。您应该优雅地处理可能出现的错误,而不是忽略它们。这些玩具程序的常见策略是使用perror(3) 打印描述性错误消息并终止。 如果你只是想要父子之间的通信,使用管道:它更容易设置,你不需要取消链接,它不会暴露在文件系统中(但你需要先用pipe(2)创建它分叉,以便孩子可以访问它)。

【讨论】:

以上是关于unlink() 后从 FIFO 读取的主要内容,如果未能解决你的问题,请参考以下文章

是否应该在刚刚从该 FIFO 读取所有数据之后从 FIFO 块中读取?

在 FIFO 到达时从 FIFO 中读取数据(linux)

关闭后从套接字读取数据

升级图 API 2.0 后从 Facebook 读取新闻提要失败

我想在写入过程完成后从我的二进制文件中读取数据,并且在没有写入过程的情况下读取数据

FIFO:一个进程从不从管道中读取