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;
}
