C++ msgsnd 和 msgrvc 陷入睡眠
c++ msgsnd and msgrvc stuck in sleep
我有一个小的 C++ 程序,其中主进程是 "creating data",并将它们发送到应该读取该数据的子(fork)进程。我的问题是,在学校我的代码运行良好,但在我自己的笔记本电脑上,两个进程在程序启动后立即卡住。具体来说,他们都在 Waiting Channel "do_msgrcv".
这是我的代码:
#define VYROBA 1 // Manufacturer
#define PREPRAVA 2 // Transport
void manufacturer ( ) {
static int count = 0;
int rcv [ 2 ];
while ( 1 ) {
int snd [ 2 ] = { VYROBA, count };
int ret = msgsnd ( glb_msg_id, &snd, sizeof ( int ), 0 );
ret = msgrcv ( glb_msg_id, &rcv, sizeof ( int ), PREPRAVA, 0 );
printf ( "Got crate\n" );
}
}
void consumer ( ) {
static int count = 0;
int rcv [ 2 ];
while ( 1 ) {
int ret = msgrcv ( glb_msg_id, &rcv, sizeof ( int ), VYROBA, 0 );
usleep ( 500000 );
if ( ret < 0 ) {
printf ( "Can't read message.\n" );
}
printf ( "Got product: %d\r\n", rcv [ 1 ] );
fflush ( stdout );
rcv [ 1 ]++;
if ( rcv [ 1 ] == 10 ) {
int snd [ 2 ] = { PREPRAVA, rcv [ 1 ] };
ret = msgsnd ( glb_msg_id, &snd, sizeof ( int ), 0 );
} else {
ret = msgsnd ( glb_msg_id, &rcv, sizeof ( int ), 0 );
}
}
}
如果对学校有帮助,我们有 Ubuntu 12.04,我正在使用 Ubuntu 16.04。
感谢您的帮助。
您正在使用 linux 内核 since version 2.6 支持的系统 V 消息队列,因此您考虑两个 Ubuntu 版本。
现在如果你看一下手册,msgrcv()
and msgsnd()
使用一个消息缓冲区,它应该有这样的结构:
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
一旦您开始不使用此结构而是使用一些手动管理的缓冲区,您就有获得不可移植代码的风险(例如,您必须确保正确的大小和填充,考虑字节序等。 ..).这肯定是这里发生的事情。
消息结构以编码为 long
的消息类型开始,您假设它与 int
(数组的第一个元素)相同。但是 C++ 标准没有固定 int
和 long
的大小(只有它们的最小大小):这个大小可能 vary between platforms, compilers and CPU architecture。例如:
- 如果在大学里你 运行 Ubuntu 32 位,
int
和 long
都是 32 位并且大小相同。您的代码有效。
- 如果你在家里 运行 Ubuntu 64 位,你的
long
将是 64 位,而 int
仍然是 32 位。所以你的缓冲区会太短,导致 msgsnd()
和 msgrcv()
溢出缓冲区。这将是未定义的行为,更不用说消息类型将被破坏的事实。
我认为更改代码并为缓冲区使用正确的结构应该可以解决问题。
补充说明
顺便说一下,您的分叉逻辑使 manufacturer() 被执行了两次:在原始进程上,但在消费者完成后也在分叉进程上执行!
if ( !fork ( ) ) {
consumer ( );
}
manufacturer ( );
最好使用 else
以确保它只用在它应该用的地方。
我有一个小的 C++ 程序,其中主进程是 "creating data",并将它们发送到应该读取该数据的子(fork)进程。我的问题是,在学校我的代码运行良好,但在我自己的笔记本电脑上,两个进程在程序启动后立即卡住。具体来说,他们都在 Waiting Channel "do_msgrcv".
这是我的代码:
#define VYROBA 1 // Manufacturer
#define PREPRAVA 2 // Transport
void manufacturer ( ) {
static int count = 0;
int rcv [ 2 ];
while ( 1 ) {
int snd [ 2 ] = { VYROBA, count };
int ret = msgsnd ( glb_msg_id, &snd, sizeof ( int ), 0 );
ret = msgrcv ( glb_msg_id, &rcv, sizeof ( int ), PREPRAVA, 0 );
printf ( "Got crate\n" );
}
}
void consumer ( ) {
static int count = 0;
int rcv [ 2 ];
while ( 1 ) {
int ret = msgrcv ( glb_msg_id, &rcv, sizeof ( int ), VYROBA, 0 );
usleep ( 500000 );
if ( ret < 0 ) {
printf ( "Can't read message.\n" );
}
printf ( "Got product: %d\r\n", rcv [ 1 ] );
fflush ( stdout );
rcv [ 1 ]++;
if ( rcv [ 1 ] == 10 ) {
int snd [ 2 ] = { PREPRAVA, rcv [ 1 ] };
ret = msgsnd ( glb_msg_id, &snd, sizeof ( int ), 0 );
} else {
ret = msgsnd ( glb_msg_id, &rcv, sizeof ( int ), 0 );
}
}
}
如果对学校有帮助,我们有 Ubuntu 12.04,我正在使用 Ubuntu 16.04。
感谢您的帮助。
您正在使用 linux 内核 since version 2.6 支持的系统 V 消息队列,因此您考虑两个 Ubuntu 版本。
现在如果你看一下手册,msgrcv()
and msgsnd()
使用一个消息缓冲区,它应该有这样的结构:
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
一旦您开始不使用此结构而是使用一些手动管理的缓冲区,您就有获得不可移植代码的风险(例如,您必须确保正确的大小和填充,考虑字节序等。 ..).这肯定是这里发生的事情。
消息结构以编码为 long
的消息类型开始,您假设它与 int
(数组的第一个元素)相同。但是 C++ 标准没有固定 int
和 long
的大小(只有它们的最小大小):这个大小可能 vary between platforms, compilers and CPU architecture。例如:
- 如果在大学里你 运行 Ubuntu 32 位,
int
和long
都是 32 位并且大小相同。您的代码有效。 - 如果你在家里 运行 Ubuntu 64 位,你的
long
将是 64 位,而int
仍然是 32 位。所以你的缓冲区会太短,导致msgsnd()
和msgrcv()
溢出缓冲区。这将是未定义的行为,更不用说消息类型将被破坏的事实。
我认为更改代码并为缓冲区使用正确的结构应该可以解决问题。
补充说明
顺便说一下,您的分叉逻辑使 manufacturer() 被执行了两次:在原始进程上,但在消费者完成后也在分叉进程上执行!
if ( !fork ( ) ) {
consumer ( );
}
manufacturer ( );
最好使用 else
以确保它只用在它应该用的地方。