版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ferlan/article/details/84190369
消息队列 Sysetm V
什么是消息队列
消息队列实际上是操作系统在内核为我们创建的一个队列.
关于组织一个带有类型的数据块,添加到队列中,其他的进程从队列中获取数据块.也就是说消息队列传输的是一个个带有类型的数据块.
消息队列是一个全双工通信,两个进程都可读可写.
在命令行中可以使用ipcs -q查看消息队列.
也可以使用ipcrm -q+msgid删除对应的消息队列
消息队列的生命周期随内核,不随进程,这就意味这如果不手动关闭消息队列且不重启计算机,那消息队列将一直存在与内核中.
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg *msg_first; /* first message on queue,unused */
struct msg *msg_last; /* last message in queue,unused */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of bytes on queue */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short msg_qbytes; /* max number of bytes on queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
struct msgbuf {//队列中节点结构体,需要自己定义
long mtype;
char mtext[1];
}
消息队列的应用函数
创建消息队列
功能:⽤来创建和访问⼀个消息队列
原型
int msgget(key_t key, int msgflg);
参数
key: 内核中某个消息队列的标识
msgflg:由九个权限标志构成,它们的⽤法和创建⽂件时使⽤的mode模式标
IPC_CREAT: 不存在则创建,存在则打开
IPC_EXCL: 与IPC_CREAT同用时,若存在则报错
mode: 倘若用IPC_CREAT创建则需要赋予权限
返回值:成功返回⼀个⾮负整数,即该消息队列的标识码;失败返回-1
发送数据/接收数据
msgsnd函数
功能:把⼀条消息添加到消息队列中
原型
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
参数
msgid: 由msgget函数返回的消息队列标识码
msgp:是⼀个指针,指针指向准备发送的消息,
msgsz:是msgp指向的消息⻓度,这个⻓度不含保存消息类型的那个long int⻓整型
msgflg:控制着当前消息队列满或到达系统上限时将要发⽣的事情
msgflg=IPC_NOWAIT表⽰队列满不等待,返回EAGAIN错误。
返回值:成功返回0;失败返回-1
功能:是从⼀个消息队列接收消息
原型:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg
参数
msgid: 由msgget函数返回的消息队列标识码
msgp:是⼀个指针,指针指向准备接收的消息,
msgsz:是msgp指向的消息⻓度,这个⻓度不含保存消息类型的那个long int⻓整型
msgtype:它可以实现接收优先级的简单形式
取0:按照队列的排列顺序接收
>0:取对应msgtype的数据,比如取3 就接收3号类型的数据的第一个节点
<0:接收小于msgtype绝对值的第一个节点,比如取-3,就接收第一个1,2号节点
msgflg:控制着队列中没有相应类型的消息可供接收时将要发⽣的事
默认为0
MSG_NOERROR 当数据长度超过指定长度,则截断数据
返回值:成功返回实际放到接收缓冲区⾥去的字符个数,失败返回-1
释放消息队列
功能:消息队列的控制函数
原型
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数
msqid: 由msgget函数返回的消息队列标识码
cmd:是将要采取的动作,(有三个可取值)
返回值:成功返回0,失败返回-1
利用消息队列实现进程间通信
- 创建消息队列
- 发送数据/接收数据
- 释放消息队列
msgqueue_c.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <sys/msg.h>
7 #include <sys/ipc.h>
8
9 //这个时消息队列的key
10 #define IPC_KEY 0x12345678
11 //下边这两个宏,用于我们赋值我们传输的数据块的类型
12 #define TYPE_S 1 //你是什么端就接收什么类型的数据,S端接收TYPE_S,
13 #define TYPE_C 2 // C端接收TYPE_C
14
15 struct msgbuf { //之所以要我们自己定义,是因为每条数据的最大长度要自己设定
16 long mtype; /* message type, must be > 0 */
17 char mtext[1024]; /* message data */
18 };
19 int main()
20 {
21 int msgid = -1;
22
23 //ftok
24 //key_t ftok(const char *pathname, int proj_id);
25 //ftok通过文件的inode节点号和一个proj_id计算得出一个key值
26 //缺点:如果文件被删除,或替换,那么将打开的不是同一个msgqueue
27 //1. 创建消息队列
28 msgid = msgget(IPC_KEY, IPC_CREAT | 0664);
29 if (msgid < 0) {
30 perror("msgget error");
31 return -1;
32 }
33 while(1) {
34 struct msgbuf buf;
35 //发送数据
36 memset(&buf, 0x00, sizeof(struct msgbuf));
37 buf.mtype = TYPE_C;
38 scanf("%s", buf.mtext);
39 msgsnd(msgid, &buf, 1024, 0);
40 //接收数据
41 memset(&buf, 0x00, sizeof(struct msgbuf));
42 msgrcv(msgid, &buf, 1024, TYPE_S, 0);
43 printf("boy say:[%s]\n", buf.mtext);
44
45 }
46 //IPC_RMID 删除IPC
47 msgctl(msgid, IPC_RMID, NULL);
48 }
msgqueue_s.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <sys/msg.h>
7 #include <sys/ipc.h>
8
9 //这个时消息队列的key
10 #define IPC_KEY 0x12345678
11 //下边这两个宏,用于我们赋值我们传输的数据块的类型
12 #define TYPE_S 1 //send
13 #define TYPE_C 2 //recv
14
15 struct msgbuf {
16 long mtype; /* message type, must be > 0 */
17 char mtext[1024]; /* message data */
18 };
19 int main()
20 {
21 int msgid = -1;
22
23 //ftok
24 //key_t ftok(const char *pathname, int proj_id);
25 //ftok通过文件的inode节点号和一个proj_id计算得出一个key值
26 //缺点:如果文件被删除,或替换,得到的inode节点号就改变了。那么将打开的不是同一个msgqueue
27
28 //1. 创建消息队列
29 msgid = msgget(IPC_KEY, IPC_CREAT | 0664);
30 if (msgid < 0) {
31 perror("msgget error");
32 return -1;
33 }
34 while(1) {
35 //接收数据
36 struct msgbuf buf;
37 //msgrcv 默认阻塞的获取数据
38 //msgid: 操作句柄
39 //buf: 接收数据的结构体,需要自己定义
40 //1024: 用于指定要接收的最大数据长度,不包含mtype
41 //TYPE_C: 用于指定接收的数据类型
42 //msgflag: 0-默认
43 // MSG_NOERROR 当数据长度超过指定长度,则截断数据
44 msgrcv(msgid, &buf, 1024, TYPE_C, 0);
45 printf("girl say:[%s]\n", buf.mtext);
46 //发送数据
47 memset(&buf, 0x00, sizeof(struct msgbuf));
48 buf.mtype = TYPE_S;
49 scanf("%s", buf.mtext);
50 msgsnd(msgid, &buf, 1024, 0);
51
52 }
53 //IPC_RMID 删除IPC
54 msgctl(msgid, IPC_RMID, NULL);
55 }
当两个都执行起来后,就可以进行通信了
消息队列的不足
消息队列也有管道⼀样的不⾜,就是每个消息的最⼤⻓度是有上限的(MSGMAX),每个消息队
列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有⼀个上限(MSGMNI)