用命名管道和叉子头痛

时间:2013-02-01 17:42:03

标签: c fork fifo

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

家长制作fifo,然后fork()

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

我用一个fork创建了一个简单的程序,其中child可以与父进行通信:

#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

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

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

2 个答案:

答案 0 :(得分:1)

问题是CHILD1正在使用O_NONBLOCK打开fifo,如果没有其他进程打开fifo进行读取,则会失败(使用EWOULDBLOCKEAGAIN)。现在在第一个程序中,父进程在fork之后继续运行并在子进程之前打开fifo进行读取并打开写入结束,因此它可以工作。但是在第二种情况下,父母首先做一个额外的分叉,这使得它减慢到足以使CHILD1在PARENT或CHILD2打开fifo进行读取之前进入其打开命令,因此CHILD1打开失败。

摆脱O_NONBLOCK并且它工作得很好(尽管你打开了用于阅读PARENT和CHILD2的fifo,这可能不是你想要的)。

如果您想从键盘上读取,还有其他问题。如果你从shell运行它,PARENT将立即退出(或多或少),所以shell将返回从键盘读取命令,这意味着CHILD1和shell将争夺输入。另一方面,如果您执行最初描述的内容并让PARENT从CHILD2的管道中等待读取,它应该按照您的要求执行。

答案 1 :(得分:0)

是不是因为你使用两次相同的变量fork_result?当你创建另一个你不使用的变量fork_result2时,它可能是无意的。

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