Linux通信之消息队列

来源:互联网 发布:越狱后抹除数据白苹果 编辑:程序博客网 时间:2024/06/05 04:12

Linux通信学习继续前进,下面是部分的函数说明,我摘抄了http://www.uml.org.cn/c++/200910192.asp 的函数介绍,写的真的很详细,特别是在size_t msg_sz这里,专门指明了不包括type,由于使用同学的电脑,没有代码,后续会补上。

现在我们来讨论第三种也是最后一种System V IPV工具:消息队列。在许多方面看来,消息队列类似于有名管道,但是却没有与打开与关闭管道的复杂关联。然而,使用消息队列并没有解决我们使用有名管道所遇到的问题,例如管道上的阻塞。

消息队列提供了一种在两个不相关的进程之间传递数据的简单高效的方法。与有名管道比较起来,消息队列的优点在独立于发送与接收进程,这减少了在打开与关闭有名管道之间同步的困难。

消息队列提供了一种由一个进程向另一个进程发送块数据的方法。另外,每一个数据块被看作有一个类型,而接收进程可以独立接收具有不同类型的数据块。消息队列的好处在于我们几乎可以完全避免同步问题,并且可以通过发送消息屏蔽有名管道的问题。更好的是,我们可以使用某些紧急方式发送消息。坏处在于,与管道类似,在每一个数据块上有一个最大尺寸限制,同时在系统中所有消息队列上的块尺寸上也有一个最大尺寸限制。

尽管有这些限制,但是X/Open规范并没有定义这些限制的具体值,除了指出超过这些尺寸是某些消息队列功能失败的原因。Linux系统有两个定义,MSGMAX与MSGMNB,这分别定义单个消息与一个队列的最大尺寸。这些宏定义在其他系统上也许并不相同,甚至也许就不存在。

消息队列函数定义如下:

#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
int msgget(key_t key, int msgflg);
int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);
int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);

与信息号和共享内存一样,头文件sys/types.h与sys/ipc.h通常也是需要的。

msgget

我们可以使用msgget函数创建与访问一个消息队列:

int msgget(key_t key, int msgflg);

与其他IPC工具类似,程序必须提供一个指定一个特定消息队列的key值。特殊值IPC_PRIVATE创建一个私有队列,这在理论上只可以为当前进程所访问。与信息量和共享内存一样,在某些Linux系统上,消息队列并不是私有的。因为私有队列用处较少,因而这并不是一个严重问题。与前面一样,第二个参数,msgflg,由9个权限标记组成。要创建一个新的消息队列,由IPC_CREAT特殊位必须与其他的权限位进行或操作。设置IPC_CREAT标记与指定一个已存在的消息队列并不是错误。如果消息队列已经存在,IPC_CREAT标记只是简单的被忽略。

如果成功,msgget函数会返回一个正数作为队列标识符,如果失败则会返回-1。

msgsnd

msgsnd函数允许我们将消息添加到消息队列:

int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);

消息结构由两种方式来限定。第一,他必须小于系统限制,第二,必须以long int开始,这在接收函数中会用作一个消息类型。当我们在使用消息时,最好是以如下形式来定义我们的消息结构:

struct my_message {
long int message_type;
/* The data you wish to transfer */
}

因为message_type用于消息接收,所以我们不能简单的忽略他。我们必须定义我们自己的数据结构来包含并对其进行初始化,从而他可以包含一个可知的值。

第一个参数,msgid,是由msgget函数所返回的消息队列标识符。

第二个参数,msg_ptr,是一个指向要发送消息的指针,正如前面所描述的,这个消息必须以long int类型开始。

第三个参数,msg_sz,是由msg_ptr所指向的消息的尺寸。这个尺寸必须不包含long int消息类型。

第四个参数,msgflg,控制如果当前消息队列已满或是达到了队列消息的系统限制时如何处理。如果msgflg标记设置了IPC_NOWAIT,函数就会立即返回而不发送消息,并且返回值为-1。如果msgflg标记清除了IPC_NOWAIT标记,发送进程就会被挂起,等待队列中有可用的空间。

如果成功,函数会返回0,如果失败,则会返回-1。如果调用成功,系统就会复杂一份消息数据并将其放入消息队列中。

