2

我想了解 Linux 管道是如何工作的!我编写了一个小而简单的程序,它使用管道在父进程和子进程之间传递字符串。但是,程序导致死锁,我不明白它的原因是什么。

这是代码:

#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define SIZE 100  

int
main(int argc, char *argv[])
{
    int pfd[2];
    int read_pipe=0, write_pipe=0; 
    pid_t cpid;
    char buf[SIZE]; 

    /* PIPE ***************************************
     * pipe() creates a pair of file descriptors, *
     * pointing to a pipe inode, and places them  *
     * in the array pointed to by filedes.    *
     * filedes[0] is for reading,         *
     * filedes[1] is for writing          *
     **********************************************/

    if (pipe(pfd) == -1) { 
        perror("pipe"); 
        exit(EXIT_FAILURE);
    }

    read_pipe=pfd[0]; 
    write_pipe=pfd[1]; 

    cpid = fork();
    if (cpid == -1) { 
        perror("fork"); 
        exit(EXIT_FAILURE);
    }


    if (cpid == 0) {    /* Child reads from pipe */

    char * hello = "I am a child process\n"; 
    sleep(1);  
    // wait until there is some data in the pipe
        while (read(read_pipe, buf, SIZE) > 0);
    printf("Parent process has written : %s\n", buf);
    write(write_pipe, hello, strlen(hello)); 
    close(write_pipe);
    close(read_pipe);
        _exit(EXIT_SUCCESS);
    } else {                /* Parent writes argv[1] to pipe */

    char * hello = "I am a parent process\n";
    write(write_pipe, hello, strlen(hello));
    while (read(read_pipe, buf, SIZE) > 0); 
printf("Child process has written : %s\n", buf);

    close(write_pipe);       
    close(read_pipe);

    wait(NULL);             /* Wait for child */
    exit(EXIT_SUCCESS);
    }
}
4

2 回答 2

7

在此链接中,您将找到在父子之间正确操作 PIPE。您的问题是没有正确设置通信。

PIPE 应该只用于一个方向的通信,因此一个进程必须关闭读取描述符,而另一个进程必须关闭写入描述符。否则会发生对'read'的调用(在父和子上),因为它可以检测到在 PIPE 上有另一个具有打开的写描述符的进程,当它发现 PIPE 为空时将阻塞(不返回 0),直到有人在其中写入内容。因此,您的父亲和您的儿子在各自的阅读中都被屏蔽了。

有两种解决方案:

.您创建两个 PIPE,一个用于每个方向的通信,并按照上面链接中的说明执行初始化。在这里,您必须记住在发送完消息后关闭写入描述符,以便其他进程的读取将返回,或者将循环设置为读取的字节数(而不是读取的返回),这样您就不会了当您阅读整条信息时,不要再拨打电话。例如:

int bread = 0;
while(bread < desired_count)
{
   bread += read(read_pipe, buf + bread, SIZE - bread);
}

.您像以前一样创建一个 PIPE,并修改读取描述符上的标志,使用fcntl使其也具有 O_NONBLOCK,因此当 PIPE 中没有信息时,读取​​的调用不会阻塞。在这里,您需要检查 read 的返回值以知道您收到了一些东西,然后继续累加直到获得完整的消息长度。此外,您将找到一种同步两个进程的方法,这样它们就不会读取不适合它们的消息。我不建议您使用此选项,但如果您想使用条件变量,您可以尝试一下。

于 2013-06-10T11:51:26.783 回答
0

也许您可以判断您是否看到任何您的 printf() 输出?无论如何,如果你想在你的孩子和孩子之间建立双向通信,你应该使用两个管道,一个用于将数据从父级写入子级,另一个用于从子级写入父级。此外,您的读取循环可能很危险:如果数据来自两个或更多块,则第二个 read() 会覆盖第一部分(我从未见过本地管道发生这种情况,但例如使用套接字)。当然,在 read() 之后,you 不会自动终止 null,因此仅使用 "%s" 打印 int 也可能会导致问题。我希望这能给你一些尝试的想法。

于 2013-06-10T11:56:49.713 回答