今天带小伙伴们学习了消息队列相关的内容,先简单介绍下消息队列,然后对消息队列相关的结构及函数进行学习,最后撸代码使用一下这些函数使用一下消息队列,希望对大家有所帮助哈!
1 消息队列的概念及使用过程
1)消息队列的概念
消息队列就是一个消息的链表。一条消息可以看作一个数据记录,此数据具有特定的格式。
进程可以按照特定的规则向队列中添加(写入)消息;其他的进程则可以从消息队列中读走消息。
2)消息队列的应用场景
消息队列本身就是IPC通信中的内容,所以它主要用于进程间的通信。
消息有读写,所以发送的消息可以用于动作的通知信号,也可以接收数据然后做其他处理。
2 消息队列相关的结构及函数
0)消息队列相关的结构
每个队列都有一个msqid_ds结构与其相关联,结构如下。
- struct msqid_ds
- {
- struct ipc_perm msg_perm; // 消息队列的存取权限以及其他一些信息
- time_t msg_stime; // 最近一次队列接受消息的时间
- time_t msg_rtime; // 最近一次从队列中取出消息的时间
- time_t msg_ctime; // 最近一次队列发生改动的时间
- unsigned long __msg_cbytes; // 队列中消息的占用内存的字节数
- msgqnum_t msg_qnum; // 队列中当前的消息数
- msglen_t msg_qbytes; // 队列所占用内存的最大字节数
- pid_t msg_lspid; // 最近一次向队列发送消息的进程的pid msgsnd
- pid_t msg_lrpid; // 最近一次从队列中取出消息的进程的pid
- };
- struct ipc_perm
- {
- key_t key;
- ushort uid; // 用户id,有效的用户ID和有效的组id(euid和egid)
- ushort gid;
- ushort cuid; // 创建者的euid和egid
- ushort cgid;
- ushort mode; // 访问模式参见模式标志
- ushort seq; // IPC对象使用频率信息
- };
1)msgget函数
msgget函数用于创建或打开消息队列。
① 函数原型。
- int msgget(key_t key,int msgflg)
② 头文件。
- include <sys/ipc.h>
- include <sys/msg.h>
- include <sys/types.h>
③ 参数。
key:键值。
msgflg:打开标志。IPC_CREAT:表明新创建的一个消息队列。
④ 返回值。
成功:返回消息队列的id。
失败:-1。
2)msgsnd函数
msgsnd函数用于发送消息,即写消息到消息队列。
① 函数原型。
- int msgsnd(int msgid,const void *msgp,size_t msgsz,int msgflg)
② 头文件。
- include <sys/ipc.h>
- include <sys/types.h>
- include <sys/msg.h>
③ 参数。
msgid:消息队列的id。
msgp:指向要发送的消息。
msgsz:消息的长度。
msgflg:标志位。
④ 返回值。
成功:0。
失败:-1。
3)msgrcv函数
msgrcv函数用于读消息队列,即从消息队列接收消息。
① 函数原型。
- int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
- ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,intmsgflg)
② 头文件。
- #include <sys/ipc.h>
- #include <sys/types.h>
- #include <sys/msg.h>
③ 参数。
msqid:消息队列的id。
msgp:存放消息。
msgsz:希望取到的消息的最大长度。
msgtyp:消息的类型,分下面三种情况:
当 msgtyp = 0:忽略类型,直接取队列中的第一条消息。
当 msgtyp > 0: 取消息队列中类型等于msgtyp的第一条消息。
当 msgtyp < 0: 取类型比msgtyp的绝对值要小或等于的消息,如果有多条消息
满足该条件,取类型最小的一条。
④ 返回值。
成功:实际接收到的消息的数据长度。
失败:-1。
4)msgctl函数
msgctl函数用于操作消息队列,比如进行消息队列的删除等等。
① 函数原型。
- int msgctl(int msqid,int cmd,struct msqid_ds *buf)
② 头文件。
- #include <sys/ipc.h>
- #include <sys/msg.h>
- #include <sys/types.h>
③ 参数。
msqid:消息队列的id。
cmd:消息队列的操作命令,此参数指定对msqid指定的队列要执行的命令。
IPC_STAT:取此队列的msqidds结构,并将它存放在buf指向的结构中。
IPCSET:将字段msg_perm.uid、msg_perm.gid、msg_perm.mode和msg_qbytes从buf指向的结构复制到与这个队列相关的msqid_ds结构中。
此命令只能由下列两种进程执行:
一种是其有效用户ID等于msg_perm.cuid或msg perm.uid。
另一种是具有超级用户特权的进程。只有超级用户才能增加msg_qbytes的值。
IPC_RMID:从系统中删除该消息队列以及仍在该队列中的所有数据。这种删除立即生效。
仍在使用这一消息队列的其他进程在它们下一次试图对此队列进行操作时,将得到EIDRM错误。
此命令只能由下列两种进程执行:
一种是其有效用户ID等于msg_perm.cuid或msg_perm.uid。
另一种是具有超级用户特权的进程。这3条命令(IPC_STAT、IPC_SET和IPC_RMID)也可用于信号量和共享存储。
buf:获取内核中的msqid_ds结构,通常不用。
④ 返回值。
成功:0。
失败:-1。
3 实例代码
下面用两个进程,给大家演示下消息队列的使用过程。
实例代码如下,说明都在代码注释中了,图片。
SendQueue.c。
- #include<stdio.h>
- #include<sys/types.h>
- #include<sys/ipc.h>
- #include<sys/msg.h>
- #include<string.h>
- // 消息结构体
- struct msg
- {
- long msgtype; //消息的类型
- char msgtext[1024]; //消息的长度
- };
- void main(int argc, char *argv[])
- {
- int msgid;
- char str[256];
- struct msg msgst;
- key_t key = ftok("/tmp",600);
- //创建消息队列
- msgid = msgget(key,0666 | IPC_CREAT);
- //键盘输入消息
- while(1)
- {
- //获取消息数据
- printf("\nPlease enter a message to send,input 'end' to quit!\n\n");
- scanf("%s",str);
- strcpy(msgst.msgtext,str);
- if(strncmp(str, "end", 3) == 0)
- {
- printf("\n");
- break;
- }
- //发送消息
- msgsnd(msgid,&msgst,sizeof(struct msg),0);
- }
- //输出消息队列
- msgctl(msgid,IPC_RMID,0);
- }
ReceiveQueue.c。
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
- #include <sys/msg.h>
- // 消息结构体
- struct msg
- {
- long msgtype;
- char msgtext[1024];
- };
- int main(int argc, char *argv[])
- {
- int RunFlag = 1; // 循环标志
- int msgid = -1; // 消息id
- long msgtp = 0; // 消息类型
- struct msg msgst; // 消息结构体变量
- key_t key = ftok("/tmp",600); // 创建一个键值
- msgid = msgget(key, 0666 | IPC_CREAT); //建立消息队列
- if(msgid == -1)
- {
- exit(1); // 异常退出
- }
- while(RunFlag) // 从队列中获取消息,直到遇到end消息为止
- {
- if(msgrcv(msgid,&msgst,sizeof(struct msg), msgtp, 0) == -1)
- {
- exit(1); // 异常退出
- }
- printf("\nThe message received is: %s\n\n",msgst.msgtext);
- if(strncmp(msgst.msgtext, "end", 3) == 0) // 遇到end结束
- {
- RunFlag = 0; // 置退出循环标志
- }
- }
- if(msgctl(msgid, IPC_RMID, 0) == -1) // 删除消息队列
- {
- exit(1); // 异常退出
- }
- exit(0); // 正常退出
- }
编译程序,先运行接收程序,再运行发送程序,输入要发送的消息,退出输入end。
① 两个终端运行结果如下:
② 单个终端运行结果如下:
本文转载自微信公众号「嵌入式杂牌军」,可以通过以下二维码关注。转载本文请联系嵌入式杂牌军公众号。