【问题标题】:why are results of message queue on macOS and Ubuntu different为什么 macOS 和 Ubuntu 上的消息队列结果不同
【发布时间】:2017-04-28 09:58:48
【问题描述】:

最近我正在学习 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);
}

高级感谢!

【问题讨论】:

    标签: linux message-queue ubuntu-16.04 macos-sierra


    【解决方案1】:

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

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

    不管怎样,你有一个竞争条件,因为进程 D 会立即删除消息队列,而不是等待它变空。因此,如果进程 B 因队列已满而阻塞,而进程 D 将其删除,则 B 将返回错误。但这一切都取决于进程的调度,因此也是竞争条件。

    您可以在 macOS 中更改队列大小,但您需要拥有超级用户权限才能使其生效。您可以通过将 MAX_TEXT 定义减少到类似 128 并验证它是否适用于两者来验证这一点而无需以 root 身份运行。

    【讨论】:

    • 没有比赛吗?消息队列可以在使用前被删除。因为没有同步,所以不能保证这不会发生。进程 B 在 macOS 上被阻止使这种情况更有可能发生。在 Linux 上,我认为它也不能保证工作。
    • @CongMa 是的,当你发表评论时,我正在编辑我的帖子以反映这一点!
    • @tau 太棒了!无论如何,您绝对应该清理比赛条件。使用 SYSV IPC 并没有真正的好方法,但msgctl 的联机帮助页描述了如何获取有关队列的信息。例如,进程 D 可以等到发送了许多字节或消息。全局信号量也可以工作。
    • 在删除this link之前检查消息队列是否为空的好方法吗?
    • @tau 是的,但这并不能解决您的问题。当进程或线程被调度的特定顺序改变了程序的行为时,就会出现竞争条件。想象一下,您的进程 D 等到队列“空”后才删除它,但随后进程 C 在进程 B 可以发送消息 3 之前耗尽了消息 1 和 2 的队列。然后可以删除队列,B 将等待发送, C 将等待接收。所以它不能解决你的问题。 Proc C 需要在收到所有消息后向 D 发出信号,然后 D 才能真正删除队列。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-30
    • 2016-05-29
    • 2018-08-04
    • 1970-01-01
    • 2010-09-13
    • 2015-10-23
    相关资源
    最近更新 更多