父子进程消息队列通信阻塞问题排查及解决方案
进程间消息队列通信停滞问题排查与解决
父进程需向n个子进程发送m条消息,每次迭代向所有子进程发送一条消息,需等所有子进程回复“Received”后再发送下一条。但当n=2、m=2时,程序运行陷入停滞,输出到某一步后无后续内容。
原实现代码
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/wait.h> #include <stdio.h> #include <string.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> #define MAX_LEN 50 struct details { int no; int id; char msg_text[MAX_LEN]; }; typedef struct msg_buf { long int msg_type; struct details d; } msg; char message[20][50] = { " ", "Message1", "Message2", "Message3", "Message4", "Message5", "Message6", "Message7", "Message8" }; int main(int argc, char *argv[]) { int id1, id2, n, m, i, j, status, p_id; pid_t pid; msg snd, rcvd; if (argc < 2) { perror("Too few arguments"); return -1; } n = atoi(argv[1]); m = atoi(argv[2]); id1 = msgget((key_t)78, IPC_CREAT | 0666); id2 = msgget((key_t)56, IPC_CREAT | 0666); if (id1 == -1 || id2 == -1) { printf("Error in creating message queue\n"); return -1; } p_id = getpid(); printf("\nQueues are created.\n\n"); for (i = 1; i <= n; i++) { pid = fork(); if (pid == 0) break; } if (pid == 0) { for (j = 1; j <= m; j++) { printf("Process %d waiting for message %d\n", getpid(), j); while (msgrcv(id1, &rcvd, sizeof(struct details), j + 1, IPC_NOWAIT) < 0) { } srand(time(0)); strcpy(snd.d.msg_text, "Received"); snd.msg_type = rcvd.msg_type; snd.d.no = rcvd.d.no; snd.d.id = getpid(); if (msgsnd(id2, &snd, sizeof(struct details), IPC_NOWAIT) < 0) { perror("msgsnd"); } else { printf("Reply sending successful for message %d for process %d\n", snd.d.no, snd.d.id); } } } if (p_id == getpid()) { for (j = 1; j <= m; j++) { strcpy(snd.d.msg_text, message[j]); snd.msg_type = j + 1; snd.d.no = j; if (msgsnd(id1, &snd, sizeof(struct details), 0) < 0) { perror("msgsnd"); } else { printf("Message %d sent- \t\t\t\t%s\n", j, snd.d.msg_text); } for (i = 1; i <= n; i++) { sleep(i); if (msgrcv(id2, &rcvd, sizeof(struct details), j + 1, 0) >= 0) { printf("Reply received from child %d for message %d - \t%s\n", rcvd.d.id, j, rcvd.d.msg_text); } } } wait(NULL); printf("Communication End.\n\n"); } }
程序停滞时的输出
Queues are created. Message 1 sent- Message1 Process 14804 waiting for message 1 Process 14805 waiting for message 1 Reply sending successful for message 1 for process 14804 Process 14804 waiting for message 2 Reply received from child 14804 for message 1 - Received
问题根源
- 消息队列共享逻辑错误:父进程只向队列发送一条消息,但所有子进程都在等待接收这条消息。消息队列是共享资源,只有第一个调用
msgrcv的子进程能获取到消息,其他子进程会一直卡在空循环里等待,父进程无法收到所有回复,进而停滞。 - 无效的sleep等待:父进程循环等待回复时加入
sleep(i),但核心问题是未拿到消息的子进程根本无法发送回复,睡眠无法解决本质问题。 - 子进程空循环耗资源:子进程用
IPC_NOWAIT模式反复调用msgrcv,未拿到消息就继续循环,空耗CPU且无法退出等待。
修复方案
- 给每个子进程分配唯一消息类型:父进程发送消息时,为每个子进程设置专属
msg_type,子进程只接收自己类型的消息;回复时也用对应类型,确保父进程能匹配接收。 - 替换阻塞模式等待:子进程使用阻塞版
msgrcv(去掉IPC_NOWAIT),无需空循环;父进程等待回复时直接阻塞,无需多余sleep。 - 等待所有子进程退出:原代码仅调用一次
wait(NULL),改为循环等待所有子进程退出,避免僵尸进程。 - 清理消息队列:程序结束时删除消息队列,避免系统资源泄漏。
修复后的代码
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/wait.h> #include <stdio.h> #include <string.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> #define MAX_LEN 50 struct details { int no; int id; char msg_text[MAX_LEN]; }; typedef struct msg_buf { long int msg_type; struct details d; } msg; char message[20][50] = { " ", "Message1", "Message2", "Message3", "Message4", "Message5", "Message6", "Message7", "Message8" }; int main(int argc, char *argv[]) { int id1, id2, n, m, i, j, status, p_id; pid_t pid, child_pids[10]; msg snd, rcvd; if (argc < 3) { perror("Too few arguments, need n and m"); return -1; } n = atoi(argv[1]); m = atoi(argv[2]); id1 = msgget((key_t)78, IPC_CREAT | 0666); id2 = msgget((key_t)56, IPC_CREAT | 0666); if (id1 == -1 || id2 == -1) { printf("Error in creating message queue\n"); return -1; } p_id = getpid(); printf("\nQueues are created.\n\n"); for (i = 1; i <= n; i++) { pid = fork(); if (pid == 0) break; child_pids[i-1] = pid; } if (pid == 0) { int child_num = i; for (j = 1; j <= m; j++) { printf("Process %d waiting for message %d\n", getpid(), j); if (msgrcv(id1, &rcvd, sizeof(struct details), child_num, 0) < 0) { perror("msgrcv child"); exit(1); } strcpy(snd.d.msg_text, "Received"); snd.msg_type = child_num; snd.d.no = rcvd.d.no; snd.d.id = getpid(); if (msgsnd(id2, &snd, sizeof(struct details), 0) < 0) { perror("msgsnd child"); exit(1); } else { printf("Reply sending successful for message %d for process %d\n", snd.d.no, snd.d.id); } } exit(0); } if (p_id == getpid()) { for (j = 1; j <= m; j++) { printf("Sending message %d to all children\n", j); for (i = 1; i <= n; i++) { strcpy(snd.d.msg_text, message[j]); snd.msg_type = i; snd.d.no = j; if (msgsnd(id1, &snd, sizeof(struct details), 0) < 0) { perror("msgsnd parent"); } else { printf("Message %d sent to child %d - %s\n", j, child_pids[i-1], snd.d.msg_text); } } for (i = 1; i <= n; i++) { if (msgrcv(id2, &rcvd, sizeof(struct details), i, 0) >= 0) { printf("Reply received from child %d for message %d - %s\n", rcvd.d.id, j, rcvd.d.msg_text); } } } for (i = 0; i < n; i++) { waitpid(child_pids[i], &status, 0); } msgctl(id1, IPC_RMID, NULL); msgctl(id2, IPC_RMID, NULL); printf("Communication End.\n\n"); } return 0; }
内容的提问来源于stack exchange,提问作者Hamsa
相关产品推荐
相关产品推荐

