C(Linux)中的管道问题

Posted

技术标签:

【中文标题】C(Linux)中的管道问题【英文标题】:Pipe trouble in C (Linux) 【发布时间】:2015-04-02 15:06:17 【问题描述】:

我目前无法理解子级与其父级之间的非双工双向未命名管道通信。我正在尝试让最多十个孩子与超级父母进行交流。

目前我正在尝试让超级父母向每个孩子问好,然后孩子们向父母问好。最后,超级家长承认他收到了每个孩子的问候。

我在添加管道之前测试了叉子,它们工作正常,产生了正确数量的子代。所以我几乎可以肯定,问题出在我代码底部的两个 pipeCommunication() 方法中的一个(或两个)中。

当我运行程序时,它静止不动(好像在等待输入)。我预计它会卡在一个 while 循环中读取,但 我什至无法打印 main() 的第一行。

代码如下:

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <stdbool.h>


int NUM_CHILDREN;
int ID;


// Create the pipes
void createPipes(int[], int[]);

// Wait for the children processes
void waitForChildren(int);

// The Child pipe communication method
void childPipeCommunication(int, int, int);

// The Parent pipe communication method
void parentPipeCommunication(int, int, int);



// MAIN FUNCTION
int main(int argc, char *argv[])

    // NOT BEING PRINTED:
    printf("Hello");

    NUM_CHILDREN = argc - 1;

    // The file descriptors for the pipes
    int fd_childReads_ParentWrites[10][2];   // Parent should close 0, Child close 1
    int fd_parentReads_ChildWrites[10][2];   // Child should close 0, Parent close 1

    // Index of the child Array (0 to NUM_CHILDREN-1)
    int user;

    int pid;
    int pidArray[10]; // Stores each of the child's process id's

    // Fork the children and create the pipes
    for(user = 0; user < NUM_CHILDREN; user++)
    
        // Create the pipes
        createPipes(fd_childReads_ParentWrites[user], fd_parentReads_ChildWrites[user]);

        // Fork the children
        pid = fork();

        if (pid < 0)   // Error occurred
        
            printf("Fork Failed\n");
            exit(1);
        
        else if (pid == 0)   // Child
        
            break;
        
        else if (pid) // Parent
        
            pidArray[user] = pid;
        
    


    if (pid == 0) // CHILD
    
        // Close the appropriate pipe ends
        close(fd_childReads_ParentWrites[user][1]);
        close(fd_parentReads_ChildWrites[user][0]);

        ID = getpid();
        int n = 0;

        // Enter pipe communication (user is the same as when it broke from the for loop)
        childPipeCommunication(ID, fd_childReads_ParentWrites[user][0],
                                fd_parentReads_ChildWrites[user][1]);

        // Finally, close the working child pipe ends
        close(fd_childReads_ParentWrites[user][0]);
        close(fd_parentReads_ChildWrites[user][1]);
    
    else // PARENT
    
        ID = getpid();
        user = 0;

        // Close the appropriate pipe ends
        for (user = 0; user < NUM_CHILDREN; user++)
        
            close(fd_childReads_ParentWrites[user][0]);
            close(fd_parentReads_ChildWrites[user][1]);
        

        // Go into Pipe Communication
        for(user = 0; user < NUM_CHILDREN; user++)
        
            parentPipeCommunication(pidArray[user], fd_parentReads_ChildWrites[user][0], fd_childReads_ParentWrites[user][1]);
        

        // Wait for the children
        waitForChildren();

        // Finally, close the working parent pipe ends
        for (user = 0;  user < NUM_CHILDREN; user++)
        
            close(fd_childReads_ParentWrites[user][1]);
            close(fd_parentReads_ChildWrites[user][0]);
        
    




void createPipes(int fd1[2], int fd2[2])

    if (pipe(fd1) < 0)
    
        printf("Pipe creation error!\n");
        exit(1);
    

    if (pipe(fd2) < 0)
    
        printf("Pipe creation error!\n");
        exit(1);
    



void waitForChildren()

    int user;
    for (user = 0; user < NUM_CHILDREN; user++)
    
        wait(NULL);
    

