6.3 消息队列 message queue

来源:互联网 发布:慈云蚕丝被 知乎 编辑:程序博客网 时间:2024/05/05 11:29

6.3 消息队列 message queue

6.3.1 消息队列

一个或多个进程可向消息队列写入消息,而一个或多个进程可从消息队列中读取消息,这种进程间通讯机制通常使用在客户/服务器模型中,客户向服务器发送请求消息,服务器读取消息并执行相应请求。在许多微内核结构的操作系统中,内核和各组件之间的基本通讯方式就是消息队列。例如,在 MINIX 操作系统中,内核、I/O 任务、服务器进程和用户进程之间就是通过消息队列实现通讯的。

Linux中的消息可以被描述成在内核地址空间的一个内部链表,每一个消息队列由一个IPC的标识号唯一的标识。Linux 为系统中所有的消息队列维护一个 msgque 链表,该链表中的每个指针指向一个 msgid_ds 结构,该结构完整描述一个消息队列。

6.3.2数据结构

6.3.2.1消息缓冲区(msgbuf)

我们在这里要介绍的第一个数据结构是msgbuf结构,可以把这个特殊的数据结构看成一个存放消息数据的模板,它在include/linux/msg.h中声明,描述如下:

/* msgsnd 和msgrcv 系统调用使用的消息缓冲区*/

struct msgbuf

{

    long mtype;        /* 消息的类型,必须为正数 */

    char mtext[1];     /* 消息正文 */

};

注意:对于消息数据元素(mtext),不要受其描述的限制。实际上,这个域(mtext)不仅能保存字符数组,而且能保存任何形式的任何数据。这个域本身是任意的,因为这个结构本身可以由应用程序员重新定义:

struct my_msgbuf

{

        long    mtype;          /* 消息类型 */

        long    request_id;     /* 请求识别号 */

        struct  client info;    /* 客户消息结构 */

};

我们看到,消息的类型还是和前面一样,但是结构的剩余部分由两个其它的元素代替,而且有一个是结构。这就是消息队列的优美之处,内核根本不管传送的是什么样的数据,任何信息都可以传送。

但是,消息的长度还是有限制的,在Linux中,给定消息的最大长度在include/linux/msg.h中定义如下:

#define MSGMAX  8192    /* max size of message (bytes) */

消息总的长度不能超过8192字节,包括mtype域,它是4字节长。

6.3.2.2消息结构(msg)

内核把每一条消息存储在以msg结构为框架的队列中,它在include/ linux/msg.h中定义如下:

struct msg {

    struct msg *msg_next;   /* 队列上的下一条消息 */

    long  msg_type;          /*消息类型*/

    char *msg_spot;         /* 消息正文的地址 */

    short msg_ts;           /* 消息正文的大小 */

};

注意:msg_next是指向下一条消息的指针,它们在内核地址空间形成一个单链表。

6.3.2.3消息队列结构(msgid_ds)

当在系统中创建每一个消息队列时,内核创建、存储及维护这个结构的一个实例。

/* 在系统中的每一个消息队列对应一个msqid_ds 结构 */

struct msqid_ds {

    struct ipc_perm msg_perm;

    struct msg *msg_first;    /* 队列上第一条消息,即链表头*/

    struct msg *msg_last;    /* 队列中的最后一条消息,即链表尾 */

    time_t msg_stime;        /* 发送给队列的最后一条消息的时间 */

    time_t msg_rtime;            /* 从消息队列接收到的最后一条消息的时间 */

    time_t msg_ctime;             /* 最后修改队列的时间*/

    ushort msg_cbytes;          /*队列上所有消息总的字节数 */

    ushort msg_qnum;          /*在当前队列上消息的个数 */

    ushort msg_qbytes;        /* 队列最大的字节数 */

    ushort msg_lspid;           /* 发送最后一条消息的进程的pid */

    ushort msg_lrpid;           /* 接收最后一条消息的进程的pid */

};

6.3.3 msgget()创建消息队列

为了创建一个新的消息队列,或存取一个已经存在的队列,要使用msgget()系统调用。    

表头文件:#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

 

定义函数:int msgget ( key_t key, int msgflg );                                           

函数说明:第一个参数 key 是消息队列的关键字值,可由 ftok() 函数获得。        第二个参数 msgflg 是一些标志,包括:                        IPC_CREAT : 如果这个队列在内核中不存在,则创建它。         IPC_EXCL :当与IPC_CREAT一起使用时,如果这个队列已存在,则返回错误。

返回值:成功,返回消息队列的例子;失败,则返回-1。

例子:

int main(int argc,char **argv)

{

int msgid;

msgid = msgget( 888, 0666|IPC_CREAT);

if( -1 == msgid )

{

perror( "msgget error" );

return -1;

}

return 0;

}

6.3.4 msgsnd() 发送消息

