目录
-
消息队列
消息队列(Message Queue,简称MQ)提供了一个从一个进程向另外一个进程发送一块数据的方法,每个数据块都被认为 是有一个类型,接收者进程接收的数据块可以有不同的类型值。消息队列也有管道一样的不足,就是每个消息的最大长度是有上 限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限 (MSGMNI)。
MQ 传递的是消息,消息即是我们需要在进程间传递的数据。MQ 采用链表来实现消息队列,该链表是由系统内核维护,系统中可能有很多的 MQ,每个 MQ 用消息队列描述符(消息队列 ID:qid)来区分,qid 是唯一的,用来区分不同的 MQ。在进行 进程间通时一个进程将消息加到 MQ 尾端,另一个进程从消息队列中取消息(不一定以先进先出来取消息,也可以按照消息 类型字段取消息),这样就实现了进程间的通信。如下 MQ 的模型, 进程 1 向内核维护的消息队列中发消息,进程 1 从消息队 列中取消息,从而实现了 A1和 2的进程间通信。
-
MQ的基本步骤
- 创建和访问 MQ
- 发送消息
- 接受消息
- 删除 MQ
-
基本API
- ftok()
#include <sys/types.h> #include <sys/ipc.h> key_t ftok(const char *pathname, int proj_id);
该函数根据pathname指定的文件或目录的索引节点号和proj_id计算并返回一个key_t类型的ID值,如果失败则返回-1;
第一个参数pathname是一个系统中必须存在的文件或文件夹的路径,会使用该文件的索引节点;
第二个参数proj_id是用户指定的一个子序号,这个数字有的称之为project ID。它是一个8bit的整数,取值范围是1~255。
注意:如果要确保key值不变,要么确保ftok()的文件不被删除,要么不用ftok()指定一个固定的key值。
-
msgget()
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t key, int msgflg);
说明:该函数用来创建消息队列ID,成功返回一个非负整数,即该共享内存段的标识码;失败返回-1。
第一个参数key是ftok()返回的key_t类型键值;
第二个参数msgflg是创建标志:IPC_CREAT,不存在则创建,存在则返回已有的mqid,IPC_CREAT|IPC_EXCL,不存在则创建,存在则返 回出错。
3.msgsnd()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
说明:用来来发送一个消息,必须要有写消息队列的权限。成功返回 0, 失败返回 -1, 并设置 erron。
第一个参数msgid是由msgget函数返回的消息队列 ID;
第二个参数msgp是一个指针,它指向要发送的消息结构体类型的变量。消息结构在两方面受到制约。首先,它必须小于系统规定的上 限值; 其次,它必须以一个long int长整数开始,接收者函数将利用这个长整数确定消息的类型,其参考类型定义形式如下:
typedef struct st_msgbuf{
long mtype;
char mtext[256];
} t_msgbuf;
第三个参数msgsz是要发送消息的长度;
第四个参数msgflg 控制着当前消息队列满或到达系统上限时将要发生的事情,设置为IPC_NOWAIT表示队列满不等待,返回EAGAIN 错误。
4.msgrcv()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
说明:用来从一个消息队列接收消息。成功返回实际放到接收缓冲区里去的字符个数,失败返回-1, 并设置 erron。
第一个参数msgid是由msgget函数返回的消息队列 ID;
第二个参数msgp是一个指针,它指向准备接收的消息;
第三个参数msgsz是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型;
第四个参数msgtype是消息的类型,它可以实现接收优先级的简单形式;
msgtype=0返回队列第一条信息 msgtype>0返回队列第一条类型等于msgtype的消息
msgtype<0返回队列第一条类型小于等于msgtype绝对值的消息,并且是满足条件的消息类型最小的消息;
第五个参数msgflg 控制着队列中没有相应类型的消息可供接收时将要发生的事;
msgflg=IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误。
msgflg=MSG_NOERROR,消息大小超过msgsz时被截断
msgtype>0且msgflg=MSG_EXCEPT,接收类型不等于msgtype的第一条消息。
5.msgctl()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
说明:该函数用于控制消息队列
第一个参数msgid是由msgget函数返回的消息队列 ID;
第二个参数cmd是要采取的操作,它可以取下面的三个值:
IPC_STAT:把msqid_ds结构中的数据设置为消息队列的当前关联值;
IPC_SET: 如果进程有足够的权限,就把消息队列的当前关联值设置为msqid_ds结构中给出的值;
IPC_RMID:删除消息队列;
第三个参数buf是一个结构指针,它指向存储消息队列的相关信息的 buf;
-
编程示例
- msgqueue_send 代码
#include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #define FTOK_PATH "/dev/zero" #define FTOK_PROJID 0x22 typedef struct s_msgbuf { long mtype; char mtext[512]; } t_msgbuf; int main(int argc, char **argv) { key_t key; int msgid; t_msgbuf msgbuf; int msgtype; int i; if( (key=ftok(FTOK_PATH, FTOK_PROJID)) < 0 ) { printf("ftok() get IPC token failure: %s\n", strerror(errno)); return -1; } msgid = msgget(key, IPC_CREAT|0666); if( msgid < 0) { printf("shmget() create shared memroy failure: %s\n", strerror(errno)); return -2; } msgtype = (int)key; printf("key[%d] msgid[%d] msgypte[%d]\n", (int)key, msgid, msgtype); for(i=0; i<4; i++) { msgbuf.mtype = msgtype; strcpy(msgbuf.mtext,"Hello Word"); if( msgsnd(msgid, &msgbuf, sizeof(msgbuf.mtext), IPC_NOWAIT) < 0) { printf("msgsnd() send message failure: %s\n", strerror(errno)); break; } printf("Send message: %s\n", msgbuf.mtext); sleep(1); } msgctl(msgid, IPC_RMID, NULL); return 0; }
- msgqueue_send 代码
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define FTOK_PATH "/dev/zero"
#define FTOK_PROJID 0x22
typedef struct s_msgbuf {
long mtype;
char mtext[512];
} t_msgbuf;
int main(int argc, char **argv)
{
key_t key;
int msgid;
t_msgbuf msgbuf;
int msgtype;
int i;
if( (key=ftok(FTOK_PATH, FTOK_PROJID)) < 0 )
{
printf("ftok() get IPC token failure: %s\n", strerror(errno));
return -1;
}
msgid = msgget(key, IPC_CREAT|0666);
if( msgid < 0)
{
printf("shmget() create shared memroy failure: %s\n", strerror(errno));
return -2;
}
msgtype = msgtype = (int)key;
printf("key[%d] msgid[%d] msgypte[%d]\n", (int)key, msgid, msgtype);
for(i=0; i<4; i++)
{
memset(&msgbuf,0,sizeof(msgbuf));
if( msgrcv(msgid, &msgbuf, sizeof(msgbuf.mtext), msgtype, IPC_NOWAIT) < 0 )
{
printf("msgsnd() receive message failure: %s\n", strerror(errno));
break;
}
printf("Receive Message: %s\n", msgbuf.mtext);
sleep(1);
}
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
分析:进程msgqueue_send ;往内核的消息队列里写入内容"Hello Word",而 进程msgqueue_recv 则从消息队列里读出并打印该消息。