为什么 macOS 和 Ubuntu 上消息队列的结果不同

why are results of message queue on macOS and Ubuntu different

最近在Linux上学习进程通信。我写了一个 C 程序来做以下事情:

  1. 进程A建立一个消息队列(像一个邮箱)
  2. 进程B依次向队列发送“111”、“222”、“333”三个消息。
  3. 进程C从队列中以“333”、“111”、“222”的顺序读取消息。
  4. 进程 D 删除队列。

我首先在 Ubuntu 16.04 上编写了这个程序,它运行良好,正如我想的那样:

Results on Ubuntu 16.04

但是,当我编译相同的代码并尝试在 macOS(Sierra 10.12.3) 上 运行 时,结果与 Ubuntu 上的结果不同:

Results on macOS

不管我怎么修改代码(比如在进程A中休眠几秒),队列总是在第三条消息发送之前被删除。

这是我的代码:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <errno.h>

#define MAX_TEXT 1024

struct mymsg123
{
        long int priority;
        char text[MAX_TEXT];
};

int main()
{
    int msgid = -1; //message id
    struct mymsg123 data; //message to send

    //set up message queue(mailbox) in Process A
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    if(msgid == -1)
    {
            fprintf(stderr, "msgget failed in Process A with error: %d\n", errno);
            exit(EXIT_FAILURE);
    }
    printf("Mailbox created in Process A\n");

    pid_t pid = fork(); //to create Process B
    if (pid < 0) {fprintf(stderr, "Fork failed in Process A"); exit(EXIT_FAILURE); }
    //child Process B
    else if (pid == 0)
    {
        const char msg1[] = "111";
        //set the first message
        data.priority = 2;
        strcpy(data.text, msg1);
        //send the first message
        if (msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)
        {
            fprintf(stderr, "msgsnd failed in Process B when sending msg1\n");
            exit(EXIT_FAILURE);
        }
        printf("Sent msg %s, priority %ld to mailbox\n", data.text, data.priority);

        const char msg2[] = "222";
        //set the second message
        data.priority = 3;
        strcpy(data.text, msg2);
        //send the second message
        if (msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)
        {
            fprintf(stderr, "msgsnd failed in Porcess B when sending msg2\n");
            exit(EXIT_FAILURE);
        }
        printf("Sent msg %s, priority %ld to mailbox\n", data.text, data.priority);


        const char msg3[] = "333";
        //set the third message
        data.priority = 1;
        strcpy(data.text, msg3);
        //send the third message
        if (msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)
        {
            fprintf(stderr, "msgsnd failed in Process B when sending msg3\n");
            exit(EXIT_FAILURE);
        }   
        printf("Sent msg %s, priority %ld to mailbox\n", data.text, data.priority);
    }
    //parent Process A
    else
    {
        pid_t pid = fork(); //to create Process C
        if (pid < 0) {fprintf(stderr, "Fork failed in Process A"); exit(EXIT_FAILURE); }
        //child process C
        else if (pid == 0)
        {
            //receiving priority 1 message
            long int priority = 1;
            if(msgrcv(msgid, (void*)&data, BUFSIZ, priority, 0) == -1)
            {
                fprintf(stderr, "msgrcv failed with error in Process C when receiving priority 1 msg: %d\n", errno);
                exit(EXIT_FAILURE);
            }
            printf("Received priority 1 message in Process C from mailbox: %s\n", data.text);
            //receiving priority 2 message
            priority = 2;
            if(msgrcv(msgid, (void*)&data, BUFSIZ, priority, 0) == -1)
            {
                fprintf(stderr, "msgrcv failed with error in Prcess C when receiving priority 2 msg: %d\n", errno);
                exit(EXIT_FAILURE);
            }
            printf("Received priority 2 message in Process C from mailbox: %s\n", data.text);
            //receiving priority 3 message
            priority = 3;
            if(msgrcv(msgid, (void*)&data, BUFSIZ, priority, 0) == -1)
            {
                fprintf(stderr, "msgrcv failed with error in Process C when receiving priority 3 msg: %d\n", errno);
                exit(EXIT_FAILURE);
            }
            printf("Received priority 3 message in Process C from mailbox: %s\n", data.text);
        }
        //parent Process A
        else
        {
            pid_t pid = fork(); //to create Process D
            if (pid < 0) {fprintf(stderr, "Fork failed in Process A"); exit(EXIT_FAILURE); }
            //child process D
            else if (pid == 0)
            {
                //sleep(10);
                if (msgctl(msgid, IPC_RMID, 0) == -1)
                {
                    fprintf(stderr, "msgctl(IPC_RMID) failed in Process D\n");
                            exit(EXIT_FAILURE);
                }
                printf("Mailbox deleted in Process D\n");
            }
            //parent Process A
            else
                ;
        }
    }
    //sleep(10);

    exit(EXIT_SUCCESS);
}

非常感谢!

如果您打印错误消息本身,而不仅仅是错误代码,将会很有帮助。

无论如何,我认为这是因为 the message queue size appears to be 2048 bytes on macOS,但通常类似于 Linux 上的 16384。再加上您的 MAX_TEXT 大小为 1024,第三条消息无法发送,因为队列已满。它可能会阻塞,直到第一个 msgrcv 调用使第一条消息出队,释放 space,但 macOS 没有为此提供手册页,所以我要离开 Linux 页面。

无论如何,你有一个竞争条件,因为进程D会立即删除消息队列,而不是等待它变空。因此,如果进程 B 因为队列已满而阻塞,并且进程 D 将其删除,则 B 将 return 并出错。但这完全取决于进程的调度,因此是竞争条件。

您可以在 macOS 中更改队列大小,但您需要拥有超级用户权限才能生效。您可以在没有 运行 作为 root 的情况下验证这一点,方法是将您的 MAX_TEXT 定义减少到 128 之类的值并验证它对两者都有效。