Linux进程间通信——消息队列
来源:互联网 发布:剑网3谢云流捏脸数据 编辑:程序博客网 时间:2024/04/30 08:21
1 消息队列基本概念
消息队列是系统内核地址空间中的一个内部的链表。消息可以按照顺序发送到队列中,也可以以几种不同的方式从队列中读取。每一个消息队列用一个唯一的IPC标识符表示。
了解在系统内核中的数据结构是了解IPC机制如何工作的最好的方法。
首先我们看一下数据结构msgbuf。此数据结构可以说是消息数据的模板。虽然此数据结构需要用户自己定义,但了解系统中有这样一个数据结构是十分重要的。在<sys/msg.h>中,此数据结构是这样定义的:
struct msgbuf{ long mtype; /*type of message, must>0*/ char mtext[1]; /*message text*/};
在数据结构msgbuf中共有两个元素:
mtype指消息的类型,它由一个整数来代表,并且它只能是大于0的整数。
mtext是消息数据本身。
mtext字段不但可以存储字符,还可以存储任何其他的数据类型。此字段可以说是完全任意的,因为程序员自己可以重新定义此数据结构。请看下面重新定义的例子:
struct my_msgbuf{ long mtype; /*Message type*/ char request_id; /*Request identifier*/ struct client info; /*Client information structure*/};
这里的消息类型字段和前面的一样,但数据结构的其余部分则由其他的两个字段所代替,而其中的一个还是另外一个结构。这就体现了消息队列的灵活之处。内核本身并不对消息结构中的数据做任何翻译。你可以在其中发送任何信息,但存在一个内部给定的消息大小的限制。在Linux系统中,消息的最大的长度是4056个字节,其中包括mtype,它占用4个字节的长度。
创建消息队列
系统调用msgget()用于创建一个新的消息队列,或者存取一个已经存在的消息队列,其函数原型是:
#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>int msgget(key_t key, int msgflg);
系统调用msgget()中的第一个参数是消息队列关键字值,可以由ftok()获得。第二个参数msgflg是一些标志,包括:
IPC_CREAT:如果内核中没有此队列,则创建它。
IPC_EXCL:当和IPC_CREAT一起使用时,如果队列已经存在,则返回错误。
当msgget()执行成功时,返回消息队列的标识符,否则返回-1,通过errno和perror()函数查看错误信息.
下面是一个打开和创建一个消息队列的例子,函数返回消息队列的标识符
int open_ queue(key_ t keyval){ int qid; if((qid = msgget(keyval,IPC_CREAT|0660)) == -1){ perror(”msgget”); return(-1); } return(qid);}
发送和接收消息
当得到了消息队列标识符,就可以在队列上执行发送或者接收消息了。msgsnd()系统调用用于向队列发送一条消息,其函数原型是:
int msgsnd(int msqid, struct msgbuf *msgp, sizet msgsz, int msgflg);
第一个参数是消息队列标识符。第二个参数msgp,是指向消息缓冲区的指针。参数msgsz指定了消息的字节大小,但不包括消息类型的长度(4个字节)。参数msgflg可以设置为:
0:此时为忽略此参数,如果消息队列已满,调用进程将会挂起,直到消息可以写入到队列中。
IPC_NOWAIT:如果消息队列己满,那么此消息则不会写入到消息队列中,控制将返回到调用进程中。消息队列写入成功时,函数返回0,否则返回-1。
下面是一个发送消息的例子:
int send_message(int qid, struct mymsgbuf *qbuf){ int result, length; /*The length is essentially the size of the structure minus sizeof(mtype)*/ length = sizeof(struct mymsgbuf) - sizeof(long); if((result = msgsnd(qid, qbuf, length, O)) == -1){ perror(”msgsnd”); return(-1); } return(result);}
msgrcv()系统调用用于从消息队列读取一条消息,其函数原型是:
ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtype, int msgflg);
第一个参数是消息队列的标识符。第二个参数代表要存储消息的缓冲区的地址。第三参数是消息缓冲区的长度,不包括mtype的长度,它可以按照如下的方法计算:
msgsz=sizeof(struct mymsgbuf)-sizeof(long);
第四个参数是要从消息队列中读取的消息的类型。
如果msgtype=0,接收消息队列的第一个消息。大于0接收队列中消息类型等于这个值的第一个消息。小于0接收消息队列中小于或者等于msgtype绝对值的所有消息中的最小一个消息。一般为0。
第五个参数msgflg取值为:
0:从队列中取出最长时间的一条消息。
IPC_NOWAIT:当队列没有消息时,调用会立即返回ENOMSG错误。否则,调用进程将会挂起,直到队列中的一条消息满足msgrcv()的参数要求。
当函数成功时,返回写入缓冲区的数据大小,否则返回-1。
下面是一个接收消息的例子:
int read_message(int qid, long type, struct mymsgbuf *qbuf){ int result, length;/* The length is essentially the size of the structure minus sizeof(mtype)*/length = sizeof(struct mymsgbuf) - sizeof(long); if((result = msgrcv(qid, qbuf, length, type, O))==-l){ perror(¨msgrcv¨); return(-1);}return(result);}
消息队列的控制
消息队列标识符的属性被记录在一个msgid_ds结构体:
struct msqid_ds{ struct ipc_perm msg_perm; /*所有者和权限*/ time_t msg_stime; /*最后一次向队列发送消息的时间*/ time_t msg_rtime; /*最后一次从队列接收消息的时间*/ time_t msg_ctime; /*队列最后一次改动的时问*/ unsigned long __msg_cbytes; /*当前队列所有消息的总长度*/ msgqnum_t msg_qnum; /*当前队列中的消息数量*/ msglen_t msg_qbytes; /*消息队列的最大消息总长度*/ pid_t msg_lspid; /*最一次给队列发送消息的进程PID*/ pid_t msg_lrpid; /*最后一次从队列接收消息的进程PID*/}
通过msgctl()可以对消息队列进行控制或者一些属性的修改,其函数原型为:
int msgctl(int msqid, int cmd, struct msqid ds *buf;
第一个参数是消息队列的标识符,第二个参数cmd指定了操作,下面是几个常用的操作:
IPC_STAT:读取消息队列的数据结构msqid_ds,并将其存储在buf指定的地址中。
IPC_SET:设置消息队列的数据结构msqid_ds中的ipc_perm、msg_qbytes、msg_ctime元素的值。这个值取自buf参数。
IPC_RMID:从系统内核中移走消息队列。
比如下面是一个删除消息队列的例子。
int remove_queue(int qid){ if(msgctl(qid, IPC_RMID, 0)==-1){ perror(”msgctl”); return(-1);} return(O);}
综合示例msgtool
我们来实现一个简单的消息队列工具,用于创建消息队列、发送、读取消息、改变权限以及删除消息队列。
它的用法如下:
(1) 发送消息
msgtool s (type)”text”
(2) 读取消息
msgtool r (type)
(3) 改变权限
msgtool l m (mode)
(4) 删除队列
msgtool d
源代码msgtoo1.c
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>#define MAX_SEND_SIZE 80struct mymsgbuf {long mtype;char mtext[MAX_SEND_SIZE];};void send_message(int qid, struct mymsgbuf *qbuf, long type, char *text);void read_message(int qid, struct mymsgbuf *qbuf, long type);void remove_queue(int qid);void change_queue_mode(int qid, char *mode);void usage(void);int main(int argc, char *argv[]){key_t key;int msgqueue_id;struct mymsgbuf qbuf;if(argc == 1)usage();/* Create unique key via call to ftok() */key = ftok(".", 'm');/* Open the queue - create if necessary */if((msgqueue_id = msgget(key, IPC_CREAT|0660)) == -1) {perror("msgget");exit(1);}printf("message queue id = [%d]\n", msgqueue_id );switch(tolower(argv[1][0])){case 's': if( argc < 4 ){usage();break;}send_message(msgqueue_id, (struct mymsgbuf *)&qbuf, atol(argv[2]), argv[3]);break;case 'r': if( argc < 3 ){usage();break;}read_message(msgqueue_id, &qbuf, atol(argv[2]));break;case 'd': remove_queue(msgqueue_id);break;case 'm': if( argc < 3 ){usage();break;}change_queue_mode(msgqueue_id, argv[2]);break;default: usage();}return(0);}void send_message(int qid, struct mymsgbuf *qbuf, long type, char *text){/* Send a message to the queue */printf("Sending a message ...\n");qbuf->mtype = type;strcpy(qbuf->mtext, text);if((msgsnd(qid, (struct msgbuf *)qbuf, strlen(qbuf->mtext)+1, 0)) ==-1){perror("msgsnd");exit(1);}}void read_message(int qid, struct mymsgbuf *qbuf, long type){/* Read a message from the queue */printf("Reading a message ...\n");qbuf->mtype = type;msgrcv(qid, (struct msgbuf *)qbuf, MAX_SEND_SIZE, type, 0);printf("Type: %ld Text: %s\n", qbuf->mtype, qbuf->mtext);}void remove_queue(int qid){/* Remove the queue */msgctl(qid, IPC_RMID, 0);}void change_queue_mode(int qid, char *mode){struct msqid_ds myqueue_ds;/* Get current info */msgctl(qid, IPC_STAT, &myqueue_ds);/* Convert and load the mode */sscanf(mode, "%ho", &myqueue_ds.msg_perm.mode);/* Update the mode */msgctl(qid, IPC_SET, &myqueue_ds);}void usage(void){fprintf(stderr, "msgtool - A utility for tinkering with msg queues\n");fprintf(stderr, "USAGE: msgtool (s)end <type> <messagetext>\n");fprintf(stderr, " (r)ecv <type>\n");fprintf(stderr, " (d)elete\n");fprintf(stderr, " (m)ode <octal mode>\n");exit(1);}
我们先来发送一条消息:
./msgtool s 1 ”Hello”message queue id = [32768]Sending a message...
用ipcs命令可以看到创建的消息队列:
ipcs -q------ Message Queues --------key msqid owner perms used-bytes messages 0x6d01c86f 32768 user 660 6 1
可以看到现在队列里有1条消息。
再用msgtool读出先读出一条消息,然后再次读取消息:
./msgtool r 0message queue id = [32768]Reading a message ...Type: 1 Text: Hello[alex@alex-/work/tutorai l]$./msgtool r 0message queue id 2[32768]Reading a message...
此时消息队列已经为空,进程被阻塞等待消息。我们到另外一个终端发送一条消息:
./msgtool s 2 “Another message”message queue id=[32768]Sending a message…
这是第一个终端的msgtool收到消息:
Type:2 Text:Another message
./msgtool m 600message queue id=[32768][alex@alex一/work/tutorail]$ ipcs –q------ Message Queues --------key msqid owner perms used-bytes messages 0x6d01c86f 32768 user 660 6 1
从ipcs命令看到的消息队列中,perms列已经被改为600。最后删除消息队列
/msgtool dmessage queue id=[32768][alex@alex一/work/tutorail]$ ipcs –q------ Message Queues --------key msqid owner perms used-bytes messages
- Linux进程通信——消息队列
- Linux 进程通信—消息队列
- 【Linux】Linux进程间通信——使用消息队列
- Linux进程间通信 ————消息队列
- Linux进程间通信——消息队列
- Linux进程间通信——消息队列
- Linux进程间通信——消息队列
- Linux 进程间通信——消息队列实现双向通信
- Linux环境进程间通信——消息队列
- Linux进程间通信——使用消息队列
- Linux进程间通信——使用消息队列
- linux进程间通信4——消息队列
- Linux环境进程间通信——消息队列
- Linux进程间通信——使用消息队列
- Linux进程间通信——消息队列
- Linux进程间通信——使用消息队列
- Linux进程间通信——使用消息队列
- Linux进程间通信——使用消息队列
- 浅析 Linux 中的时间编程和实现原理,第 1 部分: Linux 应用层的时间编程
- iPhone之Quartz 2D系列--图形上下文(2)Graphics Contexts
- libc.so.6 动态库被重命名,命令不能执行
- 5种开源协议比较
- 用户 'IIS APPPOOL\ASP.NET v4.0' 登录失败 怎么解决
- Linux进程间通信——消息队列
- git shell显示中文
- iPhone之Quartz 2D系列--路径(3)Paths
- 动态规划之最长递增子序列问题详解
- UI常用布局<二>------相对布局(RelativeLayout)
- 从IClassFactory为{...}的COM组件创建实例失败
- uva 1484 - Alice and Bob's Trip(树形dp)
- java特点、环境搭建、及hello word学习
- Django的ModelForm简单用法