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

父子进程消息队列通信阻塞问题排查及解决方案

进程间消息队列通信停滞问题排查与解决

父进程需向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且无法退出等待。

修复方案

  1. 给每个子进程分配唯一消息类型:父进程发送消息时,为每个子进程设置专属msg_type,子进程只接收自己类型的消息;回复时也用对应类型,确保父进程能匹配接收。
  2. 替换阻塞模式等待:子进程使用阻塞版msgrcv(去掉IPC_NOWAIT),无需空循环;父进程等待回复时直接阻塞,无需多余sleep。
  3. 等待所有子进程退出:原代码仅调用一次wait(NULL),改为循环等待所有子进程退出,避免僵尸进程。
  4. 清理消息队列:程序结束时删除消息队列,避免系统资源泄漏。

修复后的代码

#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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 23:37:01