LINUX下的消息队列实例解析(转)
转载自:http://blog.csdn.net/primeprime/article/details/43323891
一、消息队列的基本概念
消息队列 (也叫做报文队列)是Unix系统V版本中进程间通信机制之一。消息队列就是一个消息的链表。就是把消息看作一个记录,并且这个记录具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读出消息。Linux采用消息队列的方式来实现消息传递。这种消息的发送方式是:发送方不必等待接收方检查它所收到的消息就可以继续工作下去,而接收方如果没有收到消息也不需等待。新的消息总是放在队列的末尾,接收的时候并不总是从头来接收,可以从中间来接收。消息队列是随内核持续的并和进程相关,只有在内核重起或者显示删除一个消息队列时,该消息队列才会真正被删除。因此系统中记录消息队列的数据结构 (struct ipc_ids msg_ids)位于内核中,系统中的所有消息队列都可以在结构msg_ids中中找到访问入口。IPC标识符:每一个I P C目标都有一个唯一的I P C标识符。这里所指的I P C目标是指一个单独的消息队列、一个信号量集或者一个共享的内存段。系统内核使用此标识符在系统内核中指明 I P C目标。IPC关键字:想要获得唯一的标识符,则必须使用一个 I P C关键字。客户端进程和服务器端进程必须双方都同意此关键字。这是建立一个客户机/服务器框架的第一步。一般情况下,可以使用ftok ( )函数为客户端和服务器端产生关键字值。
#include <sys/types.h>
#include<sys/ipc.h>
key_t ftok( const char* fname, int id )
fname就是你指定的文件名(已经存在的文件名),一般使用当前目录,如: key_t key;
key =ftok(".", 1);
这样就是将fname设为当前目录。id是子序号在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。查询文件索引节点号的方法是: ls -i当删除重建文件后,索引节点号由操作系统根据当时文件系统的使用情况分配,因此与原来不同,所以得到的索引节点号也不同。 如果要确保key_t值不变,要么确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t值,比如:
#define IPCKEY 0x111
char path[256];
sprintf(path, "%s/etc/config.ini", (char*)getenv("HOME") ); msgid=ftok(path, IPCKEY );
也就是说其实ftok的作用就是根据你所指定的文件的索引点号生成一个独一无二的KEY,并保持。不过在使用的过程中,一般都是直接指定一个固定的值,这样使用起来简单,但一定要确保和其它程序不冲突。
二、使用消息队列
查看一个系统中的消息可以用ipcs –q来查看,这可以看到msgid和权限以及当前有几条消息。当你在调试的过程中,由于程序没有正常结束,而你又想删除此消息时,请使用ipcrm –q msgid命令。
下面结合具体的实例代码详细说明一下使用的过程。
Snd.c文件
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/msg.h>
#include <sys/stat.h>
#define MAX_TEXT 512
#define MSG_KEY 335
struct my_msg_st
{
long my_msg_type;//这个就是消息的类型,在接收的时候一定要指定这个
//类型才会接收到相应的消息。
char some_text[MAX_TEXT];
};
int main()
{
int i = 10;
int running = 1;
struct my_msg_st some_data;
int msgid;
char buffer[BUFSIZ];
msgid = msgget((key_t)MSG_KEY, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR);//首先创建,如果不成 功,就取得
if(msgid==-1)
{
printf("create faile get imsgid\n");
msgid=msgget((key_t)MSG_KEY,IPC_EXCL|S_IRUSR|S_IWUSR);
}
if (msgid == -1)
{
fprintf(stderr, "msgget failed with error: %d\n",errno);
exit(EXIT_FAILURE);
}
while(running)
{
printf("[%d]Enter some text: less than%d\n",msgid,MAX_TEXT);
fgets(buffer, BUFSIZ, stdin);
some_data.my_msg_type = 10;
strcpy(some_data.some_text, buffer);
if (msgsnd(msgid, (void*)&some_data, sizeof(some_data), 0) == -1)
{
fprintf(stderr, "msgsnd failed\n");
exit(EXIT_FAILURE);
}
if(strncmp(buffer,"end", 3) == 0)
{
running = 0;
}
}
exit(EXIT_SUCCESS);
}
Rcv.c文件
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
#define MSG_KEY 335
struct my_msg_st
{
long my_msg_type;
char some_text[MAX_TEXT];
};
int main()
{
int running = 1;
int msgid;
struct my_msg_st some_data;
long int msg_to_receive = 10;
msgid =msgget((key_t)MSG_KEY, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR);
if(msgid==-1)
msgid=msgget((key_t)MSG_KEY,IPC_EXCL|S_IRUSR|S_IWUSR);
if (msgid == -1)
{
fprintf(stderr,"msggetfailed with error: %d\n",errno);
exit(EXIT_FAILURE);
}
printf("prepare receivemsg:%d\n",msgid);
while(running)
{
//这里的msg_to_receive指定了接收消息的类型,如果类型不对,将取不到消息.IPC_NOWAIT 果消息队列里没有消息,则马上返回并设置errno=ENOMSG。如果指定为0则将等待消息的到来。
if (msgrcv(msgid,(void*)&some_data,sizeof(some_data),msg_to_receive,IPC_NOWAIT) == -1)
{
if(ENOMSG==errno)
{
printf("no Msg receive\n");
continue;
}
fprintf(stderr,"msgrcv failed with error: %d\n", errno);
exit(EXIT_FAILURE);
}
printf("You wrote:%s", some_data.some_text);
if (strncmp(some_data.some_text, "end", 3) == 0)
{
running = 0;
}
}
if (msgctl(msgid, IPC_RMID, 0) == -1) //使用这个库函数来移除消息队列。
{
fprintf(stderr,"msgctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);.
}
三、消息队列的相关参数
1.查看系统中默认的消息队列的相关参数
Ipcs –l可以查看当前消息队列的上限限制。
------Messages: Limits --------
max queuessystem wide = 16 2G
max size ofmessage (bytes) = 65536 2G
default maxsize of queue (bytes) = 65536 2G
msgmni最大消息队列数
msgmax最大消息长度(字节数)
msgmnb消息队列中的最大字节数
其实我们要关心的参数主要有三个,说得通俗点就是最多可以有多少个队列使用,每个队列里可以放多少消息,每个消息最多可以带多少内容。
现在解释如下:系统中默认的消息列数是16,最大可以达到2G,系统中消息的个数和消息长度的总数,受队列最大尺寸限制。Msgmnb= msgmax*最大个数.
其中消息队列的长度受结构使用。
structmy_msg_st
{
long my_msg_type;
char some_text[MAX_TEXT];
};
大家如果有兴趣,可以自己写个程序测试试下,简单的很就是无限制分配就可以了。
修改消息队列的参数
1.永久修改
root用户下修改/etc/sysctl.conf文件。
2.临时修改
root用户下sysctl-w kernel.msgmnb= 1048576
/proc/sys/kernel/msgmax 单个消息的最大值
/proc/sys/kernel/msgmnb 单个消息体的容量的最大值
/proc/sys/kernel/msgmni 消息体的数量缺省值为 16
cat/proc/sys/kernel/shmmax
可通过下面的方式进行设置
echo819200 > /proc/sys/kernel/msgmax
echo1638400 > /proc/sys/kernel/msgmnb
echo1600 > /proc/sys/kernel/msgmni
cd /proc/sys/kernel ; cat msgmax;cat msgmnb;cat msgmni;
在程序中可以通过
msgctl(msgid,IPC_STAT,&mymsq_ds);
printf("%d,%d,%d\n",mymsq_ds.msg_qbytes,mymsq_ds.msg_qnum,mymsq_ds.msg_cbytes);来获得队列消息的总大小,已经有的消息数和所占空间数。
1.IPC_STAT:该命令用来获取消息队列信息,返回的信息存贮在buf指向的msqid结构中;
2.IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid结构中;可设置属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同时,也影响msg_ctime成员。
3.IPC_RMID:删除msqid标识的消息队列。
-
顶
- 0
-
踩
- 0
- 上一篇linux消息队列函数(转)
- 下一篇网络编程原始套接字(转)