您的位置:首页 > 运维架构 > Linux

LINUX下的消息队列实例解析(转)

2015-01-31 00:29 393 查看

一、消息队列的基本概念

消息队列 (也叫做报文队列)是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标识的消息队列。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: