LINUX消息队列编程(整理融合版)

msgsnd/msgrcv系统调用功能描述msgrcv()可以从消息队列中读取消息,msgsnd()将一个新的消息写入队列。在消息队列上进行收发消息。为了发送消息,调用进程对消息队列进行写入时必须有写权能。接收消息时必须有读权能。用法#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>函数原型int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);参数msqid:消息队列的识别码。msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下struct msgbuf {long mtype; /* 消息类型,必须 > 0 */char mtext[1]; /* 消息文本 */};msgsz:消息的大小。msgtyp:消息类型msgtyp等于0 则返回队列的最早的一个消息。msgtyp大于0,则返回其类型为mtype的第一个消息。msgtyp小于0,则返回其类型小于或等于mtype参数的绝对值的最小的一个消息。msgflg:这个参数依然是是控制函数行为的标志,取值可以是:0,表示忽略;IPC_NOWAIT,如果消息队列为空,则返回一个ENOMSG,并将控制权交回调用函数的进程。如果不指定这个参数,那么进程将被阻塞直到函数可以从队列中得到符合条件的消息为止。如果一个client 正在等待消息的时候队列被删除,EIDRM 就会被返回。如果进程在阻塞等待过程中收到了系统的中断信号,EINTR 就会被返回。MSG_NOERROR,如果函数取得的消息长度大于msgsz,将只返回msgsz 长度的信息,剩下的部分被丢弃了。如果不指定这个参数,E2BIG 将被返回,而消息则留在队列中不被取出。当消息从队列内取出后,相应的消息就从队列中删除了。返回说明成功执行时,msgsnd()返回0,msgrcv()返回拷贝到mtext数组的实际字节数。失败两者都返回-1,errno被设为以下的某个值[对于msgsnd]EACCES:调用进程在消息队列上没有写权能,同时没有CAP_IPC_OWNER权能EAGAIN:由于消息队列的msg_qbytes的限制和msgflg中指定IPC_NOWAIT标志,消息不能被发送EFAULT:msgp指针指向的内存空间不可访问EIDRM:消息队列已被删除EINTR:等待消息队列空间可用时被信号中断EINVAL:参数无效ENOMEM:系统内存不足,无法将msgp指向的消息拷贝进来[对于msgrcv]E2BIG:消息文本长度大于msgsz,并且msgflg中没有指定MSG_NOERROREACCES:调用进程没有读权能,同时没具有CAP_IPC_OWNER权能EAGAIN:消息队列为空,并且msgflg中没有指定IPC_NOWAITEFAULT:msgp指向的空间不可访问EIDRM:当进程睡眠等待接收消息时,消息已被删除EINTR:当进程睡眠等待接收消息时,被信号中断EINVAL:参数无效ENOMSG:msgflg中指定了IPC_NOWAIT,同时所请求类型的消息不存在

http://www.wanglong-neu.cn/?p=37 一、消息队列的基本概念

消息队列(也叫做报文队列)是Unix系统V版本中3种进程间通信机制之一。另外两种是信号灯和共享内存。这些IPC机制使用共同的授权方法。只有通过系统调用将标志符传递给核心之后,进程才能存取这些资源。这种系统IPC对象使用的控制方法和文件系统非常类似。使用对象的引用标志符作为资源表中的索引。

消息队列就是一个消息的链表。就是把消息看作一个记录,并且这个记录具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读出消息。

Linux采用消息队列的方式来实现消息传递。这种消息的发送方式是:发送方不必等待接收方检查它所收到的消息就可以继续工作下去,而接收方如果没有收到消息也不需等待。这种通信机制相对简单,但是应用程序使用起来就需要使用相对复杂的方式来应付了。新的消息总是放在队列的末尾,接收的时候并不总是从头来接收,可以从中间来接收。

消息队列是随内核持续的并和进程相关,只有在内核重起或者显示删除一个消息队列时,该消息队列才会真正被删除。因此系统中记录消息队列的数据结构(structipc_idsmsg_ids)位于内核中,系统中的所有消息队列都可以在结构msg_ids中中找到访问入口。

IPC标识符:每一个IPC目标都有一个唯一的IPC标识符。这里所指的IPC目标是指一个单独的消息队列、一个信号量集或者一个共享的内存段。系统内核使用此标识符在系统内核中指明IPC目标。

IPC关键字:想要获得唯一的标识符,则必须使用一个IPC关键字。客户端进程和服务器端进程必须双方都同意此关键字。这是建立一个客户机/服务器框架的第一步。在SystemVIPC机制中,建立两端联系的路由方法是和IPC关键字直接相关的。通过在应用程序中设置关键字值,每一次使用的关键字都可以是相同的。一般情况下,可以使用ftok()函数为客户端和服务器端产生关键字值。

二、ipcs命令

命令ipcs用于读取SystemVIPC目标的状态。ipcs-q:只显示消息队列。ipcs-s:只显示信号量。ipcs-m:只显示共享内存。ipcs–help:其他的参数。

下面是ipcs命令输出的例子:

[root@wanglongwanglong]#ipcs

