一、消息队列的特点
消息队列是另外一种Linux下进程间通信的方式,可以在相互独立的进程间进行通信。通过在内核维护一个消息队列链表来通信,允许在进程间以异步的方式进行数据交换。相比与之前提到的共享内存,消息队列能传输特定类型的消息,可以根据先进先出的方式获取消息体,也可以获取特定类型的消息体,使得通信更加的便捷。消息队列有以下几个特点:
- 异步通信: 进程间传输数据不需要同步,可以随时传输
- 消息类型: 发送和接收都可以指定消息类型
- 先进先出: 如果不指定消息类型,则是按先进先出的方式来获取消息体
二、常见的API分析
1,msgget函数,用于创建或者获取一个消息队列标识符ID, 定义如下:
int msgget(key_t key, int msgflg);
key:键值,通过ftok获取,或者直接指定,key值唯一确定消息队列标识符ID
msgflag:消息队列的创建标志,同时还可以指定访问权限
IPC_CREATE : 消息队列不存在则创建
IPC_EXCL: 如果和IPC_CREATE一起使用,如果存在则返回错误,并返回已存在的错误码EEXIST
使用示例如下:
msgId = msgget(key, IPC_CREAT|IPC_EXCL|0666);
if(msgId == -1){
if(errno == EEXIST) //已存在则去获取
{
msgId = msgget(key, 0666);
}
}
2. msgsnd函数:发送消息,成功返回0,失败则返回-1,定义如下:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid:消息队列标识符
msqp:消息体地址,一般是一个以下的自定义结构体:
struct msgQueueData
{
long mtype; // 消息类型, 必须大于0
char mtext[128]; // 消息体,消息体可以任何格式,后面还可以有其他属性
...
};
msgsz: 消息体大小(不包括消息类型)
msgflg:发送标志,
0: 如果消息队列已满则阻塞,直到发送出去再返回。
IPC_NOWAIT: 不阻塞,立即返回。
使用示例如下:
struct msgQueueData sendBuff;
sendBuff.mtype = 20;
strncpy(sendBuff.mtext, "hello", sizeof(sendBuff.mtext));
while (1)
{
if(0 != msgsnd(msgId, &sendBuff, sizeof(sendBuff)-sizeof(long), 0)){
perror("msg send failed");
msgctl(msgId, IPC_RMID, NULL); //如果发送失败则删除消息队列标识符
return -1;
}
sleep(2);
}
3. msgrecv函数: 接收消息,成功返回数据大小,失败则返回-1,定义如下
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
msqid:消息队列标识符
msgq:存放消息数据的指针,类型和发送一致。
msgsz:msgq中空间的大小
msgtyp:消息类型,如果是0则从接受任意消息,先进先出
msgflg:接受标志
0: 没有数据则阻塞,直到接收到数据才返回
IPC_NOWAIT: 没有数据立即返回
使用示例如下:
int recvlen = msgrcv(msgId, &recvBuff, sizeof(recvBuff)-sizeof(long), 20, 0);
if(recvlen > 0){
printf("recv text:%s\n", recvBuff.mtext);
}else if(recvlen < 0){
perror("msg recv failed");
msgctl(msgId, IPC_RMID, NULL); //接收失败则删除消息队列标识符
return -1;
}
4. msgctl函数: 控制消息队列,比如删除消息队列,获取消息队列信息等
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msqid: 消息队列标识符
cmd:操作的命令
IPC_RMID: 删除消息队列, 第三个参数可以传NULL
IPC_INFO: 获取消息队列信息,信息结构体如下,可以获得消息队列最大的字节等信息:
struct msginfo {
int msgpool; /* Size in kibibytes of buffer pool
used to hold message data;
unused within kernel */
int msgmap; /* Maximum number of entries in message
map; unused within kernel */
int msgmax; /* Maximum number of bytes that can be
written in a single message */
int msgmnb; /* Maximum number of bytes that can be
written to queue; used to initialize
msg_qbytes during queue creation
(msgget(2)) */
int msgmni; /* Maximum number of message queues */
int msgssz; /* Message segment size;unused within kernel */
int msgtql; /* Maximum number of messages on all queues in system; unused within kernel */
unsigned short msgseg;/* Maximum number of segments;unused within kernel */
};
删除的使用示例如下:
msgctl(msgId, IPC_RMID, NULL);
三、代码示例
下面是一个代码示例,运行的时候传"s"参数标识消息队列服务端,接收消息数据; 运行的时候传 "c"标识客户端,发送消息。先运行服务端没有任何消息,然后运行客户端,就会以2s的间隔接收到 "hello" 字符串, 说明两个进程间已经实现了同步。这里采用的都是阻塞的收发方式。
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
struct msgQueueData
{
long mtype; // message type, must be > 0
char mtext[128]; // message data
};
int main(int argc, char ** argv)
{
int msgId;
int key = ftok("/tmp", 0x1234);
if(key == -1){
perror("ftok failed");
return -1;
}
msgId = msgget(key, IPC_CREAT|IPC_EXCL|0666);
if(msgId == -1){
if(errno == EEXIST)
{
msgId = msgget(key, 0666);
}
}
if(msgId < 0){
perror("msgId create or get failed");
return -1;
}
printf("msgId:%d\n", msgId);
if(strcmp(argv[1], "s") == 0){
struct msgQueueData recvBuff;
printf("This is msg queue server\n");
while(1){
int recvlen = msgrcv(msgId, &recvBuff, sizeof(recvBuff)-sizeof(long), 20, 0);
if(recvlen > 0){
printf("recv text:%s\n", recvBuff.mtext);
}else if(recvlen < 0){
perror("msg recv failed");
msgctl(msgId, IPC_RMID, NULL);
return -1;
}
}
}else if(strcmp(argv[1], "c") == 0){
printf("This is msg queue client\n");
struct msgQueueData sendBuff;
sendBuff.mtype = 20;
strncpy(sendBuff.mtext, "hello", sizeof(sendBuff.mtext));
while (1)
{
if(0 != msgsnd(msgId, &sendBuff, sizeof(sendBuff)-sizeof(long), 0)){
perror("msg send failed");
msgctl(msgId, IPC_RMID, NULL);
return -1;
}
sleep(2);
}
}else{
printf("args error\n");
msgctl(msgId, IPC_RMID, NULL);
return -1;
}
msgctl(msgId, IPC_RMID, NULL);
return 0;
}

1817

被折叠的 条评论
为什么被折叠?