以下是管道通信的方法。

void childPipeCommunication(int childID, int fdReadFromParent, int fdWriteToParent)

    char buf_ChildReads[80];  
    int n = 0;

    while ((n = read(fdReadFromParent, buf_ChildReads, 80)) > 0)
        
            buf_ChildReads[n] = 0;

            // CASE: CHILD RECEIVES HELLO FROM PARENT
            if(strcmp(buf_ChildReads, "Hi child.\n") == 0)
            
                // Remove new line character
                buf_ChildReads[--n] = 0;

                // Acknowledge parent's hello and then send reply
                printf("Child %d: Reveived message [%s] from parent\n", childID, buf_ChildReads);
                write(fdWriteToParent, "Hello Parent\n", 13);
            
        



void parentPipeCommunication(int childID, int fdReadFromChild, int fdWriteToChild)

    char buf_ParentReads[80];  
    int n = 0;

    // Say hello to the child
    write(fdWriteToChild, "Hi child\n", 9);

    // Engage in communication with the child
    while ((n = read(fdReadFromChild, buf_ParentReads, 80)) > 0)
        
            buf_ParentReads[n] = 0;

            // CASE: PARENT RECEIVES HELLO FROM CHILD
            if(strcmp(buf_ParentReads, "Hello Parent\n") == 0)
            
                printf("Parent: I have received response from child %d\n", childID);
            
        

如果有人能看一下我的代码并告诉我如何正确实现父子之间的通信,我将不胜感激。

【问题讨论】:

值 '10' 遍布整个代码,并且(隐式)期望参数的数量为 10。但是,代码中没有检查来确保该假设。完全忽略命令行参数会好得多(它们不用于计数以外的任何东西。)--或者-- 修改所有这些数组以使用计数。 @user,感谢您的帮助。但是,这只是代码的一部分。我还有很多关于这些值的内容,但我只想主要包括管道通信。 【参考方案1】:

1) 问题:printf 未打印:

stdout 通常在 Unix 下是行缓冲的。尝试打印到stderr(无缓冲),调用fflush(stdout),或显式打印"\n"

2) 问题:写 & strcmp

请注意,write(fd, "Hi child\n", 9) 没有尾随 '\0',但 strcmp 比较以零结尾的字符串。因此,您的子进程永远不会响应。

通过管道的双向 IPC 存在许多缺陷。见https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer

【讨论】:

【参考方案2】:

您将 NUM_CHILDREN 设置为命令行参数的数量。如果您未能提供任何参数,则它为零,不进入循环,未初始化 pid 并且未定义将采用底部的两条路径中的哪一条。两者都会挂起。

在没有从标准输入读取数据并且发生挂起的情况下使用 printf 时,您可能需要使用 fflush 来获取输出。

接下来,您的父母希望收到没有句号的消息,而您的孩子正在发送句号。

最后,父母和孩子都在循环,直到另一个关闭,所以来自第一个孩子的通信不会结束,以后的孩子也不会被处理。

【讨论】:

感谢有关 cmd 行参数的信息。我的代码中确实有验证,只是没有添加到这里,总会有 3 到 10 个孩子。反正那个时期肯定是有问题的,加了之后你是对的,其他小朋友就不交流了。您能否提供一个解决方案,说明如何与所有孩子保持持续沟通?或者类似的东西? 当孩子没有话要说的时候,这行不是退出循环吗? while ((n = read(fdReadFromChild, buf_ParentReads, 80)) &gt; 0) 同样,当孩子从父母那里读取()时? 我想我知道如何绕过它。谢谢。 该行仅在另一端关闭管道时才退出。你需要一个条件/命令来触发它。

以上是关于C(Linux)中的管道问题的主要内容,如果未能解决你的问题,请参考以下文章

C Linux 编程 - 管道使子进程退出

如何在Linux上的c中的父进程和子进程之间进行乒乓球

C 中的管道 - 我必须使用 fork 吗?

在linux中使用管道使用父子进程

linux中的管道和重定向

在linux下可以用命名管道实现c程序与qt的数据通信吗?