为什么 macOS 和 Ubuntu 上消息队列的结果不同
why are results of message queue on macOS and Ubuntu different
最近在Linux上学习进程通信。我写了一个 C 程序来做以下事情:
- 进程A建立一个消息队列(像一个邮箱)
- 进程B依次向队列发送“111”、“222”、“333”三个消息。
- 进程C从队列中以“333”、“111”、“222”的顺序读取消息。
- 进程 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 之类的值并验证它对两者都有效。
最近在Linux上学习进程通信。我写了一个 C 程序来做以下事情:
- 进程A建立一个消息队列(像一个邮箱)
- 进程B依次向队列发送“111”、“222”、“333”三个消息。
- 进程C从队列中以“333”、“111”、“222”的顺序读取消息。
- 进程 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 之类的值并验证它对两者都有效。