UNIX IPC

进程间通信

Linux及城建通信可以分为两类,一类是同一台主机之间的进程间通信,另一类是网络之间的进程通信。
同一台主机之间的进程通信有以下几种:

  1. 管道
  2. 协程
  3. FIFO
  4. 消息队列
  5. 信号量
  6. 共享存储
  7. UNIX域套接字。

而网络通信主要就是套接字通信。

管道

管道的局限性:

  1. 半双工。(虽然大多数实现都是全双工,为了移植性,假设是半双工)。
  2. 管道只能在具有公共祖先的两个进程之间使用。

FIFO没有第二种局限性,UNIX域套接字两种局限性都没有。

函数popenpclose

popen创建一个FILE stream指针。
指定参数’r’从cmd的标准输出读,指定参数’w’向cmd的标准输入写。

协同进程

UNIX过滤程序从标准输入读取数据,向标准输出读取数据。
当一个过滤程序即产生某个过滤程序的输入,又读取该过滤程序的输出时,它就变成了协同进程。
popen只提供连接到另一个进程的标准输入或者标准输出的单向通道,而协同新城有连接到另一个进程的两个单向管道:一个连接到其标准输入,另一个则连接到它的标准输出。
相当于我们把数据写入一个进程的标准输出,经过它的处理后,又从它的标准输出读出。(可以通过创建两个管道来实现,一个管道向该进程的标准输入写,另一个管道从该进程的标准输出读。)
注意如果使用标准IO函数测试的话,可能要注意缓冲区的设置。

FIFO

FIFO也叫命名管道。但是管道只能在两个相关的进程之间使用,而FIFO可以在任意两个进程之间交换数据。
创建一个FIFO之后,需要使用open打开它。
如果不指定O_NONBLOCK,只读open会阻塞到别的进程为写打开这个FIFO为止,而只写open会阻塞到别的进程为读而打开这个FIFO为止。
如果指定O_NONBLOCK,只读open总是返回成功。而如果没有其他进程为读打开这个FIFO,返回-1,置位errno为ENXIO。
FIFO的用处:

  1. shell命令将数据从一条管道传送到另一条时,无需创建中间临时文件。
  2. 客户进程-服务器进程应用程序中,FIFO用作汇集点,在服务器和客户端之间传送数据。

XSI IPC

XSI IPC包含三种,分别是消息队列,信号量,共享内存。IPC通过IPC描述符进行访问(和文件描述很像)。但是和文件不同的是,IPC都没有名字,所以要创建多个IPC,怎么区分它们,这个就是key(键)的作用,每一个IPC都有一个键(和文件名字很像)。
问题就是怎么让通信的进程知道它们要使用的IPC描述符?

  1. 使用IPC_PRIVATE创建一个新的IPC(IPC_PRIVATE保证创建一个新的IPC,将返回的IPC描述符存放在一个文件中。或者就是父子进程之间,直接复制IPC描述符。
  2. 在一个公用的头文件中指定一个键,这个键被父进程和子进程都认可。这种可能是键已经被用过了,再创建的时候就会出错。
  3. 根据一个路径名和项目ID创建一个键。

不能使用IPC_PRIVATE作为一个键来引用消息队列,引用消息队列时要绕过get函数。

优点和缺点

  1. IPC都是在系统范围内起作用的,没有引用计数,所以,如果进程创建了一个IPC,然后退出,那么这些IPC都不会被删除,需要显式的删除。在使用信号量时,如果进程退出时没有释放信号量,信号量不会被释放。
  2. IPC结构在文件系统中没有名字,所以不能使用操作文件的那些函数,需要增加新的系统调用和命令。
    此外,没有办法使用多路转接。
  3. 如果显式的删除IPC的话,不管当前有多少个进程在使用,都会被删除。下一次再使用的话就会报错。

XSI消息队列

消息队列

XSI信号量

XSI信号量是一个计数器,用于为多个进程提供对共享数据的访问。但是XSI信号量不是单个信号量,而是一个信号量的集合。
需要使用semget创建一个信号量集合(数量可以等于1)。
然后使用semget获得一个信号量描述符(成功创建时返回的也是信号量描述符)。
接下来使用semctl设置每个信号量的值。
使用semop控制信号量值的增和减。

XSI信号量的缺点:

  1. 是一个信号集合。
  2. 信号量的创建和初始化是分开的。
  3. 当进程退出时,可能没有释放分配给他的信号量,使用SEM_UNDO解决。

XSI共享内存[2]

共享内存允许两个或者多个进程共享一个给定的存储区。因为数据不需要在客户进程和服务器进程之间进行复制,这是最快的一种IPC。
使用共享内存时,需要注意的是,在多个进程之间同步访问一个给定的存储区。通常使用信号量同步共享内存,当然也可以使用互斥量和记录锁,线程锁共享。

共享内存和存储IO映射的区别,用mmap映射的存储段是和文件相关的,而XSI共享内存并没有这种限制。

它们之间的区别

  1. 信号量和互斥量的区别。都表示对于资源的访问权,但是信号量的资源计数可以超过1,而互斥量的资源计数为1。
  2. 消息队列。

进程间传递字符串

若果需要两个进程间的双向数据流,可以使用消息队列和全双工管道。

  1. 全双工管道。
  2. 消息队列。先创建一个消息队列,然后调用fork,它们就可以实现通信了。因为消息队列不能使用IPC_PRIVATE作为一个KEY打开消息队列。

进程同步的方法

信号量,记录锁和互斥量的比较。如果在多个进程中共享同一个资源,可以使用这三种方法的任意一种来实现。

  1. 使用信号量。创建一个包含一个成员的信号量集合,将该信号量的值初始化为1。为了分配资源,以sem_op为-1调用semop,为了释放资源,以sem_op为+1调用semop。对每个操作都指定SEM_UNDO,处理在未释放资源条件下进程终止的情况。
  2. 使用记录锁。创建一个空文件,并且使用该文件的第一个字节(无需存在)作为锁字节,为了分配资源,先对该字节获得一个写锁。释放资源时,对该字节解锁。记录锁的性质保证了一个锁的持有者进程终止时,内核会自动释放资源。
  3. 使用互斥量。所有的进程将相同的文件映射到它们的地址空间中,使用PTHREAD_PROCESS_SHARED互斥量属性在文件的相同偏移处初始化互斥量。为了分配资源,对互斥量加锁。为了释放资源,解锁互斥量。如果一个进程没有释放互斥量而终止,恢复是非常困难的。

参考文献

1.《APUE》第三版
2.https://www.cnblogs.com/my_life/articles/4538299.html