在多个进程写入时读取命名管道

Posted

技术标签:

【中文标题】在多个进程写入时读取命名管道【英文标题】:Reading a named pipe while multiple processes are writing to it 【发布时间】:2019-04-16 16:34:36 【问题描述】:

解决这个问题的正确方法是什么?

例如,我有一个名为 write.c 的程序,它有 4 个子进程,并且子进程将它们的 PID 写入单个全局命名管道。

另一个名为read.c的程序应该读取这个PID。

我有一个像下面这样的方法,但是这种方法有一些问题。它不能读取所有的 PID,有时是 3 个,有时是 2 个。我认为有一个同步问题,我该如何解决这个问题? :

writer.c:

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

int main() 
    int fd; 
    char * myfifo = "/tmp/myfifo"; //FIFO file
    char buffer[50]; 

    mkfifo(myfifo, 0666); //creating the FIFO

    for(int i=0;i<4;i++) //creating 4 child process
        if(fork() == 0)  
            fd = open(myfifo, O_WRONLY); //each child process opens the FIFO for writing their own PID.

            sprintf(buffer, "%d", getpid()); //each child process gets pid and assign it to buffer
            printf("write:%s\n", buffer);  // each child process prints to see the buffer clearly

            write(fd, buffer, strlen(buffer)+1); //each child process writes the buffer to the FIFO
            close(fd);

            exit(0); 
         
     
    for(int i=0;i<4;i++)  //waiting the termination of all 4 child processes.
        wait(NULL); 
    
    //parent area
 

reader.c

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

int main(int argc, char **argv)  

    int fd1; 

    // FIFO file path 
    char * myfifo = "/tmp/myfifo"; 

    // Creating the named file(FIFO) 
    mkfifo(myfifo, 0666); 

    char str1[80]; //str2[80]; 
    while (1) 
     
        // First open in read only and read 
        fd1 = open(myfifo,O_RDONLY); 
        read(fd1, str1, 80); 

        // Print the read string and close 
        printf("read: %s\n", str1); 
        close(fd1); 
     
 

【问题讨论】:

在第一个之后 mkfifo 没有得到 EEXIST 吗? 应该,@stark,但这是一个可以忽略的错误。更成问题的是,没有测试该函数或其他任何可能的其他错误。 是的。至少需要检查 mkfifo 上的错误并打开。 【参考方案1】:

这一行将空字节写入fifo:

write(fd, buffer, strlen(buffer)+1);

因此,如果您在管道中有两个 pid,您将读取以下字符串:

1234\02345\0

printf 只会打印到第一个\0

1234

要修复它,将 PID 传输为二进制比格式化和解析文本更容易:

作者:

    if(fork() == 0)  
        fd = open(myfifo, O_WRONLY);
        pid_t pid = getpid();
        write(fd, &pid, sizeof(pid));
        close(fd);
        exit(0); 
     

读者:

fd1 = open(myfifo,O_RDONLY); 
pid_t pid;
while (1) // whatever is your termination condition
 
    read(fd1, &pid, sizeof(pid)); 
    printf("read: %d\n", pid); 
 
close(fd1); 

【讨论】:

也许把它改成 '\n' 所以还有一个分隔符。 或者留下'\0',这是一个非常好的分隔符。 read() 报告它已经传输了多少字节,因此可以知道是否尝试查看空字节。 @JohnBollinger:是的,但这需要额外的解析。 为了避免被忽视,这里的关键点之一是 FIFO(或管道)提供了非结构化的字节流。没有消息边界,因此对管道的写入和读取之间不一定存在 1:1 的对应关系。 为什么需要分隔符?如果你写的是二进制数据,分隔符是没有意义的。

以上是关于在多个进程写入时读取命名管道的主要内容,如果未能解决你的问题,请参考以下文章

Java中的命名管道和多线程

Linux:打开命名管道进行写入时超时

Linux 上的管道读取是原子的吗(多个写入器,一个读取器)?

LINUX FIFO 命名管道 (IPC) 在特定文件描述符处停止写入/读取消息

C命名管道不适用于多进程

命名管道类似于“mkfifo”创建,但双向