——SharedMemorySegments——–

keyshmidownerpermsbytesnattchstatus

0×000000000root644402

0×0000000032769root644163842

0×0000000065538root6442682

——SemaphoreArrays——–

keysemidownerpermsnsems

0×000000a70root6001

0×0000000098305apache6001

0×0000000065538apache6001

0×00000000131075apache6001

0×00000000163844apache6001

0×00000000196613apache6001

0×00000000229382apache6001

0×00000000262151apache6001

0×00000000294920apache6001

——MessageQueues——–

keymsqidownerpermsused-bytesmessages

三、消息队列的主要调用

内核中实现消息传递机制的代码基本上都在文件ipc/msg.c中,消息队列的主要调用有下面4个,这里只作简单介绍:

(1)msgget:调用者提供一个消息队列的键标(用于表示个消息队列的唯一名字),当这个消息队列存在的时候,这个消息调用负责返回这个队列的标识号;如果这个队列不存在,就创建一个消息队列,然后返回这个消息队列的标识号,主要由sys_msgget执行。

(2)msgsnd:向一个消息队列发送一个消息,主要由sys_msgsnd执行。

(3)msgrcv:从一个消息队列中收到一个消息,主要由sys_msgrcv执行。

(4)msgctl:在消息队列上执行指定的操作。根据参数的不同和权限的不同,可以执行检索、删除等的操作,主要由sys_msgctl执行。

四、消息队列的应用例子

下面的例子很好的演示了创建、发送、读取、改变权限以及删除消息队列各种操作:

#include <stdio.h>#include <stdlib.h>#include <ctype.h>#include <string.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);}switch(tolower(argv[1][0])){case 's': send_message(msgqueue_id, (struct mymsgbuf *)&qbuf,atol(argv[2]), argv[3]);break;case 'r': read_message(msgqueue_id, &qbuf, atol(argv[2]));break;case 'd': remove_queue(msgqueue_id);break;case 'm': 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, "nUSAGE: msgtool (s)end \n");fprintf(stderr, " (r)ecv \n");fprintf(stderr, " (d)elete \n");fprintf(stderr, " (m)ode \n");exit(1);}

程序保存为ipcs.c

编译:gcc-oipcsipcs.c

程序运行结果解释:

[root@wanglongwanglong]#./ipcss001hello!

Sendingamessage…

[root@wanglongwanglong]#./ipcss001world!

Sendingamessage…

[root@wanglongwanglong]#./ipcss001you

Sendingamessage…

[root@wanglongwanglong]#./ipcss001and

Sendingamessage…

[root@wanglongwanglong]#./ipcss001me!

Sendingamessage…

[root@wanglongwanglong]#./ipcsr001

Readingamessage…

Type:001Text:hello!

[root@wanglongwanglong]#./ipcsr001

Readingamessage…

Type:001Text:world!

[root@wanglongwanglong]#./ipcsr001

Readingamessage…

Type:001Text:you

[root@wanglongwanglong]#./ipcsd001/*删除了消息队列001*/

[root@wanglongwanglong]#./ipcsr001

Readingamessage…/*因为删除了,所以读不出消息了*/

整理发送直接可以拷贝加入工程调用的代码

#ifndef IPCQUEUE_HPP_#define IPCQUEUE_HPP_#include <stdio.h>#include <stdlib.h>#include <ctype.h>#include <string.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>#define MAX_SEND_SIZE 500struct mymsgbuf{long mtype;char mbuf[MAX_SEND_SIZE];};int ipc_initqueue(int unique_queue_id);void ipc_send_message(int qid, long type, char *msgbuf,int Len);void ipc_read_message(int qid, struct mymsgbuf *qbuf, long type);void ipc_remove_queue(int qid);void ipc_change_queue_mode(int qid, char *mode);#endif /* IPCQUEUE_HPP_ */#include <IPCQueue.hpp>int ipc_initqueue(int unique_queue_id){    key_t key;    int msgqueue_id;    /* Create unique key via call to ftok() */    key = ftok(".", unique_queue_id);    /* Open the queue - create if necessary */    if((msgqueue_id = msgget(key, IPC_CREAT|0660)) == -1)    {printf("msgget error \n");exit(1);}    else{    return msgqueue_id;    }}void ipc_send_message(int qid, long type, char *msgbuf,int Len){    /* Send a message to the queue */    printf("Sending a message \n");    struct mymsgbuf msgbuffer;    struct mymsgbuf *qbuf=&msgbuffer ;    qbuf->mtype = type;    memcpy(qbuf->mbuf, msgbuf,Len);    if((msgsnd(qid, (struct msgbuf *)qbuf,    Len, 0)) ==-1)    {        printf("msgsnd error\n");    }    else {    printf("msgsnd done\n");    }}void ipc_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->mbuf);}void ipc_remove_queue(int qid){    /* Remove the queue */    msgctl(qid, IPC_RMID, 0);}void ipc_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);}

其实你已经错过了旅行的意义。

LINUX消息队列编程(整理融合版)

相关文章:

你感兴趣的文章:

标签云: