微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

消息队列未按预期接收信息

如何解决消息队列未按预期接收信息

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

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,IPC_NowAIT);

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

}

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

怎么了?

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

解决方法

有很多问题。

当您执行 ctrl-c 时,信号将被 发送给父母和孩子。 必须禁用信号,否则它将被默认信号操作杀死。

在执行 IPC_NOWAIT 时,父母应该使用 msgsnd

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

您可以将范围声明放在 case [IIRC] 中。最好将这些移至函数作用域。

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

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

msgsndmsgrcv 的长度参数与 sizeof(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,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,IPC_NOWAIT);
#else
        err = msgsnd(id_queue,0);
        if (err < 0)
            fault("parent: msgsnd fail -- %s\n",strerror(errno));
#endif

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

    return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。