C、是不是可以阻塞进程直到管道再次打开?

Posted

技术标签:

【中文标题】C、是不是可以阻塞进程直到管道再次打开?【英文标题】:C, is it possible to block the process untill the pipe is opened again?C、是否可以阻塞进程直到管道再次打开? 【发布时间】:2015-10-30 20:30:11 【问题描述】:

我正在组装一个类似服务器的进程,它从命名管道接收数据并返回一些输出。

众所周知,当打开管道进行读取时,它会阻塞进程,直到另一个进程打开管道进行写入。 (除非设置了非阻塞标志。) 当另一个进程打开管道并写入它时,我们可以得到这样的输入:

...
opened_pipe = fopen(argv[1], "r")
while(1)
   
   if ( fgets(readbuf, FIFO_READLEN, opened_pipe) != NULL )
        \\ process the input from the writer  
   else
       
       \\ this is the branch when the writer closed his end of the pipe and reader gets EOF
       \\ usually one exits here
       \\ but I would like to freeze the process and wait until another writer comes
       \\ (like a server-like application would do)
       
   

但是当作者退出时,这个while 进入了无意义的循环。 如果阅读器返回到初始状态会更好 - 进程被阻塞,直到管道再次连接到另一端。有可能吗?

PS

我试图在我的程序中创建一个虚拟编写器,它打开与w 相同的管道,并始终在fgets 的循环中保持打开状态。但这对我不起作用。也许我犯了一些错误。有没有可能拉这个把戏?

也可以不断关闭和重新打开while 内的管道。但我想使用pipestdin 作为输入流。最好在程序中以同样的方式对待它们。那么,可以通过fopen 使用一些"stdin" 文件名重新打开stdin 流吗?

【问题讨论】:

如果您在编写器退出时进入循环,这意味着您没有正确测试 EOF 并退出循环。 @Barmar 是的,我不会在 EOF 上退出循环,因为我想等待另一位作家来 考虑使用AF_LOCAL 套接字而不是命名管道。 API 更复杂,但您会获得一大堆好处,包括:您当前的问题 Just Goes Away;您可以一次可靠地与多个客户交谈;完成此操作后,接受网络上的连接只是进一步的一小步。 确实感谢@zwol 的建议,可能套接字更适合这项任务。我尝试使用管道作为更简单的工具。肯定会很快学会使用套接字:) @xealits 是的,这也是我的建议,因为看起来你真的是在在这里实现某种 service 之后。对于处理套接字,您需要决定如何分离客户端......有很多方法:传统的方法是 fork() 每个客户端的一个孩子,更现代的替代方法是 基于事件的 (例如使用select())或线程(使用pthread_create()而不是fork())。无论哪种方式都需要完成一些工作,但您将获得可靠的实现。 【参考方案1】:

只需在服务器进程中打开 FIFO 两次 - 首先是读取,然后是写入。这样做(打开它进行写入)将确保如果所有客户端都放弃 FIFO,您的进程将不会看到 EOF。

这是一个简短的演示:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "/tmp/fifo"

int main()

    if (mkfifo(FIFO_NAME, S_IRUSR | S_IWUSR | S_IWGRP) == -1)
    
        perror("mkfifo");
        exit(EXIT_FAILURE);
    

    FILE *readFd  = fopen(FIFO_NAME, "r");
    FILE *writeFd = fopen(FIFO_NAME, "w");

    char c;
    for (;;)
    
        c = fgetc(readFd);
        fprintf(stdout, "Read char: %c\n", c);
    

【讨论】:

我尝试了这个想法(这确实很棒)。但是我可能犯了一些错误,无法编译并且没有追求它。真的可以吗? 是的,成功了!以前不知何故,我在第二个fopenSIGSEGV 在运行时收到了关于一些转换问题的警告。现在一切都很顺利。【参考方案2】:

不确定我是否完全理解您的问题,但一般来说,当从未打开写入端的管道或 FIFO(又名命名管道)读取时,您将阅读 EOF。当fgets() 读取EOF 时,这将导致缓冲区中的第一个字节为0。你可以检查一下,在这种情况下,关闭 FIFO 并重新打开它,重新进入你的循环。

类似的东西(坚持你的伪 sn-p):

while (1)

    opened_pipe = fopen(argv[1], "r")
    while(1)
    
        if ( fgets(readbuf, FIFO_READLEN, opened_pipe) == NULL ) ...
        else if (!readbuf[0])
        
            fclose(opened_pipe);
            break;
        
    

编辑: 鉴于您在这里的评论,我觉得您可能想要使用 Unix 域套接字 而不是 FIFO。因此,您可以accept() 连接并在等待新连接的同时单独处理它们。

【讨论】:

我明白了。问题是我不想在一位作家完成后退出——我想等待另一位作家来。我将编辑这个问题,因为从代码 sn-p 中确实不清楚。很抱歉造成混乱。 @xealits 等待另一位作家的到来正是这样做的。 @xealits 很好,我用一个可能更适合您想要实现的目标的替代方案编辑了我的答案(如果我在这里没有完全弄错的话)

以上是关于C、是不是可以阻塞进程直到管道再次打开?的主要内容,如果未能解决你的问题,请参考以下文章

如果管道已满,写入管道的进程是不是会阻塞?

read() 不会阻塞在没有 O_NONBLOCK 标志的情况下打开的空 FIFO

如何测试输出到 std::cout (连接到管道)是不是会阻塞

C linux中子进程与父进程之间的通信:父进程不阻塞

C fifo一直被阻塞

低速系统调用的信号中断