首页 > 代码库 > UnixC——进程间通信(IPC)

UnixC——进程间通信(IPC)

一、进程间通信(IPC):Unix / Linux 系统基于多进程,进程和进程之间数据的交互。
1.常见的IPC:1)文件    2)信号    3)管道(FIFO)    4)共享内存    5)消息队列    6)信号量集    7)网络编程(socket)

2.共享内存、消息队列和信号量集遵循相同的规范,统称为 XSI IPC。

------------------------------------------

二、管道:一种仅做为交互媒介的IPC的特殊的文件,不存储任何的数据,文件名后缀.p,只有在有读且有写操作时才能畅通,否则阻塞。

1.管道有两种用法:有名管道和无名管道。
1)有名管道:可用于所有进程之间的交互。无名管道:只能用于fork()创建的父子进程之间的交互。
2)有名管道:程序员创建管道文件进行IPC。无名管道:系统创建和维护管道文件进行IPC。
2.使用有名管道的步骤:
   a.创建管道文件:mkfifo命令|||mkfifo("...")函数 【touch 命令和 open()函数都无法创建管道文件】。
   b.像读写普通文件一样操作管道文件。
   c.可以删除管道文件。
注意:open 一个管道文件,权限不要用 O_RDWR。

------------------------------------------

三、XSI IPC 的通用规范:每个 IPC 都由一个内部的ID做唯一的标识。内部ID的获取,需要借助与之对应外部key(类型 key_t)。
1. key 获取的三种方式:
   a 使用宏 IPC_PRIVATE 做 key,但外部无法获取,基本不用。
   b 使用ftok()获取一个 key。

   c 在头文件中为每个IPC统一分配唯一的 key。

2.通过key 获取内部 idmsgget(2)、semget(2)、shmget(2)  //int msgget(key_t key, int msgflg);
3.每种IPC 结构都提供了一个 xxxctl()函数(查询、修改和删除)。
4.所有IPC 结构都是内核管理,不使用时需要手工删除。
5.IPC 结构的相关命令
   ipcs  - 查询当前的 IPC 结构
   ipcrm - 删除当前的 IPC 结构 (用 id 删除)
   选项:-a 所有 IPC 结构
 -m 共享内存  Shared Memory Segments
 -q 消息队列  Semaphore Arrays(更常用)
 -s 信号量集  Message Queues

------------------------------------------

四、共享内存:以一块共享的物理内存做媒介。共享内存虽然速度最快(效率最高的IPC),但当多个进程同时写数据时,数据会互相覆盖,导致混乱。

1.共享的实现(通常情况下,两个进程无法直接映射相同的内存。):
  a)内核先拿出一块物理内存,内核 负责管理。
  b)允许所有进程对这块内存进行映射。则两个不同的进程,就可以映射到相同的物理内存上,从而实现信息的交互。
2.编程步骤:
  a)获取 key。
  b)使用 shmget()函数用key创建 / 获取内部的 ID。
  c)使用 shmat()挂接共享内存(映射)- shm_nattch;  /* No. of current attaches */可以像正常操作一样使用共享内存。
  d)使用 shmdt()脱接共享内存(解除映射)。
  e)如果确定已经不再使用,可以使用shmctl()删除共享内存。

3.相关函数:
1)获取key:key_t ftok(const char *pathname, int proj_id);/* ftok()函数使用给定的文件名(必须存在,可访问的文件)和proj_id的低8位(必须非零)来生成key_t类型System V IPC的key,适用于msgget(2), semget(2)或者shmget(2)。*/

2)获取(创建)内部ID,并设置共享内存大小:int/*ID */ shmget(key_t key, size_t size/*共享内存的大小*/, int shmflg/*flag获取时:0;新建时:IPC_CREAT|0666(权限)*/);
3)挂载内存:void *shmat(int shmid, const void *shmaddr, int shmflg);(和mmap相似)如:void *shmat(int shmid, 0, 0);/*If  shmaddr  is NULL, the system chooses a suitable (unused) address at which to attach the segment. */ /* Return:On success shmat() returns the address of the  attached  shared  memory segment; on error (void *) -1 is returned。*/
4)脱接内存:int shmdt(const void *shmaddr);//shmaddr 是shmat()的返回值,首地址(虚拟)。
5)查询、修改和删除共享内存:int shmctl(int shmid, int cmd, struct shmid_ds *buf /*结构体指针*/ );
       cmd 参数:1)IPC_STAT - 查询  2)IPC_SET  - 修改  3)IPC_RMID - 按ID 删除IPC 结构
       查询时:会把共享内存的信息放入第三个参数。
       修改时:只有用户id、组id和权限可以修改,大小不可修改。
       删除时:第三个参数给 0 即可。删除共享内存时,挂接数必须为 0 ,才能真正删除,否则删除只是做一个标记,等挂接数为 0 时,才能真正删除。
------------------------------------------
五、消息队列(FIFO):存放消息的队列。

1.消息队列实现:数据先封入消息中,然后再把消息存入队列。一般情况下,队列有满有空。可以解决多个进程同时写数据的问题。

2.编程步骤:
a)得到外部的 key。
b)使用msgget()函数用key 创建 / 获取一个消息队列ID。
c)把数据 / 消息 存入队列 或 从队列中取出。//函数msgsnd()存入 / msgrcv()取出/* IPC_NOWAIT 无等待,直接返回 */
d)如果不再使用消息队列,可以msgctl()删除。
3.相关函数:

int msgsnd(int msqid/*消息队列的ID*/, const void *msgp, size_t msgsz, int msgflg); 

a)msgp:消息的首地址,其中消息分为有类型消息(更规范)和无类型消息
b)有类型消息结构:
struct Msg{ // 结构的命令可以自定义
    long mtype;//消息的类型,必须大于 0/*可通过定义宏的方式*/,接收消息时,可以按照类型有选择的接受消息
     ... ...    //数据区域,可以任意写
 }

c)size:是数据区域的大小,不包括 mtype的大小(有些时候也包括,规范情况下,不包括)。

d)flag:选项,可以是 0 代表阻塞(队列满了),也可以是IPC_NOWAIT 代表非阻塞(队列满了直接返回错误)。
e)成功返回 0,失败返回 -1。
 
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
a)msgtyp 可以让接收者有选择的接收消息,值可能是:
0 - 接收任意类型的消息(第一个,先入先出)。
>0 - 接收类型为 mtype 的特定消息。
<0 - 接收类型 小于等于 mtype 绝对值的消息,从小到大接收。
b)成功返回接收到的数据大小,失败返回 -1。