命名管道和分叉令人头疼

Posted

技术标签:

【中文标题】命名管道和分叉令人头疼【英文标题】:headache with named pipes and forks 【发布时间】:2013-02-01 17:42:03 【问题描述】:

我需要编写具有这样结构的程序:

父母先做fifo,然后fork()

child 1 从标准输入读取消息并将其写入命名管道 (FIFO) 然后在父进程中我需要创建管道(未命名)和另一个fork() 子编号 2 从 FIFO 中读取,计算消息长度并通过管道(未命名)将编号发送给父编号。

我用一个 fork 创建了一个简单的程序,孩子可以与父母交流:

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

#define FIFO "/tmp/my_fifo"

int main()

pid_t fork_result;
int pipe_fd;
int res;
char writer[3];
char reader[3];

res = mkfifo(FIFO,0777);
if (res == 0)

    printf("FIFO created!\n");

    fork_result = fork();
    if (fork_result == -1)
    
        fprintf(stderr, "fork error");
        exit(EXIT_FAILURE);
           
    if (fork_result == 0)
    
        printf("CHILD 1\n");
        pipe_fd = open(FIFO, O_WRONLY | O_NONBLOCK);
        scanf("%s", writer);
        res = write(pipe_fd,writer,3);
        if (res == -1)
        
            fprintf(stderr,"error writing fifo\n");
            exit(EXIT_FAILURE);
         
        (void)close(pipe_fd);
        exit(EXIT_SUCCESS);
    
    else
    
        printf("PARENT\n");
        pipe_fd = open(FIFO, O_RDONLY);
        res = read(pipe_fd, reader, 3);
        printf("reader: 0: %c\n",reader[0]);
        printf("reader: 1: %c\n",reader[1]);
        printf("reader: 2: %c\n",reader[2]);
        (void)close(res);
             

else

    printf("deleting fifo... run program again!\n");
    unlink(FIFO);

exit(EXIT_SUCCESS);

它运行良好。所以我创建了具有上述架构的代码:

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

#define FIFO "/tmp/my_fifo"

int main()

pid_t fork_result;
pid_t fork_result2;
int pipe_fd;
int res;
char writer[3];
char reader[3];

res = mkfifo(FIFO,0777);
if (res == 0)

    printf("FIFO created!\n");
    fork_result = fork();

    if (fork_result == -1)
    
        fprintf(stderr, "fork error");
        exit(EXIT_FAILURE);
         

    if (fork_result == 0)
    
        printf("CHILD 1\n");
        pipe_fd = open(FIFO, O_WRONLY | O_NONBLOCK);
        scanf("%s", writer);
        res = write(pipe_fd,writer,3);
        if (res == -1)
        
            fprintf(stderr,"error writing to fifo\n");
            exit(EXIT_FAILURE);
         
        (void)close(pipe_fd);
    exit(EXIT_SUCCESS);
    
    else
    
        printf("PARENt 1\n");
        //don't forget pipe!

        fork_result = fork();
        pipe_fd = open(FIFO, O_RDONLY);
        if (fork_result == 0)
        
           printf("CHILD 2\n");
           res = read(pipe_fd, reader, 3);
           printf("Odczytano: 0: %c\n",reader[0]);
           printf("Odczytano: 1: %c\n",reader[1]);
           printf("Odczytano: 2: %c\n",reader[2]);
           (void)close(res);
         
             

else

    printf("deleting fifo\n");
    unlink(FIFO);

exit(EXIT_SUCCESS);
 

运行顺序是这样的:

PARENT 1
CHILD 1
CHILD 2

所以在父 1 中我打开 FIFO 进行读取,在子 1 中我正在写入 FIFO,子 2 应该读取它。我的意思是在代码中,因为当我运行它时,我什至无法向 FIFO 写入任何内容。在 scanf("%s", writer); 的块中,它在第一个程序中起作用。

我是否正确使用open()?我需要在某处使用getpid() 吗?为什么当我尝试写入 fifo 时它会阻塞。

【问题讨论】:

您好,您不测试第二次分叉是否成功。如果它失败了, printf 不会发生并且 scanf 会挂起......也许你就是这种情况?此外,open(FIFO, 0_RDONLY) 发生在 PARENT1 和 CHILD2 上,在 PARENT1 上似乎没有必要。 我为第二个分叉添加了测试。关于 PARENT1 中的 open(FIFO, O_RDONLY):我需要它,因为当我没有写入 CHILD1 中的 fifo 时出错(没有人打开 fifo 进行读取)... 编辑:我从 PARENT1 中删除了 open,在 CHILD1 中我更改了pipe_fd = open(FIFO, O_WRONLY | O_NONBLOCK);到 pipe_fd = open(FIFO, O_WRONLY);现在它不从键盘读取,而是写入屏幕空阅读器数组... 【参考方案1】:

问题是 CHILD1 正在使用 O_NONBLOCK 打开 fifo,如果没有其他进程打开 fifo 以供读取,它将失败(使用 EWOULDBLOCKEAGAIN)。现在在第一个程序中,父进程在 fork 之后继续运行,并在子进程开始之前打开 fifo 进行读取并打开写端,所以它可以工作。但是在第二种情况下,父级先执行了一个额外的分叉,这会减慢它的速度,以使 CHILD1 在 PARENT 或 CHILD2 打开 fifo 进行读取之前获得其打开命令,因此 CHILD1 打开失败。

去掉O_NONBLOCK,它工作得很好(尽管您确实打开了fifo以在PARENT和CHILD2中读取,这可能不是您想要的)。

如果您想从键盘上阅读,您还有另一个问题。如果您从 shell 运行此命令,PARENT 将立即退出(或多或少),因此 shell 将返回从键盘读取命令,这意味着 CHILD1 和 shell 将争夺输入。另一方面,如果您按照您最初描述的方式进行操作,并让 PARENT 等待从 CHILD2 的管道中读取数据,那么它应该按照您的意愿进行操作。

【讨论】:

我从 CHILD1 中删除了 O_WRONLY 并将 pipe_fd = open(FIFO,O_RDONLY) 从 PARENT1 移动到 CHILD2。程序运行,但我不能从键盘上写任何东西,而 CHILD2 从空的读取器数组中打印出一些垃圾...... :( @lvp: 在 CHILD2 if 块之后立即粘贴 else wait(0); 以使父级在返回 shell 之前等待子级,以避免 CHILD1/shell 输入冲突。【参考方案2】:

不是因为你使用了两次相同的变量fork_result吗?由于您创建了另一个变量 fork_result2,但您不使用它,这可能是无意的。

我不知道这是否能解决你的问题,但至少在第二个分叉处使用 fork_result2 会更容易理解......

【讨论】:

以上是关于命名管道和分叉令人头疼的主要内容,如果未能解决你的问题,请参考以下文章

命名管道(C#)

windows命名管道

windows命名管道

管道和命名管道

c中的命名管道

为啥命名管道是本地的?