当我们在 Linux 系统中进行进程间通信时,例如信号量,消息队列,共享内存等方式,会发现有System V以及POSIX两种类型。今天我们就来简单介绍下它们。
POSIX:
POSIX(Portable Operating System Interface for Computing Systems)是由IEEE 和ISO/IEC 开发的一簇标准。该标准是基于现有的UNIX 实践和经验,描述了操作系统的调用服务接口,用于保证编制的应用程序可以在源代码一级上在多种操作系统上移植运行。它是在1980 年早期一个UNIX 用户组(usr/group)的早期工作的基础上取得的。该UNIX 用户组原来试图将AT&T 的系统V 和Berkeley CSRG的BSD 系统的调用接口之间的区别重新调和集成,从而于1984 年产生了/usr/group 标准。1985 年,IEEE操作系统技术委员会标准小组委员会(TCOS-SS)开始在ANSI 的支持下责成IEEE 标准委员会制定有关程序源代码可移植性操作系统服务接口正式标准。到了1986 年4 月,IEEE 就制定出了试用标准。第一个正式标准是在1988 年9 月份批准的(IEEE 1003.1-1988),也既以后经常提到的POSIX.1 标准。
System V:
System V, 曾经也被称为 AT&T System V,是Unix操作系统众多版本中的一支。它最初由 AT&T 开发,在1983年第一次发布。一共发行了4个 System V 的主要版本:版本1、2、3 和 4。System V Release 4,或者称为SVR4,是最成功的版本,成为一些UNIX共同特性的源头,例如 ”SysV 初始化脚本“ (/etc/init.d),用来控制系统启动和关闭,System V Interface Definition (SVID) 是一个System V 如何工作的标准定义。
AT&T 出售运行System V的专有硬件,但许多(或许是大多数)客户在其上运行一个转售的版本,这个版本基于 AT&T 的实现说明。流行的SysV 衍生版本包括 Dell SVR4 和 Bull SVR4。当今广泛使用的 System V 版本是 SCO OpenServer,基于 System V Release 3,以及SUN Solaris 和 SCO UnixWare,都基于 System V Release 4。
System V 是 AT&T 的第一个商业UNIX版本(UNIX System III)的加强。传统上,System V 被看作是两种UNIX"风味"之一(另一个是 BSD)。然而,随着一些并不基于这两者代码的UNIX实现的出现,例如 Linux 和 QNX, 这一归纳不再准确,但不论如何,像POSIX这样的标准化努力一直在试图减少各种实现之间的不同。
与 System V 对象类似,POSIX IPC 对象的属主、属主的组以及其他用户具有读取和写入权限,但是没有执行权限。POSIX IPC 对象的属主无法将对象分配给其他属主。POSIX IPC 包括以下功能:
1.消息允许进程将已格式化的数据流发送到任意进程。
2.信号量允许进程同步执行。
3.共享内存允许进程共享其部分虚拟地址空间。
与 System V IPC 接口不同,POSIX IPC 接口均为多线程安全接口。
由于之前的文章已经介绍过了System V 的 IPC,所以以下只简单介绍下 POSIX 的 IPC 接口。
POSIX 消息队列:
API | API 作用 |
---|---|
mqd_t mq_open(const char name, int oflag, mode_t mode, struct mq_attr attr) | 创建命名消息队列 |
mqd_t mq_close(mqd_t mqdes) | 结束到开放式消息队列的连接 |
mqd_t mq_unlink(const char *name) | 结束到开放式消息队列的连接,并在最后一个进程关闭此队列时将其删除 |
mqd_t mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio) | 将消息放入队列 |
ssize_t mq_receive(mqd_t mqdes, char msg_ptr, size_t msg_len, unsigned msg_prio) | 在队列中接收消息 |
mqd_t mq_notify(mqd_t mqdes, const struct sigevent *notification) | 通知进程或线程消息已存在于队列中 |
mqd_t mq_getattr(mqd_t mqdes, struct mq_attr attr) 、mqd_t mq_setattr(mqd_t mqdes, struct mq_attr newattr, struct mq_attr *oldattr) | 设置或获取消息队列属性 |
POSIX 信号量:
API | API 作用 |
---|---|
sem_t sem_open(const char name, int oflag, mode_t mode, unsigned int value) | 创建命名信号量 |
int sem_init(sem_t *sem, int pshared, unsigned int value) | 初始化信号量结构 |
int sem_close(sem_t *sem) | 结束到开放式信号量的连接 |
int sem_unlink(const char *name) | 结束到开放式信号量的连接,并在最后一个进程关闭此信号量时将其删除 |
int sem_getvalue(sem_t sem, int sval) | 将信号量的值复制到指定整数中 |
int sem_wait(sem_t *sem) | 递减信号量计数,当其他进程拥有信号量时进行阻塞,或者当其他进程拥有信号量时返回错误 |
int sem_post(sem_t *sem) | 递增信号量计数 |
POSIX 共享内存:
API | API 作用 |
---|---|
int shm_open(const char *name, int oflag, mode_t mode) | 创建共享内存 |
int shm_unlink(const char *name) | 结束到共享内存的连接,并在最后一个进程关闭它时将其删除 |
void mmap(void addr, size_t length, int prot, int flags, int fd, off_t offset) | 映射内存 |
内存映射机制mmap是POSIX标准的系统调用,有匿名映射和文件映射两种:
1.匿名映射使用进程的虚拟内存空间,它和malloc()类似,实际上有些malloc实现会使用mmap匿名映射分配内存,不过匿名映射不是POSIX标准中规定的。
2.文件映射有MAP_PRIVATE和MAP_SHARED两种。前者使用COW的方式,把文件映射到当前的进程空间,修改操作不会改动源文件。后者直接把文件映射到当前的进程空间,所有的修改会直接反应到文件的page cache,然后由内核自动同步到映射文件上。
相比于IO函数调用,基于文件的mmap的一大优点是把文件映射到进程的地址空间,避免了数据从用户缓冲区到内核page cache缓冲区的复制过程;当然还有一个优点就是不需要频繁的read/write系统调用。
总结
POSIX 在无竞争条件下,不需要陷入内核,其实现是非常轻量级的; System V 则不同,无论有无竞争都要执行系统调用,因此性能落了下风。
总体来说,System V IPC存在时间比较老,许多系统都支持,但是接口复杂,并且可能各平台上实现略有区别,如下图的结构限制:
而 POSIX 是新标准,如果只是开发的话,我觉得还是POSIX好,因为其语法简单、使用方便,并且各平台上实现都一样。
System V 的缺点可参考下图(来自 UNIX环境高级编程):