c - 命名管道和 fork 头痛

标签 c fork fifo

我需要编写具有如下结构的程序:

父进程创建 fifo,然后 fork()

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

我用一个 fork 创建了一个简单的程序, child 可以在其中与 parent 交流:

#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); 中的 block 中,在第一个程序中工作。

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

最佳答案

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

摆脱 O_NONBLOCK 并且它工作得很好(尽管您打开 fifo 以在 PARENT 和 CHILD2 中读取,这可能不是您想要的)。

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

关于c - 命名管道和 fork 头痛,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14651997/

相关文章:

c - fork() 和 ~fork() 有什么区别

c - FIFO文件和读/写

c - Linux Mint 中的时间结构

C - UDP 客户端未完成到 Echo 服务器的 SendTo

c - 用实际代码调用函数

C:父子进程

c++ - C 程序获得不同的输出?这个程序应该输出什么?

c++ - Fober 等人无锁 FIFO 队列 : multiple consumers and producers?

amazon-web-services - 如何搜索/查询符合条件的SQS队列元素

c - 结构中的变量声明问题