消息队列未按预期接收信息
Message queue not receiving information as expected
我正在编写一个程序,我想使用消息队列来安排程序结束。这是代码的架构:
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
,则子进程循环应该中断。我已经对其进行了测试,但它没有进入(在 while(1)
循环之后不打印 write
)所以我发现我在通过消息队列发送或接收数据时遇到了一些错误。
怎么了?
此外,程序在我发送 SIGINT
中断后结束并且 wait(NULL)
没有等待子进程结束,因为 write
没有通过屏幕打印。 .
有很多问题。
当您执行 ctrl-c
时,信号将同时发送 parent 和 child。 child 必须 禁用 信号,否则它将被默认信号操作杀死。
parent 应该 不 在执行 msgsnd
时使用 IPC_NOWAIT
。
在child中,可以做IPC_NOWAIT
,但我们必须检查return值和 loop 如果它 returns -1(即消息内容 not 否则有效)。
您可以不在case
[IIRC]中放置范围声明。最好将它们移动到功能范围。
disconnect
应声明为 volatile
。这足以满足您的用例。在更一般的情况下[正如其他人提到的那样],你应该使用 stdatomic.h
原语。
有很多特殊常量的硬编码(例如 1
对应 .num
)。
msgsnd
和 msgrcv
的长度参数与 sizeof(int)
硬连线。 [如果将其他字段添加到 Message
结构],这不会很好地扩展。
您可以使用ftok
获得key_t
值。但是,在这里,它太过分了。只需在 parent [fork
之前] 使用 IPC_PRIVATE
。这就是大多数程序在 仅 做 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;
}
我正在编写一个程序,我想使用消息队列来安排程序结束。这是代码的架构:
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
,则子进程循环应该中断。我已经对其进行了测试,但它没有进入(在 while(1)
循环之后不打印 write
)所以我发现我在通过消息队列发送或接收数据时遇到了一些错误。
怎么了?
此外,程序在我发送 SIGINT
中断后结束并且 wait(NULL)
没有等待子进程结束,因为 write
没有通过屏幕打印。 .
有很多问题。
当您执行 ctrl-c
时,信号将同时发送 parent 和 child。 child 必须 禁用 信号,否则它将被默认信号操作杀死。
parent 应该 不 在执行 msgsnd
时使用 IPC_NOWAIT
。
在child中,可以做IPC_NOWAIT
,但我们必须检查return值和 loop 如果它 returns -1(即消息内容 not 否则有效)。
您可以不在case
[IIRC]中放置范围声明。最好将它们移动到功能范围。
disconnect
应声明为 volatile
。这足以满足您的用例。在更一般的情况下[正如其他人提到的那样],你应该使用 stdatomic.h
原语。
有很多特殊常量的硬编码(例如 1
对应 .num
)。
msgsnd
和 msgrcv
的长度参数与 sizeof(int)
硬连线。 [如果将其他字段添加到 Message
结构],这不会很好地扩展。
您可以使用ftok
获得key_t
值。但是,在这里,它太过分了。只需在 parent [fork
之前] 使用 IPC_PRIVATE
。这就是大多数程序在 仅 做 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;
}