- 论坛徽章:
- 0
|
目前小弟熟悉的、特定适用于父子进程之间的 IPC 方式有三种:pipe、匿名 FIFO(Unix domain socket)和共享内存。Named pipe, named FIFO, socket 和 SysV SHM 等因为都具有全局名称 / URI,适用于不相关的进程;而这里提到的三种 IPC,只适用于父子进程。
1、管道
Pipe 的特点:单向传递。在管道创建的时候,数据只能从读处读,从写处写,属于单向流动。相关文章已经太多了,小弟不多废话,贴一段代码提示记忆。
- $ cat -n pipe.c
- 1 #include <unistd.h>
- 2 #include <assert.h>
- 3
- 4 int main ()
- 5 {
- 6 int fd[2];
- 7 #define READER 0
- 8 #define WRITER 1
- 9
- 10 int r = pipe( fd );
- 11 if ( r != 0 ) {
- 12 perror( "pipe()" );
- 13 exit( 1 );
- 14 }
- 15
- 16 if ( fork() ) {
- 17 /* Parent, writer */
- 18 int n = 0;
- 19 close( fd[READER] );
- 20 while ( 1 ) {
- 21 ++n;
- 22 write( fd[WRITER], &n, sizeof(n) );
- 23 sleep( 1 );
- 24 }
- 25 }
- 26 else {
- 27 /* Child, reader */
- 28 int n;
- 29 close( fd[WRITER] );
- 30 while ( 1 ) {
- 31 read( fd[READER], &n, sizeof(n) );
- 32 printf( "Got value: %d\n", n );
- 33 }
- 34 }
- 35 }
复制代码
因为 pipe 只能进行单向传递的特性,popen(3) 的第二个参数只能为读写之一种,就是这个道理了。
2、socketpair
基本类似 pipe,不过是基于 Unix domain socket,通信为双向通信。
- $ cat -n socketpair.c
- 1 #include <sys/types.h>
- 2 #include <sys/socket.h>
- 3
- 4 #include <stdlib.h>
- 5 #include <stdio.h>
- 6
- 7 int main ()
- 8 {
- 9 int fd[2];
- 10
- 11 int r = socketpair( AF_UNIX, SOCK_STREAM, 0, fd );
- 12 if ( r < 0 ) {
- 13 perror( "socketpair()" );
- 14 exit( 1 );
- 15 }
- 16
- 17 if ( fork() ) {
- 18 /* Parent process: echo client */
- 19 int val = 0;
- 20 close( fd[1] );
- 21 while ( 1 ) {
- 22 sleep( 1 );
- 23 ++val;
- 24 printf( "Sending data: %d\n", val );
- 25 write( fd[0], &val, sizeof(val) );
- 26 read( fd[0], &val, sizeof(val) );
- 27 printf( "Data received: %d\n", val );
- 28 }
- 29 }
- 30 else {
- 31 /* Child process: echo server */
- 32 int val;
- 33 close( fd[0] );
- 34 while ( 1 ) {
- 35 read( fd[1], &val, sizeof(val) );
- 36 ++val;
- 37 write( fd[1], &val, sizeof(val) );
- 38 }
- 39 }
- 40 }
复制代码
Linux 系统中,socketpair 的第一个参数只能是 AF_LOCAL / AF_UNIX。
3、共享内存
传统 SysV IPC 系列提供了 Shared Memory 实现,简称 sysv shm。它的优劣在 Stevens 前辈的 APUE 中都有了详细介绍,此处不多做赘述。Linux 2.4 及之后版本的内核提供了一种新的进程间共享内存方式:通过 mmap。指定 MAP_SHARED | MAP_ANONYMOUS,系统会创建一块可被子进程继承的共享匿名内存块。它的优点是:与其它 mmap 分配的内存具有相同属性:当附着进程执行 exec 或者退出的时候,内存会被系统自动收回,而不像 sysv shm 一样仍然被保留在系统中。而且,由于不需要 key 来进行标识,它的 API 也相对更简单清晰。直接看代码好了。
- $ cat -n mmap.c
- 1 #include <stdlib.h>
- 2 #include <stdio.h>
- 3
- 4 #include <sys/mman.h>
- 5
- 6 int main ()
- 7 {
- 8 int *p = mmap( NULL, sizeof(int),
- 9 PROT_READ|PROT_WRITE,
- 10 MAP_SHARED|MAP_ANONYMOUS, // 关键在这里。
- 11 0, 0 );
- 12 if ( p == MAP_FAILED ) {
- 13 perror( "mmap()" );
- 14 exit( 1 );
- 15 }
- 16
- 17 if ( fork() ) {
- 18 while ( 1 ) {
- 19 sleep( 1 );
- 20 printf( "Current val: %d\n", *p );
- 21 fflush(NULL);
- 22 }
- 23 }
- 24 else {
- 25 while ( 1 ) {
- 26 (*p)++;
- 27 sleep( 1 );
- 28 }
- 29 }
- 30 }
复制代码
相比较之前两种方式,共享内存有许多不同。首先,它不是通过文件描述符进行标识,而是直接将虚拟页面映射到进程内存空间中;传递数据也不需要在用户空间 <-> 核心的三块 buf 之间来回传递;其次,当执行 exec 之后,进程空间被全部重新生成,mmap 会自动脱离被映射的地址而不会继续继承;第三,与 sysvshm 一样,使用过程需要其它的同步手段,而 pipe / socket 自身提供了同步机制。
以上是小弟的一点学习心得,欢迎指正。
ps. 忽然想起前一段时间看到的 Win32 API 中 CreateFileMapping 如果指定第一个参数(文件句柄) hFile 为 INVALID_HANDLE_VALUE,则会在系统内存交换文件中创建一块映射区域。这大约是 Win32 在放弃了 SysV shm 之后的取代方案吧。 |
|