天天看点

消息队列

努力只能及格,拼命才能优秀

消息缓冲区结构

结构msgid_ds

结构ipc_perm

键值构建ftok()函数

获得消息msgget()函数

发送消息msgsnd()函数

接收消息msgrcv()函数爱

消息控制msgctl()函数

例子

1.显示消息属性的函数msg_show_attr()

2.主函数main()

        消息队列是内核地址空间中的内部链表,通过linux内核在各个进程之间传递内容,消息顺序地发送消息队列中,并以几种不同的方式从队列中获取,每个消息队列可以用ipc标识符唯一地进行标识。内核中的消息队列是通过ipc的标识符来区别的,不同的消息队列之间是相对独立的。每个消息队列中的消息,又构成一个独立的链表。

        常用的结构是msgbuf结构。程序员可以以这个结构为模板定义自己的消息结构。在头文件<linux/msg.h>中,它的定义如下:

在结构msgbuf中有以下两个成员。

mtype:消息类型,以正数来表示。用户可以给某个消息设定一个类型,可以在消息队列中正确的发送和接收自己的消息。例如,在socket编程过程中,一个服务器可以接收多个客户端的连接,可以为每个客户端设定一个消息类型,服务器和客户端之间的通信可以通过此消息类型来发送和接收消息,并且多个客户端之间通过消息类型来区分。

mtext:消息数据

        消息数据的类型为char,长度为1。在构建自己的消息结构时,这个域并不一定要设为char或者长度为1。可以根据实际的情况进行设定,这个域能存放任意形式的任意数据,程序员可以重新定义msgbuf结构,例如:

        上面定于的消息结构与系统模板定义的不一致,但是mtype是一致的。消息在通过内核在进程之间收发时,内部不对mtext域进行转换,任意的消息都可以发送。具体的转换工作是在应用程序之间进行的。但是,消息的大小,存在一个内部的限制。在linux中,它在linux/msg.h中的定义如下:

        消息总的大小不能超过8192个字节,这其中包括ntype成员,它的长度是4个字节(long类型)

        本例在建立消息队列后,打印其属性,并在每次发送和接收后均查看其属性,最后对消息队列进行修改。

        内核msgid_ds结构——ipc对象分为3类,每一类都有一个内部数据结构,该数据结构是由内核维护的,对于消息队列而言,它的内部数据结构是msgid_ds结构,对于系统上建的每个消息队列,内核均为其创建、存储和维护该结构的一个实例。该结构在<linux/msg.h>中定义:

        内核把ipc对象的许可权限信息存放在ipc_perm类型的结构中,例如在前面描述的某个消息队列的内部结构中,msg_perm成员就是ipc_perm类型的,它的定义是在文件<linux/ipc.h>中,如下所示。

ftok()函数将路径名和项目的表示符转变为一个系统v的ipc键值。其原型如下:

其中pathname必须是已经存在的目录,而proj_id则是一个8位的值,通常用a,b等表示。例如建立如下目录:

然后用如以下代码生成一个键值:

        创建一个新的消息队列,或者访问一个现由的队列,可以使用函数msgget(),其原型如下:

        msgget()函数的第一个参数是键值,可以用ftok()函数生成,这个关键字的值将被拿来与内核中其他消息队列的现有关键字相对比。比较之后,打开或者访问操作依赖于msgflg参数的内容。

ipc_creat:如果在内核中不存在该队列,则创建它

