UNIX网络进程间通信漫谈(1)
- 进程间通信
IPC是进程间通信的简称,指的是运行在某个操作系统上的不同进程间各种消息传递方式,在Unix操作系统过去30年的演变史中,消息传递经历了如下几个阶段:
- 管道,管道是第一个广泛使用的IPC形式,管道分为有名管道和无名管道,无名管道只可以在父子进程间通信,有名管道可以在非父子进程间通信。
- System V消息队列,它们可以用在同一主机上有亲缘关系或无亲缘关系的进程间,当今多数Unix版本都支持此种机制。
- Posix消息队列,Posix消息队列是Posix实时标准,支持任何进程间的通信。
- RPC,远程过程调用是从一个系统上某个程序调用另一个系统上某个函数的一种方法,同样也适用于同一主机上的客户和服务器之间。
有了进程间通信,那么就需要进程间同步,下面是Unix提供的各种同步形式的演变:
- 记录上锁
- System V信号量
- Posix信号量
- 互斥锁和条件变量
- 读写锁
说到这里有必要提起两个标准,那就是Posix,System V。POSIX 表示可移植操作系统接口(Portable Operating System Interface ,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEEE为要在各种UNIX操作系统上运行的软件而定义的一系列API标准的总称。System V, 曾经也被称为 AT&T System V,是Unix操作系统众多版本中的一支,基于System V的系统也实现了自己的一套标准。先如今这两种标准都很流行,但是POSIX操作简单,通用性强,逐渐被众多unix系统采用。
- 管道
管道通过read,write函数读取数据,通过pipe函数创建,创建子管道时调用fork函数,提供单路数据流(有些也提供全双工管道,大部分提供半双工,用在shell中),其典型用途是通过创建父子进程的方式提供进程间的通信手段,如下图所示:
例如到我们在shell中输入一个像下面的命令时: who|sort|lp, 这三个进程间的通信如下图所示,只提供单项数据流,当需要一个双向数据流时,我们必须创建两个管道,每个方向一个:
命名管道的正真优势表现在服务器可以是一个长期运行的进程(例如守护进程),使用与一个服务器多个客户端的情况。管道服务器是迭代服务器,它以串行方式每次处理一个客户请求,它们易遭受拒绝服务攻击(发送请求,但不取走数据)。但即使是并发服务器,同样会遭受拒绝服务攻击(多次请求,知道大道最大FD限制)
- Posix消息队列
消息队列可认为是一个消息链表。有足够写权限的线程可往队列中放置消息,有足够读权限的线程可从队列中取走消息。每个消息都是一个记录,它由发送者赋予一个优先级。在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。这个管道相反,管道是先有读出者,才可以写。队列里面的消息可以是一个进程写入的,然后另一个进程读出。mq_open创建一个新队列或打开一个已经存在的队列,mq_close关闭队列,mq_unlink则删除队列名。往一个队列中放置消息使用mq_send,从一个队列中读出消息使用mq_receive。队列属性的查询与设置使用mq_getattr和mq_setattr,函数mq_notity可以注册一个信号或线程,当有消息被放置到队列上时发送。队列中的每个消息被赋予一个小整数优先级,mq_receive每次被调用时总是返回最高优先级的最早消息。
- 进程间同步
- 互斥锁和条件变量
互斥锁指代相互排斥,它是最基本的同步形式。互斥锁用于保护临界区,以保证任何时刻只有一个线程或进程在执行代码。尽管我们说互斥锁保护的是临界区,实际上保护的是在临界区中被操作的数据。也就是说,互斥锁通常用于保护由多个线程或进程分享的共享数据。条件变量是用于等待,每个条件变量总是有一个互斥锁与之关联。当条件变量被发送信号后,系统立即调度等待在其上的线程,该线程开始运行,并取得互斥锁,开始访问临界区。另外一个重要的锁称为读写锁,这些读写锁的分配规则如下:
- 只要没有线程持有某个给定的读写锁用于写,那么任意数目的线程可以持有该读写锁用于读
- 仅当没有线程持有某个给定的读写锁用于读或用于写时,才可以分配该读写锁用于写
某些应用中读写数据比修改数据频繁,这些应用可以是读写锁。提高读写的并发度。