简体   繁体   English

父进程无法从C中的4个不同管道读取所有消息

[英]Parent process can't read all messages from 4 different pipes in C

Finally after reviewing and asking questions here, I was able to write this code which generates 4 child processes and communicate via 4 different pipes. 最后,在这里查看并提出问题之后,我能够编写此代码,该代码生成4个子进程并通过4个不同的管道进行通信。 Each of the 4 child processes writes into a single pipe ( fd_log ), but when the parent process reads from the pipe it reads only the first message from process A , the first message from process C , all messages from process B and none from process D . 4个子进程中的每一个都写入单个管道( fd_log ),但是当父进程从管道中读取时,它仅读取来自进程A的第一条消息,来自进程C的第一条消息,来自进程B所有消息,而没有任何进程的消息D This is the code: 这是代码:

int main(int argc, char *argv[]) {

    printf("\nWritten by Nawar Youssef\n");
    int i, x=0, fd_log[2], fd_A_B[2], fd_B_C[2], fd_B_D[2], pipe_size=500;
    char ch, msg_from_A[pipe_size], msg_to_B[pipe_size], msg_to_log[pipe_size],
        msg_to_C[pipe_size], msg_to_D[pipe_size], msg_B_C[pipe_size], msg_B_D[pipe_size],
        msg_from_log[pipe_size], temp_char[pipe_size], msg_C_log[pipe_size], msg_D_log[pipe_size];

    pipe(fd_log);
    pipe(fd_A_B);
    pipe(fd_B_C);
    pipe(fd_B_D);

    if (fork()==0) { //child A
        for (i=0; i < 10; i++) {
            x = (rand() % 2);
            if (x == 1)
                ch='C';
            else
                ch='D';

            //write records to A-B pipe
            close(fd_A_B[READ]);
            sprintf(msg_to_B, "%c %d", ch, i);
            write(fd_A_B[WRITE], msg_to_B, strlen(msg_to_B)+1);

            //write records to log pipe
            close(fd_log[READ]);
            sprintf(msg_to_log, "A sent to B: %c %d", ch, i);
            write(fd_log[WRITE], msg_to_log, strlen(msg_to_log)+1);
        }//for
        close(fd_A_B[WRITE]);
        close(fd_log[WRITE]);
        _exit(1);
    }//if

    if (fork()==0) { //child B
        //read A-B pipe
        close(fd_A_B[WRITE]);
        int n_bytes = read(fd_A_B[READ], msg_from_A, sizeof(msg_from_A));
        for(i=0; i < pipe_size; i++) {

            if ( msg_from_A[i] == 'C') {

                //write the message from B to C pipe
                sprintf(msg_to_C, "%c %c", msg_from_A[i], msg_from_A[i+2]);
                //printf("--%s\n", msg_to_C);
                close(fd_B_C[READ]);
                write(fd_B_C[WRITE], msg_to_C, strlen(msg_to_C)+1);

                //write C message to log pipe
                close(fd_log[READ]);
                sprintf(msg_to_log, "B sent to C: %s", msg_to_C);
                write(fd_log[WRITE], msg_to_log, strlen(msg_to_log)+1);
                sleep(1);

            }else if (msg_from_A[i] == 'D') {

                //write the message from B to D pipes
                sprintf(msg_to_D, "%c %c", msg_from_A[i], msg_from_A[i+2]);
                //printf("--%s\n", msg_to_D);
                close(fd_B_D[READ]);
                write(fd_B_D[WRITE], msg_to_D, strlen(msg_to_D)+1);

                //write D message to log pipe
                close(fd_log[READ]);
                sprintf(msg_to_log, "B sent to D: %s", msg_to_D);
                write(fd_log[WRITE], msg_to_log, strlen(msg_to_log)+1);
                sleep(5);

            }else
                continue;
        }//for
        close(fd_B_C[WRITE]); close(fd_B_D[WRITE]); close(fd_log[WRITE]);
        _exit(1); //process B
    }//if

    if (fork()==0) { //child C
        //read from B-C pipe
        close(fd_B_C[WRITE]);
        int n_bytes = read(fd_B_C[READ], msg_B_C, sizeof(msg_B_C));
        for (i=0; i < pipe_size; i++) {

            //write to log pipe
            if (msg_B_C[i] == 'C') {
                sprintf(msg_C_log, "%c %c", msg_B_C[i], msg_B_C[i+2]);
                close(fd_log[READ]);
                sprintf(msg_to_log, "C sent to log: %s", msg_C_log);
                write(fd_log[WRITE], msg_to_log, strlen(msg_to_log)+1);
            }else
                continue;
        }
        _exit(1); //process C
    }

    if (fork()==0) { //child D
        //read from fd_B_D
        close(fd_B_D[WRITE]);
        int n_bytes = read(fd_B_D[READ], msg_B_D, sizeof(msg_B_D));
        for (i=0; i < pipe_size; i++) {

            //write to log pipe
            if (msg_B_D[i] == 'D') {
                sprintf(msg_D_log, "%c %c", msg_B_D[i], msg_B_C[i+2]);
                close(fd_log[READ]);
                sprintf(msg_to_log, "D sent to log: %s", msg_D_log);
                write(fd_log[WRITE], msg_to_log, strlen(msg_to_log)+1);
            }
        }
        _exit(1);
    }//if

    //parent
    close(fd_log[WRITE]);
    int n_bytes;
    while( (n_bytes = read(fd_log[READ], msg_from_log, sizeof(msg_from_log)-1)) > 0){
        printf("Log pipe reads (%d) bytes: %s\n", n_bytes, msg_from_log);
        sleep(1);
    }
    close(fd_log[READ]);
    return (0);
}

