C, передача сообщений дочерним процессам не работает. Почему?


В настоящее время я работаю над заданием для моего класса операционных систем, где мы должны написать программу на языке Си, которая сначала откроет четыре канала, а затем создаст 3 дочерних.

Затем родитель будет принимать сообщения от пользователя для отправки дочерним процессам и отправлять их по каналам дочернему процессу. Затем дочерние процессы отобразят все полученные сообщения.

Труба 1 будет идти от родителя к потомку 1. Труба 2 будет идти от родителя к ребенку 2 или 3. Трубы 3 и 4-это трубы, идущие между детьми 2 и 3, чтобы убедиться, что сообщение заканчивается правильным ребенком.

Ваша программа должна вести себя следующим образом:

•   AFTER opening the pipes and creating the children, the parent process should prompt the user for the number of messages to pass. ONLY the parent should have access to this quantity. The children should not be aware of it!

•   Once the parent knows how many messages to expect from the user, it should prompt the user for those messages in the form:
“<message string> <child to receive message>”

•   You may assume that messages are only one word in length (you do not need to handle spaces in messages).

•   The parent will then use pipes P1 and P2 to send all the messages to the appropriate children.

•   Because C2 and C3 share pipe P2, they may receive each other’s messages. In this situation, they are responsible for using P3 or P4 to forward the messages as appropriate.

•   Each process should ensure that its pipes are unidirectional.

•   Once received, messages MUST be printed out in the form
“Child <x> read message: <msg>”
Where <x> is the number of the child (1, 2, 3) that is printing the message and
<msg> is the message itself.

*Hint: To avoid blocking reads in the children, you should consider what happens when processes close one end of a pipe.

*Hint: When sending messages to C2 and C3, you may want to append a special character to the message so that they will know if it was meant for them or not.

*Hint: It’s probably a good idea to perform all the writes before performing any reads.

В настоящее время мои дочерние процессы получат одно или два сообщения, но затем остановятся. Я думаю, что функция чтения блокирует, но я не уверен, для чего и почему.

Пример вывода:

[root@cmachine CIS370]# ./ parksPipes сколько сообщений? 3

Сообщение (1): ey 1

Сообщение (2) : ay 1

Сообщение (3): yo 1

Сообщение: ey, написанное ребенку 1.

Сообщение: ay написано ребенку 1.

Сообщение: yo написано ребенку 1.

Выход родителя...

C1 receive: ey

(программа зависает в этом месте.)

Код:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#define MSGSIZE 256

main()
{
    int ppid = getpid(); // Get parent id
    int nmessages = 0;
    int p1[2]; int p2[2]; int p3[2]; int p4[2]; // Arrays used for the pipes
    if(pipe(p1)==-1){printf("p1 err, errno: %d", errno); exit(-1);} if(pipe(p2)==-1){printf("p2 err, errno: %d", errno); exit(-1);}  // Open pipes
    if(pipe(p3)==-1){printf("p3 err, errno: %d", errno); exit(-1);} if(pipe(p4)==-1){printf("p4 err, errno: %d", errno); exit(-1);}

    int kidpid1 = fork();
    if(kidpid1 == 0) // child 1
    {
        int nread;
        char mesg[MSGSIZE];
        close(p1[1]);
        close(p2[0]); close(p2[1]); close(p3[0]); close(p3[1]); close(p4[0]); close(p4[1]);
        while( (nread = read(p1[0], mesg, MSGSIZE))>0) // read will be blocked until write end of pipe closed
        {
              printf("C1 receive: %sn", mesg);
        }
        printf("Child 1 exiting...n");
        exit(0);
    }
    else if(kidpid1 > 0) // parent
    {
        int kidpid2 = fork();
        if(kidpid2 == 0) // child 2
        {
            int nread;
            char mesg[MSGSIZE];
            while( (nread = read(p2[0], mesg, MSGSIZE))>0)
            {
                printf("C2 receive: %sn", mesg);
            }
            printf("Child 2 exiting...n");
            exit(0);
        }
        else if(kidpid2>0) // parent
        {
            int kidpid3 = fork();
            if(kidpid3 == 0) // child 3
            {
                    close(p2[1]);
                    int nread;
                    char mesg[MSGSIZE];
                    while( (nread = read(p2[0], mesg, MSGSIZE))>0)
                    {
                        printf("C3 receive: %sn", mesg);
                    }
                    printf("Child 3 exiting...n");
                    exit(0);
            }
            else if(kidpid3 > 0) // parent
            {
                close(p1[0]); close(p2[0]); close(p3[0]); close(p4[0]); // Close all the read ends of the pipes (parent will not be reading)
                close(p3[1]); close(p4[1]); // Close the write ends of pipes 3 and 4 (parent will not be writing down these pipes)
                printf("How many messages? ");
                scanf("%d", &nmessages);
                char *msg[nmessages];
                 int child[nmessages];
                 int i=0;
                for(i=0; i<nmessages; i++)
                {
                    printf("Enter message (%d) <message string> <child to receive message>: ", i+1);
                    scanf("%s %d", &msg[i], &child[i]);
                    //printf("%s %d", &msg[i], child[i]); // Parrot the message back for debugging purposes
                }
                for(i=0; i<nmessages; i++)
                {
                    switch(child[i])
                    {
                        case 1: // send msg[i] to child 1 (p1)
                        if(write(p1[1],&msg[i],sizeof(msg[i]))== -1)
                        {
                            perror("Pipe 1 write error. errno: %dn");
                            exit(-1);
                        }
                        else
                          printf("Message : %s written to Child %d.n", &msg[i], child[i]);
                        break;
                        case 2: // send msg[i] to child 2 (p2) (p2 goes to C2 and C3, p3 and p4 is between the two children)
                        if(write(p2[1],&msg[i],sizeof(msg[i])) < 0)
                        {
                            printf("Pipe 2 write error. errno: %dn", errno);
                            exit(-1);
                        }
                        break;
                        case 3: // send msg[i] to child 3 (p2)
                        if(write(p2[1],&msg[i],sizeof(msg[i])) < 0)
                        {
                            printf("Pipe 1 write error. errno: %dn", errno);
                            exit(-1);
                        }
                        break;
                        default:
                        printf("Incorrect child ID entered: %d", child[i]);
                        exit(-1);
                        break;
                    }
                }
                close(p1[1]); // Close write end of Pipe 1
                close(p2[1]); // Close write end of Pipe 2
                printf("Parent exiting...n");
            }
            else
            {printf("Err ln35, errno: %d", errno); exit(-1);}
        }
        else // error
        {printf("Err ln18, errno: %d", errno); exit(-1);}
    }
    else // error
    {printf("Fork err, ln 75. errno: %d", errno); exit(-1);}
}
1 2

1 ответ:

У вас есть неопределенное поведение в коде. У вас есть массив указателей на символы msg, и вы используете этот массив при чтении ввода от пользователя.

Первая проблема здесь заключается в том, что указатели в массиве являются неинициализированными. Значение неинициализированных локальных переменных является неопределенным, и использование их любым способом, кроме инициализации, приводит к неопределенному поведению. Либо динамически выделять память для этих указателей, либо использовать массив массивы символов, например
char msg[nmessages][MAX_MESSAGE_LENGTH];
Вторая проблема заключается в том, как вы используете массив msg в вызове scanf. Когда вы делаете &msg[i], Вы получаете указатель на указатель , то есть вы получаете char **, не совсем то, что предполагалось, я полагаю? Эта проблема решится сама собой, если вы измените msg на массив массивов, но также можно решить, просто отбросив оператор address-of и передав msg[i] в scanf.