进程间通信(2)----消息队列

        进程间通信的方式有:管道、信号量、消息队列、共享内存和socket套接字
        管道、信号量、消息队列、共享内存适用于单机,即完成一台主机上的两个进程或多个进程之间的通信,socket套接字适用于网络通信,通信的进程可以运行在不同的主机上。
        无名管道:无名管道是一种半双工通信方式,数据只能单方向流动,而且只能在具有亲缘关系的进程间使用,进程的亲缘关系通常指父子进程关系。
        有名管道:有名管道也是一种半双工通信方式,适用于无亲缘关系的进程间通信。
        信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它通常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源,因此主要作为进程间以及同一进程内不同线程之间的同步手段。
        消息队列:消息队列是消息的链表,存放在内核中并有消息队列标识符标识。消息队列克服了信号传递信息量少,管道职业承载无格式字节流以及缓冲区受限等缺点。
        共享内存:共享内存是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建但是多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信。
        套接字(socket):套接字也是一种进程间通信机制,与其他进程不同的是它可以用于不同主机间的进程通信。
        本文对消息队列进行讨论。
        消息队列是一个存放在内核中的消息链表,每个消息队列由消息队列标识符标识,其发送的内容由类型和数据组成。与管道不同的是消息队列存放在内核中,只有在内核(即操作系统重启)或者显式地删除一个消息队列时,该消息队列才会被真正删除。
        进程都是相互独立的,它们之间要想通信,必须共享一些资源。每个进程都有4G的虚拟地址空间,3G的用户空间是每个进程独有的,1G的内核空间是共享的。
下面来看几个概念:
对象:类的一次实例化
类:对实体的抽象,包括成员属性和成员方法
标识符:内核对内核对象的标识,每个id都能唯一的标识一个内核对象
键:用户层次对于内核中对象的标识
操作消息队列时需要用到一些数据结构
1> 消息缓冲结构
Linux系统定义了一个模板数据结构msgbuf:

#include <linux/msg.h>
struct msgbuf
{
	long mtype;
	char mtext[1];
};

mtype:消息的类型;
mtext:消息的内容;
2> msqid_ds内核数据结构
Linux内核中,每个消息队列都维护一个结构体 msqid_ds,此结构体保存着消息队列当前的状态信息。

struct msqid_ds
{
	struct_ipc_perm msg_perm;
	struct_msg *msg_first;
	struct_msg *msg_last;
	__kernel_t time_t msg_stime;
	__kernel_t time_t msg_rtime;
	__kernel_t time_t msg_ctime;
	unsigned long msg_lcbytes;
	unsigned long msg_lqbytes;
	unsigned short msg_cbytes;
	unsigned short msg_qnum;
	unsigned short msg_qbytes;
	__kernel_ipc_pid_t msg_lspid;
	__kernel_ipc_pid_t msg_lrpid;
};

msg_perm:是一个ipc_perm的结构,保存了消息队列的存取权限、用户ID、组ID等信息。
msg_first:指向队列中的第一条消息
msg_last:指向队列中的最后一条消息
msg_stime:向消息队列发送最后一条信息的时间
msg_rtime:从消息队列取最后一条信息的时间
msg_ctime:最后一次变更消息队列的时间
msg_cbytes:消息队列中所有消息占的字节数
msg_qnum:消息队列中消息的数目
msg_qbytes:消息队列的最大字节数
msg_lspid:向消息队列发送最后一条消息的进程ID
msg_lrpid:从消息队列读取最后一条信息的进程ID
3> ipc_perm内核数据结构
结构体ipc_perm保存着消息队列的一些重要的信息,比如消息队列关联的键值,消息队列的用户ID、组ID等信息。

struct ipc_perm
{
	__kernel_key_t key;
	__kernel_uid_t uid;
	__kernel_gid_t gid;
	__kernel_uid_t cuid;
	__kernel_gid_t cgid;
	__kernel_mode_t mode;
	unsigned short seq;
}

key:创建消息队列用到的键值key
uid:消息队列的用户id
gid:消息队列的组id
cuid:创建消息队列的进程用户id
cgid:创建消息队列的进程组id
两个进程进行通信需要访问同一个内核对象,必须给一个相同的键值,通过键值来找到对应的内核对象标识符。

       例如:A进程与B进程要进行通信,A进程首先访问内核对象时,发现没有对应的内核对象,会创建一个内核对象,B进程访问时,会直接获取同一个内核对象,通过内核对象中的消息队列进行通信,内核中空间若不够的话可以利用高端内存去映射用户空间。
1、消息队列的创建
        消息队列是随着内核的存在而存在的,每个消息队列在系统范围内对应唯一的键值,要获得一个消息队列的描述符,只需提供该消息队列的键值即可,该值通常由函数fork返回。

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname,int proj_id);

        该函数执行成功会返回一个键值,失败返回-1。ftok函数返回的键值可以提供给msgget函数,msgget函数根据这个键值创建一个新的消息队列或者访问一个已存在的消息队列。

#include <sys/msg.h>
int magget(key_t key,int msgflg);

key:ftok函数的返回值
msgflg:一个标志参数,其取值有以下两种:
IPC_CREATE:如果内核中不存在键值与key相等的消息队列,则创建一个消息队列,如果存在这样的消息队列,返回该消息队列的描述符;
IPC_EXCL:和IPC_CREATE一起使用,如果对应键值的消息队列已经存在,则出错返回-1;
该函数如果调用成功返回一个消息队列的描述符,失败返回-1。
2、发送消息

#include <sys/msg.h>
int msgsnd(int misqid,struct msgbuf *msgp,size_t msgsz,int magflg);

misqid:函数向misqid标识的消息队列发送一个消息;
msgp:指向发送的消息;
msgsz:要发送的消息的大小,不包含消息类型占用的4个字节;
msgflg:操作标志位,msgflg为0表示当消息队列已满的时候,msgsnd函数将会阻塞,直到消息可以写进消息队列,msgflg为IPC_NOWAIT表示当消息队列已满的时候,msgsnd函数将不等待立即返回。
该函数如果调用成功返回0,失败返回-1。
3、接收消息

#include <sys/msg.h>
int msgrcv(int misqid,struct msgbuf *msgp,size_t msgsz,long int msgtyp,int magflg);

misqid:向misqid标识的消息队列发送一个消息;
msgp:读取的消息存到msgp指向的消息结构中;
msgsz:消息缓冲区的大小;
msgtyp:请求读取的消息类型;
msgflg:操作标志位,msgflg为PC_NOWAIT表示如果没有满足条件,调用立即返回;msgflg为IPC_EXCEPT时,IPC_EXCEPT与msgtyp配合使用,返回队列中第一个类型不为msgtyp的消息;msgflg为IPC_NOERROR表示如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分的数据将被丢弃;
该函数如果调用成功返回读出消息的实际字节数,失败返回-1。
4、设置和获取消息队列的属性
       消息队列的属性保存在系统维护的数据结构msqid_ds中,用户可以通过函数msgctl设置和获取消息队列的属性。

#include <sys/msg.h>
int msgctl(int msqid,int cmd,struct msqid_ds *buf);

cmd有以下三种操作:
IPC_STAT:获取消息队列对应的msqid_ds结构,并将其保存到buf指向的地址空间;
IPC_SET:设置消息队列的属性,要设置的属性存储在buf中,可设置的属性包括:msg_perm_uid,msg_perm_gid,msg_perm_mode和msg_qbytes;
IPC_RMID:从内核中删除msqid标识的消息队列;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值