When I look at the output, I know that all messages were read (see in the output the number of bytes read returned). 查看输出时,我知道所有消息都已读取(请参阅输出中返回的read字节数)。 So it looks like the problem is in displaying the messages. 所以看起来问题出在显示消息中。 I have faced a similar problem with much simpler code and I solved it by making pipe_size larger. 我已经用更简单的代码遇到了类似的问题,我通过pipe_size来解决了这个问题。 But here it didn't work. 但是这里没有用。
This is the output number between (#) is the bytes read each time the while loops: 这是(#)是每次while循环读取的字节之间的输出数字:

Written by Nawar Youssef 由Nawar Youssef撰写

Log pipe reads (187) bytes: A sent to B: C 0 日志管道读取(187)字节:A发送到B:C 0

Log pipe reads (36) bytes: C sent to log: C 0 日志管道读取(36)字节:C发送到日志:C 0

Log pipe reads (17) bytes: B sent to C: C 2 日志管道读取(17)字节:B发送到C:C 2

Log pipe reads (35) bytes: B sent to D: D 3 日志管道读取(35)字节:B发送到D:D 3

Log pipe reads (17) bytes: B sent to D: D 4 日志管道读取(17)字节:B发送到D:D 4

Log pipe reads (17) bytes: B sent to D: D 5 日志管道读取(17)字节:B发送到D:D 5

Log pipe reads (17) bytes: B sent to D: D 6 日志管道读取(17)字节:B发送到D:D 6

Log pipe reads (17) bytes: B sent to D: D 7 日志管道读取(17)字节:B发送到D:D 7

Log pipe reads (17) bytes: B sent to C: C 8 日志管道读取(17)字节:B发送到C:C 8

Log pipe reads (17) bytes: B sent to C: C 9 日志管道读取(17)字节:B发送到C:C 9

The code is a little confusing because you just decided to put everything in main() . 代码有点混乱,因为您只是决定将所有内容都放入main() A few auxiliary functions would be useful, but I'm not here to judge that. 一些辅助功能将很有用,但我不是在这里判断。

A couple of more important mistakes: 一些更重要的错误:

  • No error handling. 没有错误处理。 Not one. 不是一个。 read(2) , write(2) , close(2) , pipe(2) and fork(2) can all return an error. read(2)write(2)close(2)pipe(2)fork(2)都可以返回错误。 You never check for that, and you should. 您永远不需要检查,应该这样做。 If you did, you would see that close(2) is returning EBADF in most cases, because you call it inside a loop with the same file descriptor over and over. 如果这样做了,您会发现在大多数情况下close(2)返回EBADF ,因为您在一遍循环中使用同一文件描述符反复调用它。 For example, look at the code for child A. It closes two pipes inside the loop. 例如,看一下子A的代码。它关闭了循环中的两个管道。 As soon as the loop hits the second iteration, close(2) starts returning an error because you already closed that descriptor. 一旦循环到达第二次迭代, close(2)就开始返回错误,因为您已经关闭了该描述符。
  • You also never close the pipes write channels in the parent. 您也永远不会在父级中关闭管道写入通道。 This will leave the other child processes alive and blocked in read(2) because there is still one active writer in the pipes (the parent) that could (but won't) write. 这将使其他子进程处于活动状态,并在read(2)被阻塞,因为在管道(父级)中仍然有一个活动的写程序可以(但不会)进行写操作。 You must close the pipes write ends in the parent. 您必须关闭管道,并在父管道中写入两端。 It is always a good idea to create the pipes in the parent immediately before forking the children that will use them, and close the unused channels in the parent immediately after forking. 在派生将要使用的子代之前,立即在父级中创建管道,并在分支后立即关闭父级中未使用的通道,这始终是一个好主意。 The same goes for the child processes. 子进程也是如此。
  • Each child should read the pipe inside a loop similar to what the parent does with the log. 每个孩子都应该在循环中读取管道,这与父母对日志的处理类似。 Otherwise, you will just read once and discard the other messages that are written in the meantime. 否则,您将只阅读一次并丢弃同时写入的其他消息。 You must keep reading until the pipe's write channel is closed by the other end (which happens when read(2) returns 0). 您必须继续读取,直到管道的写通道被另一端关闭为止(这在read(2)返回0时发生)。

  • Your loop conditions in the children to parse the message read from the pipe should be i+2 < nbytes , not i < pipe_size - the latter will go out of bounds when the message is smaller than pipe_size bytes (which is very likely). 在子级中,用于解析从管道读取的消息的循环条件应为i+2 < nbytes ,而不是i < pipe_size当消息小于pipe_size字节时,后者将超出范围(这很有可能)。 The update condition should also be i+= 3 since you consume 3 bytes per iteration. 更新条件也应为i+= 3因为每次迭代消耗3个字节。

  • else continue; in the end of a loop is utterly useless. 在循环的末尾完全没有用。

  • Please compile your code with warnings enabled. 请在启用警告的情况下编译您的代码。 You would see that you have a variable temp_char that is never used. 您会看到您有一个从未使用过的变量temp_char

  • Child D should read the number from msg_B_D instead of msg_B_C . 子D应该从msg_B_D而不是msg_B_C读取数字。 I believe this was a copy / paste error. 我相信这是复制/粘贴错误。

  • You can't send the null terminator over the pipe. 您不能通过管道发送空终止符。 You are doing this, and because of that, you don't see every message that you sent to the log when you print it to output - remember that printf() only prints a string until it sees a terminating null character. 您正在执行此操作,因此,在打印输出到日志时不会看到发送到日志的每条消息-请记住, printf()仅打印字符串,直到看到终止的空字符为止。 Instead, I suggest that you send the messages to the log with a newline ready for the parent to print it without further worries. 相反,我建议您使用换行符将消息发送到日志,以备父级打印,而不必担心。

Here's the code with all of these mistakes addressed. 这是解决所有这些错误的代码。 I commented out the sleeps to get the output quickly: 我注释掉睡眠以快速获得输出:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>

#define READ 0
#define WRITE 1

int main(int argc, char *argv[]) {
    printf("\nWritten by Nawar Youssef\n");
    int i, x=0, fd_log[2], fd_A_B[2], fd_B_C[2], fd_B_D[2], pipe_size=500;
    char ch, msg_from_A[pipe_size], msg_to_B[pipe_size], msg_to_log[pipe_size],
        msg_to_C[pipe_size], msg_to_D[pipe_size], msg_B_C[pipe_size], msg_B_D[pipe_size],
        msg_from_log[pipe_size], msg_C_log[pipe_size], msg_D_log[pipe_size];

    if (pipe(fd_log) < 0) {
        fprintf(stderr, "%d: pipe(): %s\n", __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (pipe(fd_A_B) < 0) {
        fprintf(stderr, "%d: pipe(): %s\n", __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    pid_t pid;

    if ((pid = fork()) < 0) {
        fprintf(stderr, "%d: fork(): %s\n", __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        //child A
        for (i = 0; i < 10; i++) {
            x = (rand() % 2);
            if (x == 1)
                ch = 'C';
            else
                ch = 'D';

            sprintf(msg_to_B, "%c %d", ch, i);
            size_t to_write = strlen(msg_to_B);
            if (write(fd_A_B[WRITE], msg_to_B, to_write) < 0) {
                fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
                exit(EXIT_FAILURE);
            }

            sprintf(msg_to_log, "A sent to B: %c %d\n", ch, i);
            to_write = strlen(msg_to_log);
            if (write(fd_log[WRITE], msg_to_log, to_write) < 0) {
                fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
                exit(EXIT_FAILURE);
            }
        }

        exit(EXIT_SUCCESS);
    }

    if (close(fd_A_B[WRITE]) < 0) {
        fprintf(stderr, "%d: close(): %s\n", __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (pipe(fd_B_C) < 0) {
        fprintf(stderr, "%d: pipe(): %s\n", __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (pipe(fd_B_D) < 0) {
        fprintf(stderr, "%d: pipe(): %s\n", __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if ((pid = fork()) < 0) {
        fprintf(stderr, "%d: fork(): %s\n", __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        // Child B

        int n_bytes;
        while ((n_bytes = read(fd_A_B[READ], msg_from_A, sizeof(msg_from_A)-1)) > 0) {
            size_t to_write;

            for (i = 0; i+2 < n_bytes; i += 3) {
                if (msg_from_A[i] == 'C') {
                    sprintf(msg_to_C, "%c %c", msg_from_A[i], msg_from_A[i+2]);
                    to_write = strlen(msg_to_C);
                    if (write(fd_B_C[WRITE], msg_to_C, to_write) != to_write) {
                        fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
                        exit(EXIT_FAILURE);
                    }

                    sprintf(msg_to_log, "B sent to C: %s\n", msg_to_C);
                    to_write = strlen(msg_to_log);
                    if (write(fd_log[WRITE], msg_to_log, to_write) < 0) {
                        fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
                        exit(EXIT_FAILURE);
                    }

                    //sleep(1);

                } else if (msg_from_A[i] == 'D') {
                    sprintf(msg_to_D, "%c %c", msg_from_A[i], msg_from_A[i+2]);
                    to_write = strlen(msg_to_D);
                    if (write(fd_B_D[WRITE], msg_to_D, to_write) != to_write) {
                        fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
                        exit(EXIT_FAILURE);
                    }

                    sprintf(msg_to_log, "B sent to D: %s\n", msg_to_D);
                    to_write = strlen(msg_to_log);
                    if (write(fd_log[WRITE], msg_to_log, to_write) < 0) {
                        fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
                        exit(EXIT_FAILURE);
                    }
                    //sleep(5);
                }
            }
        }

        if (n_bytes < 0) {
            fprintf(stderr, "%d: read(): %s\n", __LINE__, strerror(errno));
            exit(EXIT_FAILURE);
        }

        exit(EXIT_SUCCESS);
    }

    if ((pid = fork()) < 0) {
        fprintf(stderr, "%d: fork(): %s\n", __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (close(fd_B_C[WRITE]) < 0) {
        fprintf(stderr, "%d: close(): %s\n", __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (close(fd_B_D[WRITE]) < 0) {
        fprintf(stderr, "%d: close(): %s\n", __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        // Child C

        int n_bytes;
        while ((n_bytes = read(fd_B_C[READ], msg_B_C, sizeof(msg_B_C))) > 0) {
            for (i = 0; i+2 < n_bytes; i += 3) {
                if (msg_B_C[i] == 'C') {
                    sprintf(msg_C_log, "%c %c", msg_B_C[i], msg_B_C[i+2]);
                    sprintf(msg_to_log, "C sent to log: %s\n", msg_C_log);
                    size_t to_write = strlen(msg_to_log);
                    if (write(fd_log[WRITE], msg_to_log, to_write) != to_write) {
                        fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
                        exit(EXIT_FAILURE);
                    }
                }
            }
        }

        if (n_bytes < 0) {
            fprintf(stderr, "%d: read(): %s\n", __LINE__, strerror(errno));
            exit(EXIT_FAILURE);
        }

        exit(EXIT_SUCCESS);
    }

    if ((pid = fork()) < 0) {
        fprintf(stderr, "%d: fork(): %s\n", __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        // Child D

        int n_bytes;
        while ((n_bytes = read(fd_B_D[READ], msg_B_D, sizeof(msg_B_D))) > 0) {
            for (i = 0; i+2 < n_bytes; i += 3) {
                if (msg_B_D[i] == 'D') {
                    sprintf(msg_D_log, "%c %c", msg_B_D[i], msg_B_D[i+2]);
                    sprintf(msg_to_log, "D sent to log: %s\n", msg_D_log);
                    size_t to_write = strlen(msg_to_log);
                    if (write(fd_log[WRITE], msg_to_log, to_write) != to_write) {
                        fprintf(stderr, "%d: write(): %s\n", __LINE__, strerror(errno));
                        exit(EXIT_FAILURE);
                    }
                }
            }
        }

        if (n_bytes < 0) {
            fprintf(stderr, "%d: read(): %s\n", __LINE__, strerror(errno));
            exit(EXIT_FAILURE);
        }

        exit(EXIT_SUCCESS);
    }

    // Parent

    if (close(fd_log[WRITE]) < 0) {
        fprintf(stderr, "%d: close(): %s\n", __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    int n_bytes;
    while ((n_bytes = read(fd_log[READ], msg_from_log, sizeof(msg_from_log)-1)) > 0) {
        msg_from_log[n_bytes] = '\0';
        printf("Log pipe reads (%d) bytes:\n%s", n_bytes, msg_from_log);
        //sleep(1);
    }

    return 0;
}

There is still a lot of room for improvement. 仍有很大的改进空间。 You don't need so many character array variables - remember that the memory is not shared between parent and child processes. 您不需要太多的字符数组变量-请记住,父进程和子进程之间不共享内存。 You could just have 1 or 2 buffers to print the intermediate messages before writing them. 在编写中间消息之前,您可能只有1或2个缓冲区来打印中间消息。 But it seems to be working: 但它似乎正在工作:

filipe@filipe-Kubuntu:~/dev$ ./a.out 

Written by Nawar Youssef
Log pipe reads (170) bytes:
A sent to B: C 0
A sent to B: D 1
A sent to B: C 2
A sent to B: C 3
A sent to B: C 4
A sent to B: C 5
A sent to B: D 6
A sent to B: D 7
A sent to B: C 8
A sent to B: C 9
Log pipe reads (170) bytes:
B sent to C: C 0
B sent to D: D 1
B sent to C: C 2
B sent to C: C 3
B sent to C: C 4
B sent to C: C 5
B sent to D: D 6
B sent to D: D 7
B sent to C: C 8
B sent to C: C 9
Log pipe reads (57) bytes:
D sent to log: D 1
D sent to log: D 6
D sent to log: D 7
Log pipe reads (133) bytes:
C sent to log: C 0
C sent to log: C 2
C sent to log: C 3
C sent to log: C 4
C sent to log: C 5
C sent to log: C 8
C sent to log: C 9
filipe@filipe-Kubuntu:~/dev$

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM