消息队列
消息队列就是一些消息的列表,用户可以在消息队列中添加消息和读取消息灯。消息队列具有一定的FIFO特性,但是它可以实现消息的随机查询,比FIFO更具有优势。同时,这些消息又是存在于内核中的,由“队列ID”来标识。
- 消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法
- 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值
- 消息队列也有管道一样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)
消息大小三大限制
- cat /proc/sys/kernel/msgmax最大消息长度限制
- cat /proc/sys/kernel/msgmnb消息队列总的字节数
- cat /proc/sys/kernel/msgmni消息条目数
IPC对象数据结构(进程间通信的工具)
内核为每个IPC对象维护一个数据结构
struct ipc_perm { key_t __key; /* Key supplied to xxxget(2) */ uid_t uid; /* Effective UID of owner */ gid_t gid; /* Effective GID of owner */ uid_t cuid; /* Effective UID of creator */ gid_t cgid; /* Effective GID of creator */ unsigned short mode; /* Permissions */ unsigned short __seq; /* Sequence number */ }; struct msqid_ds { struct ipc_perm msg_perm; /* Ownership and permissions */ time_t msg_stime; /* Time of last msgsnd(2) */ time_t msg_rtime; /* Time of last msgrcv(2) */ time_t msg_ctime; /* Time of last change */ unsigned long __msg_cbytes; /* Current number of bytes in queue (nonstandard) */ msgqnum_t msg_qnum; /* Current number of messages in queue */ msglen_t msg_qbytes; /* Maximum number of bytes allowed in queue */ pid_t msg_lspid; /* PID of last msgsnd(2) */ pid_t msg_lrpid; /* PID of last msgrcv(2) */ };
消息队列在内核中的表:
函数学习
创建和访问-msgget
函数原型
int msgget(key_t key, intmsgflg);
函数功能
用来创建和访问一个消息队列
所属头文件
<sys/types.h> <sys/ipc.h><sys/msg.h>
返回值
成功返回一个非负整数,即该消息队列的标识码;失败返回-1
参数说明
key: 某个消息队列的名字 msgflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
msgget函数参数关系图
控制-msgctl
函数原型
int msgctl(intmsqid, intcmd, structmsqid_ds *buf);
函数功能
消息队列的控制函数
所属头文件
<sys/types.h> <sys/ipc.h><sys/msg.h>
返回值
成功返回0,失败返回-1
参数说明
msqid: 由msgget函数返回的消息队列标识码 cmd:是将要采取的动作,(有三个可取值)
cmd:将要采取的动作(有三个可取值),分别如下:
发送和接收-msgsnd/msgrcv
函数原型
int msgsnd(intmsqid, const void *msgp, size_tmsgsz, intmsgflg);
函数功能
把一条消息添加到消息队列中
所属头文件
<sys/types.h> <sys/ipc.h><sys/msg.h>
返回值
成功返回0;失败返回-1
参数说明
msgid: 由msgget函数返回的消息队列标识码 msgp : 是一个指针,指针指向准备发送的消息, msgsz : 是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型 msgflg : 控制着当前消息队列满或到达系统上限时将要发生的事情 msgflg=IPC_NOWAIT表示队列满不等待,返回EAGAIN错误。 消息结构在两方面受到制约。 首先,它必须小于系统规定的上限值; 其次,它必须以一个long int长整数开始,接收者函数将利用这个长整数确定消息的类型 消息结构参考形式如下: structmsgbuf{ long mtype; char mtext[100]; }
函数原型
ssize_t msgrcv(intmsqid, void *msgp, size_tmsgsz, long msgtyp, intmsgflg);
函数功能
是从一个消息队列接收消息
所属头文件
<sys/types.h> <sys/ipc.h><sys/msg.h>
返回值
成功返回实际放到接收缓冲区里去的字符个数,失败返回-1
参数说明
msgid : 由msgget函数返回的消息队列标识码 msgp : 是一个指针,指针指向准备接收的消息, msgsz : 是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型 msgtype : 它可以实现接收优先级的简单形式 msgflg : 控制着队列中没有相应类型的消息可供接收时将要发生的事
msgtype = 0返回队列第一条信息 msgtype>0返回队列第一条类型等于msgtype的消息 msgtype<0返回队列第一条类型小于等于msgtype绝对值的消息,并且是满足条件的消息类型最小的消息 msgflg = IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误。 msgflg = MSG_NOERROR,消息大小超过msgsz时被截断 msgtype>0且msgflg = MSG_EXCEPT,接收类型不等于msgtype的第一条消息。
综合示例:
在该实例当中,发送端发送的消息类型设置为该进程的进程号(也可以取其他值),因此接受端根据消息类型来确定消息发送者的进程号。
messagesend.c
//使用消息队列发送信息 #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> #define BUFF_SIZE 512 struct message{ long msg_type; char msg_text[BUFF_SIZE]; }; void main() { key_t key=0; int qid; struct message msg; if( (key=ftok(".",'a')) == -1) { //创建键值失败 printf("creat key error\n"); exit(0); } //创建消息队列 /***************************** 函数原型:int msgget(key_t key,int msgflg) 函数参数:key 消息队列的键值,多个进程通过它访问一个消息 队列 msgflg 权限标志位 返回值:成功 返回消息队列的ID 失败 返回-1 *****************************/ if((qid=msgget(key,IPC_CREAT|0666))==-1) { printf("msgget\n",-1); exit(0); } //打印消息队列的ID printf("open queue %d\n",qid); while(1) { printf("Enter some message to the queue:\n"); if((fgets(msg.msg_text,BUFF_SIZE,stdin))==NULL) { printf("no message\n"); exit(1); } msg.msg_type=getpid(); /***************************** 函数原型:int msgsnd(int msqid, const void *msqp, size_t msgsz, int msgflg) 函数参数: msqid 消息队列的id msqp 指向消息结构的指针,该消息结构msgbuf通常如下 struct msgbuf { long mtype; //消息类型,该结构必须从这个域开始 char mtext[];//消息正文 } msgsz 消息正文的字节数(不包括消息类型指针变量) msgflg IPC_NOWAIT:若消息无法立即发送,函数会立刻返回 0 msgsnd:调用阻塞直到发送成功为止 返回值: 成功 0 失败 -1 *****************************/ if(msgsnd(qid,&msg,strlen(msg.msg_text),0)==-1) { printf("msgsnd error\n"); exit(0); } if(strncmp(msg.msg_text,"quit",4)==0) { break; } } exit(0); }
messagereceive.c
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> #define BUFF_SIZE 512 struct message{ long msg_type; char msg_text[BUFF_SIZE]; }; void main() { int qid; key_t key; struct message msg; if( (key=ftok(".",'a')) == -1) { //创建键值失败 printf("creat key error\n"); exit(0); } if((qid=msgget(key,IPC_CREAT|0666))==-1) { printf("msgget\n",-1); exit(0); } //打印消息队列的ID printf("open queue %d\n",qid); do { /************************ 函数原型:void *memset(void *s, int ch, size_t n); 函数描述:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s ************************/ memset(msg.msg_text,0,BUFF_SIZE); /*读取消息队列*/ /******************** 函数原型:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) 函数参数:msqid 消息队列的ID msgp 消息缓冲区,通msgsnd()函数的msgp msgsz 消息正文的字节数 (不包括消息类型指针变量) msgtyp 0 接受消息队列中的第一个消息 大于0 接受消息队列中第一个类型为msgtyp的消息 小于0 接受消息队列中第一个类型值不小于msgtyp绝对值且类型值最小的消息 msgflg 0 msgsnd()调用阻塞直到接受一条相应类型的消息为止 函数返回值:成功 0 失败 -1 ********************/ if(msgrcv(qid,(void *)&msg,BUFF_SIZE,0,0)<0) { printf("msgrcv\n"); exit(0); } printf("the message from %d:%s",msg.msg_type,msg.msg_text); }while(strncmp(msg.msg_text,"quit",4)); //删除消息队列 /* 函数原型:int msgctl(int msgqid,int cmd,struct msgqid_ds *buf) 函数参数:msgqid 消息队列的队列id cmd IPC_STAT 读取消息队列的数据结构msqid_ds,并将其存储在buf指定的地址中 IPC_SET 设置消息队列的数据结构msqid_ds中的ipc_perm域值,这个值取自buf参数 IPC_RMID 从内核中删除消息队列 buf 描述消息队列的msqid_ds结构类型变量 返回值: */ if(msgctl(qid,IPC_RMID,NULL)<0) { printf("msgctl\n"); exit(0); } }