进程间通信至少可以通过传送打开文件来实现,不同的进程通过一个或多个文件来传递信息,事实上,在很多应用系统里,都使用了这种方法。但一般说来,进程间 通信(IPC:InterProcess Communication)不包括这种似乎比较低级的通信方法。Unix系统中实现进程间通信的方法很多,而且不幸的是,极少方法能在所有的Unix系 统中进行移植(唯一一种是半双工的管道,这也是最原始的一种通信方式)。而Linux作为一种新兴的操作系统,几乎支持所有的Unix下常用的进程间通信 方法:管道、消息队列、共享内存、信号量、套接口等等。
1、管道(pipe)
管道是进程间通信中最古老的方式,它包括无名管道和有名管道两种,
无名管道用于父进程和子进程间的通信,
有名管道用于运行于同一台机器上的任意两个进程间的通信。
无名管道由pipe()函数创建:
#include <unistd.h>
int pipe(int filedis[2]);
参数filedis返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开;filedes[1]的输出是filedes[0]的输入
1 1 #include <stdio.h> 2 2 #include <string.h> 3 3 #include <stdlib.h> 4 4 #include <errno.h> 5 5 #include <unistd.h> 6 6 7 7 #define INPUT 0 8 8 #define OUTPUT 1 9 9 10 10 int main() 11 11 { 12 12 int file_descriptors[2]; 13 13 pid_t pid; 14 14 char buf[256]; 15 15 char send1[256]; 16 16 int returned_count; 17 17 18 18 //创建无名管道 19 19 pipe(file_descriptors); 20 20 //创建子进程 21 21 if((pid=fork()) == -1) 22 22 { 23 23 printf("Error in fork\n"); 24 24 exit(1); 25 25 } 26 26 //执行子进程 27 27 if(pid == 0) 28 28 { 29 29 printf("int the child process....\n"); 30 30 fgets(send1,256, stdin); 31 31 32 32 //子进程向父进程写数据,关闭管道的读端; 33 33 close(file_descriptors[INPUT]); 34 34 write(file_descriptors[OUTPUT], send1, strlen(send1)); 35 35 exit(0); 36 36 } 37 37 else //执行父进程 38 38 { 39 39 printf("int the parent process...\n"); 40 40 //父进程从管道读取子进程写的数据,关闭管道的写端 41 41 close(file_descriptors[OUTPUT]); 42 42 returned_count = read(file_descriptors[INPUT], buf, sizeof(buf)); 43 43 printf("%d bytest of data received from child process: %s\n" 44 44 ,returned_count,buf); 45 45 } 46 46 47 47 return 0; 48 48 } 49 //////////////////////// 50 [root@cp ~]# ./a.out 51 int the parent process... 52 int the child process.... 53 lkfjasldkf 54 11 bytest of data received from child process: lkfjasldkf
在linux系统下,有名管道可由两种方式创建:命令行方式mknod 系统调用 和函数mkfifo。
下面的两种途径都在当前目录下生成了一个名为myfifo的有名管道:
方式一:mkfifo("myfifo" , "rw");
方式二:mknod myfifo p
生成了有名管道后,就可以使用一般的文件I/O函数如open, close, read, write等来对它进行操作。
2、消息队列
3、共享内存
共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。得到共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但现实中并不常用,因为它控制存取的将是实际的物理内存,在linux系统下,这只有通过限制linux系统存取的内存才可以做到,这当然不太实际。常用的方式是通过shmxxx函数族来实现利用共享内存进行存储的。
首先是用的函数是shmget,它获得一个共享存储标识符。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int flag);
这个函数有点类似malloc函数,系统按照请求分配size大小的内存用作共享内存。linux系统内核中每个IPC结构都有一个非负整数的标识符,这样对一个消息队列发送消息时总要引用标识符就可以了。这个标识符是内核由IPC结构的关键字得到的,这个关键字,就是上面第一个函数的key。 数据类型key_t是头文件sys/types.h 中定义的,它是一个长整形的数据。
当共享内存创建后,其余进程可以调用shmat() 将其连接到自身的地址空间中;
void *shmat(int shmid, void *addr, int flag);
shmid 为shmget函数返回的共享存储标识符, addr 和flag参数决定了以什么方式来确定连接的地址,函数的返回值即是该进程数据段所连接的实际地址, 进程可以对此进程进行读写操作;
使用共享存储来实现进程间通信的注意点是对数据存取的同步,必须确保当一个进程去读取数据时,它所想要的数据已经写好了。通常,信号量被要来实现对共享存储数据存取的同步,另外,可以通过使用shmctl函数设置共享存储内存的某些标志位如SHM_LOCK,
SHM_UNLOCK等来实现。
4、信号量
信号量称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是前一节的共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况,一般来说,为了获得共享资源,进程需要执行下列操作:
1、测试控制该资源的信号量
2、若此信号量的值为正,则允许进行使用该资源。进程将信号量减1
3、若此信号量为0,则该资源目前不可用,进程进入睡眠状态,直到信号量大于0.进程被唤醒,转入步骤1;
4、当进程不再使用一个信号量控制的资源时,信号量加1。如果当时有进程正在睡眠等待此信号量,则唤醒此进程。
维护信号量状态的是Linux内核操作系统而不是用户进程。我们可以从头文件/usr/scr/linux/include/linux/sem.h中看到内核用来维护信号量状态的各个结构的定义。信号量是一个数据集合,用户可以单独使用这一集合的每个元素。要调用的第一个函数是semget,用以获得一个信号量ID;