C语言父子进程IPC消息队列发送不同Key消息异常求助
C语言IPC消息队列问题:子进程异常退出+消息接收值为-1
我在C语言中尝试通过父子进程IPC消息队列实现通信,在子进程的同一个函数内发送两条不同Key的整数消息,但其中一条消息被父进程接收时始终返回-1。另外,子进程发送完两条消息后,会直接跳过while(runstatus)循环,执行终止打印并返回0,用gcc编译后问题依旧。期望父进程能通过msgrcv正常接收消息,打印子进程PID和对应的cpu_burst值。
相关代码
main.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/time.h> #include <sys/wait.h> #include <signal.h> #include "msg_cm.h" #include "runq.h" #include "waitq.h" #include "cidq.h" void timer_handler(int signum) { int temp = runq[0]; putwtq(temp); rmvrnq(); } int child(int key, int quant){ int cpid = getpid(); srand(cpid); int cpu_burst = rand() % 100 + 6; int io_burst = rand() % 100 + 1; msg_sndr(key, cpid); msg_sndr(cpid, cpu_burst); int runstatus = 1; int onoffchk = 0; while(runstatus) { if (msg_rcvr(getpid()) > 0) { onoffchk = 1 - onoffchk; } if (onoffchk == 1) { cpu_burst = cpu_burst - quant; } } printf("Child process terminated.\n"); msg_sndr(5678, 3); return 0; } int main() { int temp = 500000; int cnt = 1; int pid; int quantum = 5; int count = 9; int comp[10]; while(temp > 0) { msg_rcvr(1234); temp = temp -1; } printf("Parent running: %d\n", getpid()); for (int i = 0; i < 10; i++) { pid = fork(); if(pid == 0) { break; } else if (pid > 0) { printf("Child process : %d created.\n", pid); } else { printf("fork() failed!!!"); return -1; } } if(pid == 0) { child(1234, quantum); } else { int tick = 1; int end = 10; struct sigaction sigac; struct itimerval timer; struct itimerval stoptimer; sigac.sa_handler = timer_handler; sigaction(SIGALRM, &sigac, NULL); timer.it_value.tv_sec = quantum; timer.it_value.tv_usec = 0; timer.it_interval.tv_sec = quantum; timer.it_interval.tv_usec = 0; stoptimer.it_value.tv_sec = 0; stoptimer.it_value.tv_sec = 0; stoptimer.it_interval.tv_sec = 0; stoptimer.it_interval.tv_sec = 0; setitimer(ITIMER_REAL, &timer, NULL); while(end > 1){ if (msg_rcvr(5678) > 0) { end--; setitimer(ITIMER_REAL, &stoptimer, NULL); setitimer(ITIMER_REAL, &timer, NULL); } if (msg_exist(1234) != 2) { putrnq(msg_rcvr(1234)); } if (runq[0] == -1) { printf("%d\tidle\n",tick); } else { printf("%d\tProcess: %d running...\tCPU burst: %d\n", tick, runq[0], msg_rcvr(runq[0])); } tick++; sleep(1); } printf("All childs are terminated.\n"); } return 0; }
msg_cm.h
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> struct msgdata { long int msg_type; int text; }message; int msg_sndr(int key, int num) { int msgid; struct msgdata message; char buf[20]; msgid = msgget((key_t)key, 0666 | IPC_CREAT); if (msgid == -1) { printf("error while creating msg q!!!"); return -1; } message.msg_type = 1; message.text = num; if (msgsnd(msgid, (void *)&message, sizeof(message), 0) == -1) { printf("msg not sent!!!"); return -1; } } int msg_rcvr(int key) { int msgid; struct msgdata message; long int msg_to_rec = 0; msgid = msgget((key_t)key, 0666| IPC_CREAT); if (msgrcv(msgid, (void *)&message, sizeof(message), msg_to_rec, 0 | IPC_NOWAIT) > 0) { return message.text; } else { return -1; } msgctl(msgid, IPC_RMID, 0); } int msg_exist(int key) { int msgid; msgid = msgget((key_t)key, 0666| IPC_CREAT); struct msqid_ds buf; int rc = msgctl(msgid, IPC_STAT, &buf); unsigned int msg = (unsigned int)(buf.msg_qnum); rc = msgctl(msgid, IPC_STAT, &buf); msg = (uint)(buf.msg_qnum); if (msg > 0) { return 1; } else { return 2; } }
idq.h
#include <stdio.h> #include <string.h> #include <stdlib.h> int cburstq[20]; int ccount = 0; void putidq(int num) { cburstq[ccount] = num; ccount++; } int rmvidq() { int tmp = cburstq[0]; for (int tmpnm = 0; tmpnm < ccount; tmpnm++){ cburstq[tmpnm] = cburstq[tmpnm + 1]; } ccount--; return tmp; }
问题根源及修复方案
1. 子进程跳过循环直接退出的原因
子进程的while(runstatus)循环没有退出条件,且runstatus始终为1,理论上应无限循环。实际退出是因为:
msg_sndr函数成功执行时无返回值,会返回随机栈值,可能触发未定义行为;- 子进程未收到父进程发送的消息,
onoffchk始终为0,cpu_burst不会减少,但如果内存被意外修改(如其他全局变量越界),会导致runstatus被篡改。
修复:
- 给
msg_sndr添加成功返回值:int msg_sndr(int key, int num) { // ... 原有代码 if (msgsnd(...) == -1) { printf("msg not sent!!!"); return -1; } return 0; // 添加成功返回值 } - 给循环添加合理退出条件,当
cpu_burst耗尽时终止:while(runstatus) { if (msg_rcvr(getpid()) > 0) { onoffchk = 1 - onoffchk; } if (onoffchk == 1) { cpu_burst = cpu_burst - quant; if (cpu_burst <= 0) { runstatus = 0; // 触发退出 } } }
2. 消息接收值为-1的原因
msgrcv参数错误:传递的消息大小包含了msg_type字段,而msgrcv要求仅传递消息正文(text字段)的大小,导致调用失败返回-1;- 消息队列未正确清理:父进程初始化时的循环反复调用
msg_rcvr,但msg_rcvr中删除队列的代码永远不会执行(在return之后),残留消息干扰新消息接收; msg_exist逻辑冗余:重复调用msgctl获取队列状态,且使用IPC_CREAT会创建不存在的队列,导致判断逻辑失效。
修复:
- 修正
msgrcv和msgsnd的消息大小参数:// msg_sndr中 if (msgsnd(msgid, (void *)&message, sizeof(message.text), 0) == -1) // msg_rcvr中 if (msgrcv(msgid, (void *)&message, sizeof(message.text), msg_to_rec, 0 | IPC_NOWAIT) > 0) - 简化
msg_exist函数,仅检查队列是否存在及消息数量:int msg_exist(int key) { int msgid = msgget((key_t)key, 0666); // 不创建新队列,仅检查存在性 if (msgid == -1) return 2; struct msqid_ds buf; if (msgctl(msgid, IPC_STAT, &buf) == -1) return 2; return (buf.msg_qnum > 0) ? 1 : 2; } - 父进程打印CPU burst时,先接收消息并保存,避免重复调用
msg_rcvr导致消息被消耗:else { int cpu_burst_val = msg_rcvr(runq[0]); printf("%d\tProcess: %d running...\tCPU burst: %d\n", tick, runq[0], cpu_burst_val); }
内容的提问来源于stack exchange,提问作者ULICK NORMAN OWEN
相关产品推荐
相关产品推荐

