一.消息队列
前面提到的进程间通信的一种最基本的方式就是管道,而现在来谈一下另一种进程间的通信方式——消息队列。消息队列是从一个进程向另一个进程发送数据块的方式,每个数据块都有其类型,接收者接收的数据块也可以有不同的类型,这样我们就可以通过发送消息的方式来避免命名管道的同步和阻塞问题。
消息队列不同于管道的是,管道是基于字节流的,而消息队列是基于消息的,而且消息队列的读取方式不一定是先进先出的,消息队列是用链表实现的;但相同的是,它们有一样的不足,就是都有固定的大小,每个消息的大小都有上限(msgmax),每个队列的大小也有上限(msgmnb),而系统中队列的个数同样有上限(msgmni)。
二.IPC对象数据结构
内核为每个IPC对象都维护了一个数据结构(/usr/include/linux/ipc.h):
三.消息队列结构
(usr/include/linux/msg.h)
可以看到消息队列的结构中第一个就是IPC结构体,这是每个IPC对象都有的,剩下的才是各自所私有的成员。
四.关于消息队列的函数
- 创建一个新的消息队列或者获得一个已有的消息队列
函数参数中,key可以认为是一个端口号,用函数ftok生成
函数ftok把一个已经存在的路径名和一个整数标识转换成一个key_t值,这里的proj_id只有其低八位被使用。
而msgget函数参数中第二个msgflg是消息队列的标志,IPC_CREAT和IPC_EXCL,其中当IPC_CREAT单独使用时,如果共享内存已存在就返回已存在的操作符,若不存在就会新建一个共享内存的额操作符,而当IPC_CREAT和IPC_EXCL一起使用时,当共享内存不存在就会新建一个,若存在就会返回一个错误值-1。也就是说IPC_EXCL单独使用并没有什么意义,只有和IPC_CREAT一起使用可以保证所得到的对象是新建的而不是打开已有的对象。
2. 消息队列的发送/接收消息
在函数参数中,
msqid表示消息队列的标识码;
msgp是指向消息队列的一个缓冲区,用来暂时存储发送和接收的消息,它是一个用户可定义的结构体,如下:
msgsz则表示消息的大小;
msgtype就是指从消息队列内读取的消息形态,也就是指要读取哪一方的数据,若值为零,则表示消息队列中的所有消息都会被读取;
msgflg用来指明当队列中没有数据的情况下应采取的行动。如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时若消息队列已满,则magsnd()将不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列为空时,不等待而是直接返回-1,并设定错误码为ENOMSG,当msgflg为0时,则不论msgsnd()执行时队列已满还是msgrcv()执行时队列为空,一律采取阻塞等待的处理模式。
3. 消息队列属性
函数参数中同样msqid表示消息队列的标识码,
cmd表示对msqid标识的消息队列执行以下三种cmd操作:
IPC_STAT,该命令用来获取消息队列对应的msqid_ds数据结构,并将其保存到buf指定的地址空间;
IPC_SET,该命令用来设置消息队列的属性,要设置的属性存储在buf中;
IPC_RMID,该命令从内核中删除msqid标识的消息队列。
下面举个栗子用一下上面的函数:
首先当然是要创建出两个进程了,比如有一端msg_client和另一端msg_server,将要使用的函数封装到同一个头文件comm.h内,同时在comm.c中实现:
为了区分出消息队列是新创建的还是返回已存在的,可以分为两个获取消息队列的函数,使用creat_msg()时,因为IPC_CREAT和IPC_EXCL是一起使用的,可以确保返回的消息队列是新创建的,而使用get_msg()函数就不一定了;
在两个进程中,都用get_msg()函数来获取消息队列,一个先接收消息另一个先发送消息,这样来回就完成了进程间的通信。
《完》