【Linux/OS/Network】XSI IPC(消息队列,信号量,共享内存)

来源:互联网 发布:wifi信号搜索软件 编辑:程序博客网 时间:2024/05/06 15:47

XSI IPC 包括消息队列、信号量以及共享内存,他们都依托标识符和键来实现的,这就像是管道靠文件描述符来实现一样


IPC主题一:消息队列

一、什么是消息队列

消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。我们可以通过发送消息来避免命名管道的同步和阻塞问题。消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的,且消息队列的读取不一定是先入先出。消息队列与命名管道有一样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。

这里写图片描述

二、IPC对象数据结构

内核为每个IPC对象维护一个数据结构(/usr/include/linux/ipc.h)
struct ipc_perm {
key_t __key; /* Key supplied to xxxget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};

这里写图片描述

消息队列,共享内存和信号量都有这样⼀一个共同的数据结构。

三、消息队列结构(/usr/include/linux/msg.h)

这里写图片描述

可以看到第一个条目就是IPC结构体,即是共有的,后面的都是消息队列所私有的成员。消息队列是用链表实现的。

四. 函数

1、创建新消息队列或取得已存在消息队列
原型:int msgget(key_t key, int msgflg);
参数:
key:可以认为是⼀一个端⼜⼝口号,也可以由函数ftok⽣生成。
msgflg:
IPC_CREAT :如果IPC不存在,则创建一个IPC资源,否则打开操作。
IPC_EXCL:只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。
如果单独使用IPC_CREAT,XXXget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符。
如果将IPC_CREAT和IPC_EXCL标志⼀一起使用,XXXget()将返回一个新建的IPC标识符;如果该IPC资源已存在,或者返回-1。
IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。
2、向队列读/写消息
原型:
msgrcv从队列中取用消息:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, intmsgflg);
msgsnd将数据放到消息队列中:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数:
msqid:消息队列的标识码
msgp:指向消息缓冲区的指针,此位置⽤用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:
struct msgstru{
long mtype; //大于0
char mtext[用户指定大小];
};
msgsz:消息的大小。
msgtyp:从消息队列内读取的消息形态。如果值为零,则表示消息队列中的所有消息都会被读取。
msgflg:用来指明核心程序在队列没有数据的情况下所应采取的行动。如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式。
3、设置消息队列属性
原型:int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
参数:msgctl 系统调用对 msgqid 标识的消息队列执行 cmd 操作,系统定义了 3 种 cmd 操作: IPC_STAT , IPC_SET , IPC_RMID
IPC_STAT : 该命令用来获取消息队列对应的 msqid_ds 数据结构,并将其保存到 buf 指定的地址空间。
IPC_SET : 该命令⽤用来设置消息队列的属性,要设置的属性存储在buf中。
IPC_RMID : 从内核中删除 msqid 标识的消息队列。
4、 ftok函数
函数ftok把一个已存在的路径名和一个整数标识得转换成一个key_t值,称为IPC键:
# include < sys/types.h >
# include < sys/ipc.h >
key_t ftok(const char *pathname, int proj_id);
该函数把从pathname导出的信息与id的低序8位组合成一个整数IPC键。


消息队列相关代码实现见:

Linux-OS-Network/tree/master/msg”>https://github.com/xiaoxiao-su/Linux-OS-Network/tree/master/msg


IPC主题二:信号量

信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,⽽而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能。当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求,等于0,无资源可用,进程会进入睡眠状态直至资源可用。
当进程不再使用一个信号量控制的共享资源时,信号量的值+1,对信号量的值进行的增减操作均为原子操作,这是由于信号量主要的作用是维护资源的互斥或多进程的同步访问。而在信号量的创建及初始化上,不能保证操作均为原子性。

一 、为什么要使用信号量

为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它, 也就是说信号量是用来调协进程对共享资源的访问的。其中共享内存的使用就要用到信号量。

二、 信号量的工作原理

由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行。
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1。

三 、Linux的信号量机制

Linux提供了一组精心设计的信号量接口来对信号进行操作,它们不只是针对二进制信号量,下面将会对这些函数进行介绍,但请注意,这些函数都是用来对成组的信号量值进行操作的。它们声明在头⽂文件sys/sem.h中。

四、函数

1、 创建信号量
函数原型:int semget(key_t key, int nsems, int semflg);
nsems:该集合中的信号量数,如果是创建新集合(一般在服务器进程中),则必须指定nsems,如果是引用现有集合(一个客户进程),则将nsfems指定为0

2、对多种信号量的操作
函数原型:int semctl(int semid, int semnum, int cmd, …);
第四个参数是可选的,是否使用取决于所请求的命令,如果使用该参数,则其类型是semun,它是也联合体,如下:

这里写图片描述

注意:该联合体没有定义在任何系统头文件中,因此得用户自己声明。 《Centos 下确实是这样,但是UNIX下不同,不需要自己定义声明》
3、 PV操作
函数原型:int semop(int semid, struct sembuf *sops, unsigned nsops);
nsops:规定该数组中操作的数量(元素数)
sops:是一个指针,指向一个由sembuf结构表示的信号集操作数组,结构体成员有:
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */


信号量相关代码实现见:

Linux-OS-Network/tree/master/sem”>https://github.com/xiaoxiao-su/Linux-OS-Network/tree/master/sem


IPC主题三:共享内存

共享存储允许两个或者多个进程共享一个给定的存储区,因为数据不需要在客户进程和服务器进程间进行复制,所这是一种最快的IPC,若服务器进程正在将数据放入存储共享区,则在它做完这一操作之前,客户进程不应当去取这些数据。通常,信号量用于同步共享存储访问的。

一、 函数

1、 创建共享存储段
int shmget(key_t key, size_t size, int shmflg);

2、对共享存储段执行多种操作
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

3、一旦创建了一个共享存储段,进程就可调用shmat将其连接到它的地址空间中
void *shmat(int shmid, const void *shmaddr, int shmflg);

4、当对共享存储段的操作结束时,则调用shmdt与该段分离
int shmdt(const void *shmaddr);


共享内存相关代码实现见:

Linux-OS-Network/tree/master/shm”>https://github.com/xiaoxiao-su/Linux-OS-Network/tree/master/shm


总结IPC优缺点::

一、消息队列与管道区别:

1、消息队列是基于字节流的,而管道是基于数据流的数据通信
2、消息队列是要显示删除,否则一直存在,是随内核的(生命周期),而管道是随着进程的
3、管道和队列都可以由操作系统开辟

二、信号量与消息队列的区别:

1、信号量不具有数据交换功能,但是消息队列和管道具有数据交换功能

三、共享内存和信号量的区别:

1、共享内存不提供同步与互斥机制(即一般与信号量配合着使用)

参考资料:http://blog.csdn.net/colzer/article/details/8146138

0 0
原创粉丝点击