msgrcv

msgrcv函数由一个消息队列中收取消息:

int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);

第一个参数,msqid,是由msgget函数所返回的消息队列标记符。

第二个参数,msg_ptr,是一个指向将要接收消息的指针,正如在msgsnd函数中所描述的,这个消息必须以long int类型开始。

第三个参数,msg_sz,是由msg_ptr所指向的消息的尺寸,并不包含long int消息类型。

第四个参数,msgtype,是一个long int类型,允许一个接收优先级形式的实现。如果msgtype的值为0,队列中第一个可用的消息就会被接收。如果其值大于0,具有相同消息类型的第一个消息就会被接收。如果其值小于0,第一个具有相同类型或是小于msgtype绝对值的消息就会被接收。

这听起来要比实际操作复杂得多。如果我们只是简单的希望以其发送的顺序来接收消息,我们可以将msgtype设置为0。如果我们希望接收特殊消息类型的消息,我们可以将msgtype设置为等于这个值。如果我们希望接收消息类型为n或是小于n的值,我们可以将msgtype设置为-n。

第五个参数,msgflg,控制当没有合适类型的消息正在等待被接收时如何处理。如果在msgflg中设置了IPC_NOWAIT位,调用就会立即返回,而返回值为-1。如果msgflg标记中消除了IPC_NOWAIT位,进程就会被挂起,等待一个合适类型的消息到来。

如果成功,msgrcv会返回放入接收缓冲区中的字节数,消息会被拷贝到由msg_ptr所指向的用户分配缓冲区中,而数据就会由消息队列中删除。如果失败则会返回-1。

msgctl

最后一个消息队列函数是msgctl,这与共享内存中的控制函数十分类型。

int msgctl(int msqid, int command, struct msqid_ds *buf);

msqid_ds结构至少包含下列成员:

struct msqid_ds {
uid_t msg_perm.uid;
uid_t msg_perm.gid
mode_t msg_perm.mode;
}

第一个参数,msqid,是由msgget函数所返回的标记符。

第二个参数,command,是要执行的动作。他可以取下面三个值:

命令 描述
IPC_STAT 设置msqid_ds结构中的数据来反射与消息队列相关联的值。
IPC_SET 如果进程有权限这样做,这个命令会设置与msqid_ds数据结构中所提供的消息队列相关联的值。
IPC_RMID 删除消息队列。

如果成功则会返回0,如果失败则会返回-1。当进程正在msgsnd或是msgrcv函数中等待时如果消息队列被删除,发送或接收函数就会失败。

说好的代码如下:

#include<cstdlib>#include<sys/types.h>#include<ctype.h>#include<sys/ipc.h>#include<sys/msg.h>#include<sys/wait.h>using namespace std;struct mymsgbuf{        long mtype;        char mtext[80];};int main(){        pid_t pid;        key_t key;        struct mymsgbuf buf;        key=ftok(".",'f');        int msg_id=msgget(key,IPC_CREAT|0660);        if(msg_id==-1)                cout<<"error"<<endl;        cout<<"this  is  father\n";        cout<<"snd msg.......\n";        buf.mtype=1;        strcpy(buf.mtext,"hello world");        msgsnd(msg_id,(struct msgbuf*)(&buf),81,0);        cout<<"hello";        sleep(2);        cout<<"rcv msg .....\n";        struct mymsgbuf child_buf;        msgrcv(msg_id,(struct msgbuf *)(&child_buf),80,1,0);        cout<<"hello"<<child_buf.mtext<<endl;        return 0;}

上面的代码比较简单,主要为了体现是可以通信的以及简单的函数使用。下面再来一个功能比较强大的。

使用例子:

msgtool s 1 test
msgtool s 5 test
msgtool s 1 "This is a test"
msgtool r 1
msgtool d
msgtool m 660

代码如下:

#include <stdio.h>#include <stdlib.h>#include <ctype.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>#include <string.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 <type> <messagetext>\n");        fprintf(stderr, " (r)ecv <type>\n");        fprintf(stderr, " (d)elete\n");        fprintf(stderr, " (m)ode <octal mode>\n");        exit(1);}