1.1. 什么是消息队列
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。
1.2. 相关的接口函数介绍
Linux提供了一系列消息队列的函数接口来让我们方便地使用它来实现进程间的通信。它的用法与其他两个System V PIC机制,即信号量和共享内存相似。
1、 ftok函数: 动态获取key值
|
2、 msgget函数:创建消息
|
3、 msgsnd函数:发送消息
|
4、 msgrcv函数:接收消息
|
5、 msgctl函数:控制消息
|
1.3. 内核限制
消息队列是IPC资源信息中的一种,所以可以通过ipcs确定系统的当前IPC限制及已使用的资源状况。
查看IPC:ipcs [-u](查看当前的IPC资源状况)、[-l](可以查看IPC限制值)
创建IPC:ipcmk [ [-q msqid] [-m shmid] [-s semid] [-Qmsgkey] [-M shmkey] [-S semkey] ... ]
删除IPC:ipcrm [[-Q] [-S <size>] [-M <nsems>]]
|
修改消息队列参数如下:
1、 永久修改:
在root用户下修改/etc/sysctl.conf配置文件
具体方法为:在sysctl.conf中加上 kernel.msgmni=10kernel.msgmax=2048 kernel.msgmnb=65535,然后运行sysctl –p即可修改。
在Red Hat中,rc.sysinit初始化脚本将自动读取/etc/sysctl.conf文件
而在SUSE Linux中,还需要激活 boot.sysctl服务(chkconfig boot.sysctl on)
2、 临时修改:
root用户下sysctl -w kernel.msgmni=10 kernel.msgmax=2048 kernel.msgmnb=65535
1.4. 实例
发送信息的程序MsgSend.c的源代码如下:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MSGKEY 1024
#define MAX_TEXT 512
struct msg_st
{
long int msg_type; //第一个参数必须是long的消息类型
char text[MAX_TEXT]; //此后可以定义多个数组和其他值
};
#define SPACE " "
#define PRINT_X(var1, var2) printf(""#var2"%.*s: 0X%08X\n", \
strlen(SPACE) - strlen(#var2), SPACE, var1.var2)
#define PRINT_D(var1, var2) printf(""#var2"%.*s: %d\n", \
strlen(SPACE) - strlen(#var2), SPACE, var1.var2)
void Show(int msqid)
{
int iRet = 0;
struct msqid_ds buf;
iRet = msgctl(msqid, IPC_STAT, &buf);
if(0 != iRet)
{
printf("msgctl error, errno=%d [%s]\n", errno, strerror(errno));
return ;
}
printf("\n");
PRINT_X(buf.msg_perm, __key);
PRINT_D(buf.msg_perm, uid);
PRINT_D(buf.msg_perm, gid);
PRINT_D(buf.msg_perm, cuid);
PRINT_D(buf.msg_perm, cgid);
PRINT_X(buf.msg_perm, mode);
PRINT_X(buf.msg_perm, __seq);
PRINT_X(buf, msg_stime);
PRINT_X(buf, msg_rtime);
PRINT_X(buf, msg_ctime);
PRINT_D(buf, __msg_cbytes);
PRINT_D(buf, msg_qnum);
PRINT_D(buf, msg_qbytes);
PRINT_X(buf, msg_lspid);
PRINT_X(buf, msg_lrpid);
printf("\n");
}
int main()
{
int running = 1;
struct msg_st data;
char buffer[MAX_TEXT];
int msgid = -1;
//建立消息队列
msgid = msgget(MSGKEY, 0666 | IPC_CREAT);
if(msgid == -1)
{
fprintf(stderr, "msgget failed, error=%d [%s]\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
Show(msgid);
//向消息队列中写消息,直到写入end
while(running)
{
//输入数据
printf("Enter some text: ");
fgets(buffer, MAX_TEXT, stdin);
data.msg_type = 1; //注意此处设置消息类型
strcpy(data.text, buffer);
/* 向队列发送数据, 如果是字符串,则参数3可以修改为字符串的长度,可以节省内存
msgsnd(msgid, (void*)&data, strlen(data.text), 0) */
if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)
{
fprintf(stderr, "msgsnd failed, error=%d [%s]\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
Show(msgid);
//输入end结束输入
if(strncmp(buffer, "end", 3) == 0)
{
running = 0;
}
sleep(1);
}
exit(EXIT_SUCCESS);
}
接收信息的程序MsgReceive.c的源代码如下:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
#define MSGKEY 1024
#define MAX_TEXT 512
struct msg_st
{
long int msg_type;
char text[MAX_TEXT];
};
int main()
{
int running = 1;
int msgid = -1;
struct msg_st data;
long int msgtype = 0; //此处设置为0,表示接收所有消息
//建立消息队列
msgid = msgget(MSGKEY, 0666 | IPC_CREAT);
if(msgid == -1)
{
fprintf(stderr, "msgget failed, error=%d [%s]\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
//从队列中获取消息,直到遇到end消息为止
while(running)
{
if(msgrcv(msgid, (void*)&data, MAX_TEXT, msgtype, 0) == -1)
{
fprintf(stderr, "msgrcv failed, error=%d [%s]\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
printf("You wrote: %s\n",data.text);
//遇到end结束
if(strncmp(data.text, "end", 3) == 0)
{
running = 0;
}
}
//删除消息队列
if(msgctl(msgid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "msgctl(IPC_RMID) failed, error=%d [%s]\n",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
1.5. 注意事项
一、每次msgrcv一个消息时:
1、那个消息会在内核中移除
2、每次msgrcv都只会给一个消息出来,不管你用多大的buf来接收,都是可以的。如果msgrcv的bufSize小于实际的该消息的大小,那么可以设置一个标志(MSG_NOERROR):表示截断。 如果不设置,那么会报错,取不出来
消息满了,则默认0为阻塞,直到有了空间位置,才能snd消息进入到内核
消息空了,则默认0为阻塞,直到有了一个消息位置,才能 rcv消息进入到进程内存
二、如果指定msgflg = MSG_NOERROR,如果函数取得的消息长度大于msgsz,将只返回msgsz 长度的信息,剩下的部分被丢弃了。如果不指定这个参数,E2BIG 将被返回,而消息则留在队列中不被取出
三、如果使用msgctl控制单个队列的大小(msg_qbytes),当设置的值小于系统设置的值时,可以生效;当设置的值大于系统设置的值时,设置会失败,此时还是原来系统设置的值