fork() 后的消息行为
Messages behavior after fork()
我正在学习通过消息进行进程间通信。
在下面的代码片段中,我应用 fork()
并在父进程和子进程之间发送消息。
我希望控制台输出“1 - 2 - 3 - 4”。但是,我得到了“1 - 2”,在此之后,程序似乎永远停留在打印“3”之前的 msgrcv
行。谁能告诉我代码有什么问题?
#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;
}
这里的消息类型初始化有问题:
msg.mtype = 1;
msgPing.mtype = 1;
这些值仅在 child 中初始化。当 parent 尝试在 msg
中发送消息时, mtype
尚未设置,因此 child 无休止地等待 mtype
的消息 1
,永远不会到达。
要么在 fork()
之前设置这两个值,要么在 parent 和 child 中设置它们,这样就可以了:
$ ./msg
1 - started, sending msgPing
3 - msg received, sending msgPing
2 - msgPing received, sending msg
4 - msgPing received, finished
大约一秒钟。
您只在 child 中设置了 mtype
,但在 parent 中设置了 而不是 。
此外,child 中的 msgctl
次调用 与 parent 中的最终 msgrcv
竞争。也就是说,child 可能会在 之前完成 msgctl
调用 parent 有机会完成其最终的 msgrcv
。
而且,我认为 child 的 return 0;
应该是 exit(0);
以防止落入 parent 代码。
在下面的重构代码中,我使用 cpp
条件来表示 old/broken 与 new/fixed 代码:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
重构代码如下:
#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;
}
当我为实时、关键任务、生产代码完成 msgsnd/msgrcv
后,我在两个方向上都使用了 single msgid
,使用不同 mtype
值(例如 mtype = 1
用于 parent 和 mtype = 2
用于 ping)。
这是一个进一步清理和简化的版本:
#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;
}
我正在学习通过消息进行进程间通信。
在下面的代码片段中,我应用 fork()
并在父进程和子进程之间发送消息。
我希望控制台输出“1 - 2 - 3 - 4”。但是,我得到了“1 - 2”,在此之后,程序似乎永远停留在打印“3”之前的 msgrcv
行。谁能告诉我代码有什么问题?
#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;
}
这里的消息类型初始化有问题:
msg.mtype = 1;
msgPing.mtype = 1;
这些值仅在 child 中初始化。当 parent 尝试在 msg
中发送消息时, mtype
尚未设置,因此 child 无休止地等待 mtype
的消息 1
,永远不会到达。
要么在 fork()
之前设置这两个值,要么在 parent 和 child 中设置它们,这样就可以了:
$ ./msg
1 - started, sending msgPing
3 - msg received, sending msgPing
2 - msgPing received, sending msg
4 - msgPing received, finished
大约一秒钟。
您只在 child 中设置了 mtype
,但在 parent 中设置了 而不是 。
此外,child 中的 msgctl
次调用 与 parent 中的最终 msgrcv
竞争。也就是说,child 可能会在 之前完成 msgctl
调用 parent 有机会完成其最终的 msgrcv
。
而且,我认为 child 的 return 0;
应该是 exit(0);
以防止落入 parent 代码。
在下面的重构代码中,我使用 cpp
条件来表示 old/broken 与 new/fixed 代码:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
重构代码如下:
#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;
}
当我为实时、关键任务、生产代码完成 msgsnd/msgrcv
后,我在两个方向上都使用了 single msgid
,使用不同 mtype
值(例如 mtype = 1
用于 parent 和 mtype = 2
用于 ping)。
这是一个进一步清理和简化的版本:
#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;
}