一旦我们有了队列识别号,我们就可以在这个队列上执行操作。要把一条消息传递给一个队列,你必须用msgsnd()系统调用。

定义函数int msgsnd ( int msqid , struct msgbuf *msgp , int msgsz , int msgflg );

函数说明:msqid 第一个参数是消息队列标识符。

msgp 第二个参数是一个指针,指向我们重新声明和装载的消息缓冲区。是一个数据结构

struct msgbuf

{

    long mtype;         /* 消息的类型,必须为正数 */

    char mtext[N];      /* 消息正文 */

};

msgsz 第三个参数指定了消息的字节大小,但不包括消息类型的长度( 4 个字节)。

msgflg 第四个参数可以设置成: 0:此时忽略此参数,如果消息队列已满,调用进程将会挂起,直到消息可以写入到队列中。或者:IPC_NOWAIT :如果消息队列满,消息不写到队列中,并且控制权返回给调用进程(继续执行)。如果不指定IPC_NOWAIT,调用进程将挂起(阻塞)直到消息被写到队列中。

返回值:成功返回 0 ,失败返回 -1。

例子

#define N 512

struct msgbuf

{

    long mtype;//消息类型

    char mtext[N];//消息正文

};

struct msgbuf msgbuf;

msgbuf.mtype = 1L;

strcpy(msgbuf.mtext,"hello,msg!");

if( msgsnd(msgid,&msgbuf,N,0) < 0  ) //发送消息 

 {  

        perror("msgsnd error");

        return -1;

 }

 

6.3.5msgrcv() 接收消息

定义函数ssize_t msgrcv ( int msqid, struct msgbuf *msgp, int msgsz, long mtype, int msgflg );

返回值:成功,则为拷贝到消息缓冲区的字节数,失败为-1。

函数说明msqid 第一个参数是消息队列的标识符。

msgp 第二个参数是存放消息的缓冲区的地址,也是一个数据结构类型。

msgsz 第三个参数是消息缓冲区的大小,不包括消息类型mtype的长度(4字节)。msgsz = sizeof( struct mymsgbuf ) - sizeof( long );

msgtype 第四个参数是要从消息队列中读取的消息的类型。0 :接收消息队列中第一个消息。大于 0 :接受消息队列中第一个类型为msgtype 的消息。 小于 0:接受消息队列中类型值小于mtype 的绝对值且类型值又最小的消息。

          Msgflg  取值为 :0 从队列中取出最长时间的一条消息。IPC—NOWAIT 当队列没有消息时,调用会立即返回ENOMSG错误。否则,调用进程将会挂起,直到队列中一条消息满足msgtcv()的参数要求。

让我们来看一个从我们已建的消息队列中检索消息的例子

    memset( &msgbuf,0,sizeof(msgbuf) );

    if( msgrcv(msgid,&msgbuf,N,1L,0 ) < 0 )//读取或接收消息

    {

        perror("msgrcv error");

        return -1;

    }

    printf("msgrcv :%s \n",msgbuf.mtext);

6.3.6 msgctl() 消息队列的控制

  定义函数 :int msgctl ( int msgid , int cmd , struct msgid ds *buf );

函数说明 : msgid 第一个参数是消息队列的标识符。

            cmd 第二个参数指定了的操作:IPC_STAT:读取消息队列的属性,并将其保存在buf 指向的缓冲区中。IPC_SET :设置消息队列的属性。IPC_RMID:从系统中删除消息队列。

  例如:

msgctl(msgid,IPC_RMID,0);

 

例子:

#include <sys/ipc.h>

#include <sys/types.h>

#include <sys/msg.h>

#include <stdio.h>

#include  <string.h>

#define KEY 888

#define PERMS 0666|IPC_CREAT

#define N 512

#define M 512

struct msgbuf

{

    long mtype;//消息类型

    char mtext[N];//消息正文

    char text[M];

};

int main()

{

    int msgid;

    struct msgbuf msgbuf;

    msgid = msgget(KEY,PERMS);//创建消息队列,成功返回队列ID 号。 ipcs -q 查看

    if( -1 == msgid )

    {

        perror("msgget error");

        return -1;

    }

    printf("msgid is %d\n",msgid);

    msgbuf.mtype = 1L;

    strcpy(msgbuf.mtext,"hello,msg!");

    if(msgsnd(msgid,&msgbuf,N,0) < 0 )//发送消息 

    {

        perror("msgsnd error");

        return -1;

    }

    memset(&msgbuf,0,sizeof(msgbuf));

    if(msgrcv(msgid,&msgbuf,N+M,1L,0) < 0 )//读取或接收消息

    {

        perror("msgrcv error");

        return -1;

    }

    printf("msgrcv :%s \n",msgbuf.mtext);

//  msgctl(msgid,IPC_RMID,0);

    return 0;

}

0 0
原创粉丝点击