消息队列
消息队列是在内核空间开辟的一块共享内存, 类似于以下结构:
内核提供共享区域做IPC
类似于具名管道, 消息队列也有一个标识符MSG_KEY, 用来标识不同的消息队列. 只要知道某个消息队列的标识符, 并且拥有相应的权限, 就可以使用相应的消息队列. 所以, 消息队列可以在没有亲缘关系的进程间使用.
Linux系统调用为我们提供了几个C接口用于消息队列, 在sys/msg.h可以找到定义. 主要是以下4个函数:
|
|
msgget
接收MSG_KEY和权限flag, 用于创建或者打开一个消息队列.msgsnd
接收msg_id和消息内容以及消息控制flag, 用于发送消息.msgrcv
接收msg_id和接收消息的容器以及消息控制flag, 用于接收消息.msgctl
接收msg_id和控制命令, 用于控制消息队列.
在bits/ipc.h可以找到各种flag:
|
|
/* Control commands for msgctl',
semctl’, and shmctl'. */ #define IPC_RMID 0 /* Remove identifier. */ #define IPC_SET 1 /* Set
ipc_perm’ options. /
#define IPC_STAT 2 / Get `ipc_perm’ options. */
#ifdef __USE_GNU
define IPC_INFO 3 /* See ipcs. */
#endif
接下来, 我们开启两个进程, 使用消息队列实现进程间通信, 一个用于发送消息, 一个用于接收消息.
发送端
|
|
#define MSG_KEY 7777
struct MSG {
long type;
char msg[1024];
};
int main() {
int msg_id = msgget(MSG_KEY, IPC_EXCL);
if (msg_id < 0)
{
msg_id = msgget(MSG_KEY, IPC_CREAT | 0666);
}
if (msg_id < 0)
{
printf("get msg queue failed!");
return -1;
}
printf("msq key %d id %d\n", MSG_KEY, msg_id);
while(1)
{
struct MSG msg;
printf("msg type: ");
scanf("%ld", &msg.type);
printf("msg info: ");
scanf("%s", &msg.msg);
int snd_id = msgsnd(msg_id, &msg, sizeof(msg.msg), IPC_NOWAIT);
if (snd_id < 0)
{
printf("send msg failed with errno=%d[%s]\n", errno, strerror(errno));
msgctl(msg_id, IPC_RMID, 0);
return -1;
}
}
}
发送端定义了#define MSG_KEY 7777
, MSG_KEY
可以是任意的KEY, 不要和已有的混淆即可. 在创建消息队列的时候加了额外的权限, msgget(MSG_KEY, IPC_CREAT | 0666);
, 6
对应的是0110
表示read和write. 在发送消息的时候, 如果发送失败, 则会移除对应的消息队列msgctl(msg_id, IPC_RMID, 0);
.
此外, 我们还定义了一个结构体用来作为消息容器:
|
|
第一个成员是long型, 作为消息的ID, 这意为着在同一个消息队列中, 每条消息都可以拥有不同的消息ID. 所以仅使用一个消息队列, 也可以在多个进程间实现通信, 且多个进程可以互不干扰.(关于这一点, 继续看后面的接收端就清楚了)
启动发送端程序, 输入消息ID和消息内容:
|
|
接收端
|