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