You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 12:31:02