Linux 系统应用编程——进程间通信(下)

        在前面,我们学习了传统的进程间通信方式——无名管道(pipe)、有名管道(fifo)和信号(signal)。

        下面我们来学习 System V  IPC 对象

1、共享内存(share memory);

2、信号灯(semaohore);

3、消息队列(message queue);

 

       IPC对象是活动在内核级别的一种进程间通信的工具。存在的IPC对象通过它的标识符来引用和访问,这个标识符是一个非负整数,它唯一的标识了一个IPC对象,这个IPC对象可以是消息队列或信号量或共享存储器中的任意一种类型。

     Linux系统中标识符被声明成整数,所以可能存在的最大标识符为65535。当时对于IPC对象,其标识符(id)与文件描述符有所不同,使用open函数打开一个文件时,返回的文件描述符的值为当前进程最小可用的文件描述符数组的下标。IPC对象是系统全局的流水号,其删除或创建时相应的标识符的值会不断增加到最大的值,归零循环分配使用。

     IPC的标识符只解决了内部访问一个IPC对象的问题,如何让多个进程都访问某一个特定的IPC对象还需要一个外部键(key),一个IPC对象都与一个键相关联。这样就解决了多进程在一个IPC对象上汇合的问题。IPC对象时需要指定一个键值,类型为key_t,在<sys/types.h>中定义为一个长整型。键值到标识符的转换是由系统内核来维护的,这里调用IPC对象的创建函数(semget msgget shmget )实现key 值到 id 的转换。

      从上图中我们可以看到得到这个键值 key 有两种方法:

1)通用方法:调用ftok()函数
       函数ftok可以使用两个参数生成一个键值,函数原型如下:

#include <sys/ipc.h>
key_t ftok( const char *path, int id );

       函数中参数path是一个文件名。函数中进行的操作是,取该文件的stat结构的st_dev成员和st_ino成员的部分值,然后与参数ID的第八位结合起来生成一个键值。由于只是使用st_dew和st_ino的部分值,所以会丢失信息,不排除两个不同文件使用同一个ID,得到同样键值的情况。
系统为每一个IPC对象保存一个ipc_perm结构体,该结构说明了IPC对象的权限和所有者,每一个版本的内核各有不用的ipc_perm结构成员。

文件<sys/ipc.h> 中对其定义:

struct ipc_perm 
{
	ey_t key;
	id_t uid;
	id_t gid;
	id_t cuid;
	id_t cgid;
	nsigned short mode;
	nsigned short seq;
};

2)父子进程之间:

      Key 为IPC_PRIVATE,父子进程之间key值为IPC_PRIVATE。

      当有了一个IPC对象的键值,如何让多个进程知道这个键,可以有多种实现的办法:

1) 、使用文件来做中间的通道,创建IPC对象进程,使用键IPC_PRIVATE成功建立IPC对象之后,将返回的标识符存储在一个文件中。其他进程通过读取这个标识符来引用IPC对象通信。

2)、定义一个多个进程都认可的键,每个进程使用这个键来引用IPC对象,值得注意的是,创建IPC对象的进程中,创建IPC对象时如果该键值已经与一个IPC对象结合,则应该删除该IPC对象,再创建一个新的IPC对象。

3)、多进程通信中,对于指定键引用一个IPC对象而言,可能不具有拓展性,并且在该键值已经被一个IPC对象结合的情况下。所以必须删除这个存在对象之后再建立一个新的。这有可能影响到其他正在使用这个对象的进程。函数ftok可以在一定程度上解决这个问题。

但IPC对象存在一些问题,主要集中在以下几点:

1)、过于繁杂的编程接口,比起使用其他通信方式,IPC所要求的代码量要明显增多。

2)、IPC不使用通用的文件系统,这也是饱受指责的原因。所以不能使用标准I/O操作函数来读写IPC对象。为此不得不新增加一些函数来支持必要的一些操作(例如msgget msgrev msgctl等)并且对于不同类型的IPC对象都有一系列特定的操作函数。由于IPC不使用文件描述符,所以不能使用多路I/O监控函数select及poll函数来操作IPC对象。

3)、缺少的资源回收机制。由于IPC对象在使用过程中并不保存引用计数,所以当出现一个进程创建了IPC对象然后退出时,则这个对象只有在出现后面几种情况才会被释放或者删除,即由某一个进程读出消息,或者IPC的所有者或超级用户删除了这个对象。这也是IPC相对于管道或FIFO所欠缺的资源回收机制。

下面是文件对象和IPC对象的对比

 

一、共享内存

       共享内存可以说是Linux 下最快速、最有效的进程间通信方式。两个不同进程A 、B 共享内存的意思是,同一块物理内存被映射到进程A 、B 各自的进程地址空间,进程A 可以即时看到进程B 对共享内存中数据的更新;反之,进程B 也可以即时看到进程A对共享内存中数据的更新。

       这里简单说下映射的概念:

       Linux系统会为每个进程分配 4GB 的虚拟地址空间,一定情况下,需要将虚拟内存转换成物理内存,这就需要内存映射。为什么我们需要使用虚拟地址呢?最主要的原因是不同PC的物理内存会不一样,如果直接使用物理地址,会造成程序的移植性很差,另外虚拟地址访问内存有以下优势:

1、程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。

2、程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。

3、不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。

      进程可用的虚拟地址范围称为该进程的“虚拟地址空间”。每个用户模式进程都有其各自的专用虚拟地址空间。对于 32 位进程,虚拟地址空间通常为 4 GB,范围从 0x00000000 至 0xFFFFFFFF。

1、共享内存的概念

      共享内存从字面意义解释就是多个进程可以把一段内存映射到自己的进程空间,以此来实现数据的共享及传输,这也是所有进程间通信方式最快的一种,共享内存是存在于内核级别的一种资源。

      在Shell 中可以使用ipcs 命令查看当前系统IPC中的状态,在文件系统中/proc目录下有对其描述的相应文件

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值