刚接触该书时甚是兴奋,书中思想令人振奋,以致深夜久不能寐,但毕竟书籍年代久远,刚好博主处于学习阶段,本着探索精神,搭配 man 手册进行编码学习。献上 W. Richard Stevens 所写书籍中的一句话——"理解某种特性的实现通常有助于了解如何使用该特性”。这不禁让人回想起侯捷先生所著《STL 源码剖析》的开篇,“源码之前,了无秘密”。
本文提炼自 UNIX 网络编程第 2 卷,下面会引用原书前言以明确内容提纲,致敬原书作者 W. Richard Stevens。
进程间通信(IPC)几乎是所有 UNIX 程序性能的关键,理解 IPC 也是理解如何开发不同主机间网络应用程序的必要条件。
原书从Posix IPC 和 System V IPC 的内部结构开始讨论,全面深入地介绍 4 种 IPC 形式。
IPC 是进程间通信(interprocess communication)的简称。传统上该术语描述的是运行在某个操作系统之上的不同进程间各种消息传递(message passing)的方式。还讲述多种形式的同步(synchronization),因为像共享内存区需要某种形式的同步参与。
按照传统的 UNIX 编程模型,我们在一个系统上运行多个进程,每个进程都有各自的地址空间。UNIX 进程间的信息共享可以有多种方式。UNIX 进程间共享信息的三种方式如图:
线程
从 IPC 角度看来,一个给定进程内的所有线程共享同样的全局变量。然而我们必须关注各个线程间对全局数据的同步访问。尽管同步不是一种明确的 IPC 形式,但它确实伴随许多形式的 IPC 使用,以控制对某些共享数据的访问。
当两个或多个无亲缘关系的进程使用某种类型的 IPC 对象来彼此交换信息时,该 IPC 对象必须有一个某种形式的名字(name)或标识符(identifier,简称 ID)。这样其中要给进程(往往是服务器)可以创建该 IPC 对象,其余进程则可以指定同一个 IPC 对象。
管道没有名字(因此不能用于无亲缘关系的进程间),但是 FIFO 有一个在文件系统中的路径名作为其标识符(因此可用于无亲缘关系的进程间)。对于一种给定的 IPC 类型,其可能的名字的集合称为它的名字空间(name space)。名字空间很重要,因为对于除匿名管道以外的所有形式的 IPC 来说,名字是客户与服务器彼此连接以交换信息的手段。
key_t 键与 ftok 函数
以 key_t 键与 ftok 函数为例,在System V 消息队列、System V 信号量、System V 共享内存区,这三种 IPC 使用 key_t 值作为它们的名字。头文件
函数 ftok 把一个已存在的路径名和一个整数标识符转换成一个 key_t 值,称为 IPC 键。
#include
key_t ftok(const char *pathname, int id); // 返回: 若成功则为 IPC 键, 若出错则为 -1
包裹函数
在现实程序中,我们必须检查每个函数调用是否返回错误。由于碰到错误时终止程序执行是个惯例,因此我们可以通过定义包裹函数(wrapper function)来缩短程序的长度。包裹函数执行实际的函数调用,测试其返回值,并在碰到错误时终止进程。我们使用的命名约定是将函数名的第一个字母改成大写字母,例如:
Sem_post(ptr);
定义这个包裹函数:
void Sem_post(sem_t *sem) {
if (sem_post(sem) == -1) {
err_sys("sem_post error");
}
}
每当你遇到一个大写字母开头的函数名时,它就是我们所说的包裹函数。它调用一个名字相同但相应小写字母开头的实际函数。当碰到错误时,包裹函数总是输出一个错误消息后终止。
UNIX errno 值
每当在一个 UNIX 函数(同 Linux 函数)中发生错误时,全局变量 errno 将被设置成一个指示错误类型的正数,函数本身则通常返回 -1。
#include
#include
#include
#include
int main() {
pid_t pid;
if ((pid = fork()) < 0) {
// 出错处理
perror("fork()");
exit(1);
}
if (pid) {
// 子进程 fork() 返回 0, 父进程 fork() 返回正的子进程 pid
printf("In Parent printf Process! <%d>--><%d>--><%d>\n", getppid(), getpid(), pid);
} else {
printf("In Child printf Process! <%d>--><%d>\n", getppid(), getpid());
}
return 0;
}
IPC 传统上是 UNIX 中一个杂乱不堪的领域。虽然有了各种各样的解决办法,但没有一个是完美的。讨论分为 4 个主要领域:
考虑单个进程中的多个线程间 IPC 以及多个进程间的 IPC。