c - 消息队列未按预期接收信息
问题描述
我正在编写一个程序,我想使用一个消息队列来安排程序结束。这是代码的架构:
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
不会通过屏幕打印......
解决方案
有很多问题。
当你这样做ctrl-c
时,信号将同时发送给父母和孩子。孩子必须禁用信号,否则它将被默认的信号动作杀死。
父母在做的时候不应该使用。IPC_NOWAIT
msgsnd
在孩子中,它可以做IPC_NOWAIT
,但我们必须检查返回值,如果它返回 -1 则循环(即消息内容无效)。
您不能case
在[IIRC] 中放置范围声明。最好将这些移动到功能范围。
disconnect
应该声明volatile
。这对于您的用例来说已经足够了。在更一般的情况下[正如其他人提到的那样],您应该使用stdatomic.h
原语。
有很多特殊常量的硬编码(例如1
for .num
)。
msgsnd
和的长度参数msgrcv
与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, (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;
}
推荐阅读
- javascript - 类型 'ClassNameMap<>' 不可分配给类型 'string'.ts
- java - 调用 AudioSystem.getClip() 时出现 IllegalArgumentException
- javascript - 来自 txt 的 Javascript 数组并在本地 html 上打印
- javascript - 如何将多个 JavaScript 对象数组填充到 HTMLDOM
- powerdns - 基于地理位置的域名解析
- c# - ASP.NET Core 5 - CreatedAtAction 在返回复杂对象时不返回任何数据
- reactjs - 在元素可见之前反应 useInView 触发器
- javascript - 来自json的Anychart范围列
- powershell - 远程签署 PowerShell 和 .DLL
- azure-devops - 如何使用相同的继承流程模板 Azure Devops 将相同的规则从一个工作项导入另一个