首页 > 解决方案 > 消息队列未按预期接收信息

问题描述

我正在编写一个程序,我想使用一个消息队列来安排程序结束。这是代码的架构:

typedef struct {
    long id;
    int num;
}Message;

int disconnect = 0;

void disconnection() {
    disconnect = 1;
}

int main () {
   
    //...

    int n = fork();
    int id_queue = msgget(ftok("connection.h", 66), 0600|IPC_CREAT);

    switch(n) {

        case 0: //child process

            Message message;
            message.id = 1;
            message.num = 0;

            while (1) {
                // ...

                msgrcv(id_queue, (struct msgbuf *) &message, sizeof(int), 1, IPC_NOWAIT);

                if (message.num == 1) break;
            }

            write(1, "Exit successfull\n", strlen("exit successfulll")); //The child process does not reach this line

            msgctl(id_queue, IPC_RMID, (struct msqid_ds *)NULL);
        break;

        default: //main process
            signal(SIGINT, disconnection);

            while (!disconnect) { /* ... */ }

            Message msg;
            msg.id = 1;
            msg.num = 1;

            msgsnd(id_queue, (struct msgbuf *) &msg, sizeof(int), IPC_NOWAIT);

            wait(NULL); //waiting for child process to end
        break;

}

如您所见,该程序一直有效,直到我用SIGINT. 为了结束子进程的断开连接过程是发送一条消息,int如果它是1,则子进程循环应该中断。write我已经对其进行了测试,但它没有进入(循环后不打印while(1)),所以我发现我在通过消息队列发送或接收数据时遇到了一些错误。

怎么了?

此外,程序在我发送SIGINT中断后结束并且wait(NULL)不等待子进程结束,因为write不会通过屏幕打印......

标签: cmessage-queue

解决方案


有很多问题。

当你这样做ctrl-c时,信号将同时发送给父母和孩子。孩子必须禁用信号,否则它将被默认的信号动作杀死。

父母在做的时候应该使用。IPC_NOWAITmsgsnd

在孩子中,它可以IPC_NOWAIT,但我们必须检查返回值,如果它返回 -1 则循环(即消息内容无效)。

不能case在[IIRC] 中放置范围声明。最好将这些移动到功能范围。

disconnect应该声明volatile。这对于您的用例来说已经足够了。在更一般的情况下[正如其他人提到的那样],您应该使用stdatomic.h原语。

有很多特殊常量的硬编码(例如1for .num)。

msgsnd和的长度参数msgrcvsizeof(int). 这不会很好地扩展[如果将其他字段添加到Message结构中]。

可以使用ftok来获取key_t值。但是,在这里,它是矫枉过正的。只需IPC_PRIVATE在父级[之前fork]中使用。这就是大多数程序所做的,如果他们只是在做fork


我已经重构了代码。我使用cpp条件标记了更改:

#if 0
// old code
#else
// new code
#endif

无论如何,这是工作代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define fault(_fmt...) \
    do { \
        printf(_fmt); \
        exit(1); \
    } while (0)

typedef struct {
    long id;
    int num;
} Message;

// equates to id
enum {
    MSGID_ANY,                      // receive any message
    MSGID_TOCLD,                    // send/receive messages to child
    MSGID_TOPAR,                    // send/receive messages to parent
};

// equates to num
enum {
    NUM_DO_X,                       // perform "x" ...
    NUM_DO_Y,                       // perform "y" ...
    NUM_STOP,                       // stop request
};

#if 0
int disconnect = 0;
#else
volatile int disconnect = 0;
#endif

void
disconnection()
{
    disconnect = 1;
}

int
main()
{
#if 1
    Message msg;
    int err;
#endif

    // ...

#if 0
    int n = fork();
    int id_queue = msgget(ftok("connection.h", 66), 0600 | IPC_CREAT);
#else
    int id_queue = msgget(IPC_PRIVATE, 0600 | IPC_CREAT);
    printf("parent: id_queue=%d\n",id_queue);
    int n = fork();
#endif

    switch (n) {
    case 0:  // child process
#if 0
        Message message;
#endif
        msg.id = MSGID_TOCLD;
        msg.num = 0;

#if 1
        signal(SIGINT, SIG_IGN);
#endif

        while (1) {
            // ...
#if 0
            msgrcv(id_queue, (struct msgbuf *) &msg, sizeof(int), 1,
                IPC_NOWAIT);
#else
            err = msgrcv(id_queue, (struct msgbuf *) &msg,
                sizeof(msg) - sizeof(msg.id), MSGID_TOCLD, IPC_NOWAIT);
            if (err < 0)
                continue;
            printf("child: got num=%d\n",msg.num);
#endif
            if (msg.num == NUM_STOP)
                break;
        }

        // The child process does not reach this line
#if 0
        write(1, "Exit successfull\n", strlen("exit successfulll"));
#else
        printf("Exit successful\n");
#endif

        msgctl(id_queue, IPC_RMID, (struct msqid_ds *) NULL);
        break;

    default:  // main process
        signal(SIGINT, disconnection);

        while (!disconnect) {
        }
        printf("Parent got signal\n");

#if 0
        Message msg;
#endif
        msg.id = MSGID_TOCLD;
        msg.num = NUM_STOP;

#if 0
        msgsnd(id_queue, (struct msgbuf *) &msg, sizeof(int), IPC_NOWAIT);
#else
        err = msgsnd(id_queue, (struct msgbuf *) &msg,
            sizeof(msg) - sizeof(msg.id), 0);
        if (err < 0)
            fault("parent: msgsnd fail -- %s\n",strerror(errno));
#endif

        // waiting for child process to end
        wait(NULL);
        break;
    }

    return 0;
}

推荐阅读