ipc_excl:当与ipc_creat一起使用时,如果队列早已存在则将出错。

        如果只使用了ipc_creat,msgget()函数或者返回新创建消息队列的消息队列标识符,或者会返回现有的具有同一个关键字值的队列的标识符。如果同时使用了ipc_excl和ipc_creat,那么将可能会有两个结果:创建一个新的队列,如果该队列存在,则调用将出错,并返回-1。ipc_excl本身是没有什么用处的,但在与ipc_creat组合使用时,它可以用于保证没有一个现存的队列为了访问而被打开。例如,下面的代码创建一个消息队列:

        一旦获得了队列标识符,用户就可以开始在该消息队列上执行相关操作了。为了向队列传递消息,用户可以使用msgsnd()函数:

        msgsnd()函数的第一个参数是队列标识符,它是前面调用msgget()获得的返回值。第二个参数是msgp,它是一个void类型的指针,指向一个消息缓冲区。msgsz参数则包含着消息的大小,它是以字节为单位的,其中不包括消息类型的长度(4个字节长)。

        msgflg参数可以设置为0(表示忽略),也可以设置为ipc_nowait。如果消息队列已满,则消息将不会被写入到队列中。如果没有指定ipc_nowait,则调用进程将被中断(阻塞),直到可以写消息为止,例如,如下代码已经打开消息队列发送信息:

        首先将要发送的消息打包到msg_mbuf.mtext域中,然后调用msgsnd发送消息给内核。这里的mtype设置了类型为10,当接收时必须设置此域为10,才能接收到这时发送的消息。msgsnd()函数的msg_id时之前msgget创建的。

        当获得队列标识符后,用户就可以开始在该消息队列上执行消息队列的接收操作。msgrev()函数用于接收队列标识符中的消息,函数原型如下:

msgrcv()函数的第1个参数msqid是用来指定,在消息获取过程中所使用的队列(该值是由前面调用msgget()得到的返回值)

第二个参数msgp代表消息缓冲区变量的地址,获取的消息将存放在这里

第三个参数msgsz代表喜喜缓冲区结构的大小,不包括mtype成员的长度

第四个参数mtype指定要从队列中获取的消息类型。内核将查找队列中具有匹配类型的第一个到达的消息,并把它复制返回由msgp参数所指定的地址中。如果mtype参数传送一个为0的值,则将返回队列中最老的消息,不管消息的类型是什么。

        如果把ipc_nowait作为一个标志传送给该函数,而队列中没有任何消息,则该此调用将会向调用进程返回enomsg。否则,调用进程将阻塞,直到满足msgrcv()参数的消息到达队列为止。如果在客户等待消息的时候队列被删除了,则返回eidrm。如果在进程阻塞并等待消息的到来时捕获到一个信号,则返回eintr。函数msgrcv的使用代码如下:

        上面的代码中将mtype设置为10,可以获得之前发送的内核的消息获得(因为之前发送的mtype值也设置为10),msgrcv返回值为接收到的消息长度。

        为了在一个消息队列上执行控制操作,用户可以使用msgctl()函数。

        msgctl()向内核发送一个cmd命令,内核根据此来判断进行何种操作,buf为应用层和内核空间进行数据交换的指针。其中cmd可以为如下值:

ipc_stat:获得队列的msqid_ds结构,并把它存放在buf变量所指定的地址中,通过这种方式,应用层可以获得当前消息队列的设置情况,例如是否有消息到来、消息队列的缓冲区设置等。

ipc_set:设置队列的msqid_ds结构的ipc_perm成员值,它是从buf中取得该值的。通过ipc_set命令,应用层可以设置消息队列的状态,例如修改消息队列的权限,使其他用户可以访问或者不能访问当前的队列;甚至可以设置消息队列的某些当前值来伪装。

ipc_rmid:内核删除队列。使用此命令执行后,内核会把此消息队列从系统中删除。

        msg_show_attr()函数根据用户输入的消息id,将消息队列中的字节数、消息数、最大字节数、最后发送消息的进程、最后接收消息的进程、最后发送消息的时间、最后接收消息的时间、最后消息变换时间,以及消息的uid和gid等信息进行打印。

        主函数先用函数ftok()使用路径"./"获得一个键值,之后进行相关的操作并打印消息的属性。

调用函数msgget()获得一个消息后,打印消息的属性。

调用函数msgsnd()发送一个消息后,打印消息的属性。

调用函数msgrcv()接收一个消息后,打印消息的属性。

c