Home > OS >  Messages behavior after fork()
Messages behavior after fork()

Time:11-20

I'm learning interprocess communication through messages.

In the following code snippet, I apply fork() and send messages between the parent and the child processes.

I expect "1 - 2 - 3 - 4" console output. However, I got "1 - 2", and after this, the program seems to be stuck forever on the msgrcv line before printing "3". Could anyone tell what's wrong with the code?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define BUF_SIZE 16
#define MSG_KEY1 75
#define MSG_KEY2 76

struct msgform
{
    long     mtype;
    char     mbuf[BUF_SIZE];
    long     mind;
} msg;

struct msgping
{
    long     mtype;
    long     ping ;
} msgPing;

int main() {

    if(fork() == 0) {
    // child process
    int msgid1;
    int msgid2;
    msgid1 = msgget(MSG_KEY1, 0666 | IPC_CREAT);
    msgid2 = msgget(MSG_KEY2, 0666 | IPC_CREAT);
    msg.mtype = 1;
    msgPing.mtype = 1;

    printf("1 - started, sending msgPing\n");

    msgsnd(msgid1, &msgPing, sizeof(msgPing), 0);

    msgrcv(msgid2, &msg, sizeof(msg), 1, 0);

    printf("3 - msg received, sending msgPing\n");

    msgsnd(msgid1, &msgPing, sizeof(msgPing), 0);

    msgctl(msgid1, IPC_RMID, 0);
    msgctl(msgid2, IPC_RMID, 0);

    return 0;
    }

    //parent process
    sleep(1);
    int msgid1;
    int msgid2;
    msgid1 = msgget(MSG_KEY1, 0666 | IPC_CREAT);
    msgid2 = msgget(MSG_KEY2, 0666 | IPC_CREAT);

    msgrcv(msgid1, &msgPing, sizeof(msgPing), 1, 0);

    printf("2 - msgPing received, sending msg\n");

    msgsnd(msgid2, &msg, sizeof(msg), 0);

    msgrcv(msgid1, &msgPing, sizeof(msgPing), 1, 0);

    printf("4 - msgPing received, finished\n");

    return 0;
}

CodePudding user response:

The problem is with the message type initialization here:

msg.mtype = 1;
msgPing.mtype = 1;

These values are only getting initialized in the child. When the parent tries to send a message in msg, the mtype hasn't been set, so the child waits endlessly for a message of mtype 1, which never arrives.

Either setting those two values before fork(), or setting them in the parent as well as the child, makes this work:

$ ./msg
1 - started, sending msgPing
3 - msg received, sending msgPing
2 - msgPing received, sending msg
4 - msgPing received, finished

in about a second.

CodePudding user response:

You only set mtype in the child but not in the parent.

Also, the msgctl calls in the child are racing against the final msgrcv in the parent. That is, the child may complete the msgctl call before the parent has a chance to complete its final msgrcv.

And, I think, the child's return 0; should be exit(0); to prevent fall through into the parent code.


In the refactored code below, I've used cpp conditionals to denote old/broken vs new/fixed code:

#if 0
// old code
#else
// new code
#endif

#if 1
// new code
#endif

Here is the refactored code:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#if 1
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#endif

#define BUF_SIZE 16
#define MSG_KEY1 75
#define MSG_KEY2 76

struct msgform {
    long mtype;
    char mbuf[BUF_SIZE];
    long mind;
} msg;

struct msgping {
    long mtype;
    long ping;
} msgPing;

#define ONERR(_fd) \
    do { \
        if (_fd >= 0) \
            break; \
        printf("msgget error " #_fd " at line %d cldpid=%d -- %s\n", \
            __LINE__,cldpid,strerror(errno)); \
        exit(1); \
    } while (0)

int
main()
{

#if 0
    if (fork() == 0) {
#else
    pid_t cldpid = fork();
    if (cldpid == 0) {
#endif
        // child process
        int msgid1;
        int msgid2;

        msgid1 = msgget(MSG_KEY1, 0666 | IPC_CREAT);
        ONERR(msgid1);
        msgid2 = msgget(MSG_KEY2, 0666 | IPC_CREAT);
        ONERR(msgid2);
        msg.mtype = 1;
        msgPing.mtype = 1;

        printf("1 - started, sending msgPing\n");

        msgsnd(msgid1, &msgPing, sizeof(msgPing), 0);

        msgrcv(msgid2, &msg, sizeof(msg), 1, 0);
        printf("3 - msg received, sending msgPing\n");

        msgsnd(msgid1, &msgPing, sizeof(msgPing), 0);

#if 0
        msgctl(msgid1, IPC_RMID, 0);
        msgctl(msgid2, IPC_RMID, 0);
#endif

#if 0
        return 0;
#else
        exit(0);
#endif
    }

    // parent process
    sleep(1);
    int msgid1;
    int msgid2;

    msgid1 = msgget(MSG_KEY1, 0666 | IPC_CREAT);
    ONERR(msgid1);
    msgid2 = msgget(MSG_KEY2, 0666 | IPC_CREAT);
    ONERR(msgid2);

// NOTE/FIX: the parent needs to set this as well as the child
#if 1
    msg.mtype = 1;
    msgPing.mtype = 1;
#endif

    msgrcv(msgid1, &msgPing, sizeof(msgPing), 1, 0);

    printf("2 - msgPing received, sending msg\n");

    msgsnd(msgid2, &msg, sizeof(msg), 0);

    msgrcv(msgid1, &msgPing, sizeof(msgPing), 1, 0);

    printf("4 - msgPing received, finished\n");

#if 1
    waitpid(cldpid,NULL,0);

    msgctl(msgid1, IPC_RMID, 0);
    msgctl(msgid2, IPC_RMID, 0);
#endif

    return 0;
}

When I've done msgsnd/msgrcv for realtime, mission critical, production code, I've used a single msgid for both directions, using a different mtype value (e.g. mtype = 1 for parent and mtype = 2 for the ping).

Here is a further cleaned up and simplified version:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#if 1
#include <sys/wait.h>
#endif

#define BUF_SIZE 16
#define MSG_KEY1 75
#define MSG_KEY2 76

struct msgform {
    long mtype;
    char mbuf[BUF_SIZE];
    long mind;
} msg;

struct msgping {
    long mtype;
    long ping;
} msgPing;

enum {
    MSGTYPE = 1,
    MSGPING = 2,
};

int
main(void)
{

    int msgid = msgget(MSG_KEY1, 0666 | IPC_CREAT);

    pid_t cldpid = fork();

    msg.mtype = MSGTYPE;
    msgPing.mtype = MSGPING;

    if (cldpid == 0) {
        sleep(1);

        printf("1 - started, sending msgPing\n");
        msgsnd(msgid, &msgPing, sizeof(msgPing), 0);

        msgrcv(msgid, &msg, sizeof(msg), MSGTYPE, 0);

        printf("3 - msg received, sending msgPing\n");
        msgsnd(msgid, &msgPing, sizeof(msgPing), 0);

        exit(0);
    }

    // parent process
    sleep(1);

    msgrcv(msgid, &msgPing, sizeof(msgPing), MSGPING, 0);
    printf("2 - msgPing received, sending msg\n");

    msgsnd(msgid, &msg, sizeof(msg), 0);

    msgrcv(msgid, &msgPing, sizeof(msgPing), MSGPING, 0);
    printf("4 - msgPing received, finished\n");

    waitpid(cldpid,NULL,0);
    msgctl(msgid, IPC_RMID, 0);

    return 0;
}
  • Related