进程间通信至少可以通过传送打开文件来实现,不同的进程通过一个或多个文件来传递信息,事实上,在很多应用系统里,都使用了这种方法。但一般说来,进程间 通信(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 "); 24 24 exit(1); 25 25 } 26 26 //执行子进程 27 27 if(pid == 0) 28 28 { 29 29 printf("int the child process.... "); 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... "); 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 " 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。
生成了有名管道后,就可以使用一般的文件I/O函数如open, close, read, write等来对它进行操作。
什么是命名管道称为FIFO文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在;
由于Linux中所有的事物都可被视为文件,所以对命名管道的使用也就变得与文件操作非常统一,也使得它的使用非常方便,同时我们也可以像平常的文件名一样在命令中使用。
使用下面两个函数均可以创建一个命名管道,函数原型如下:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);
int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t)0);
这两个函数都能创建一个FIFO文件,注意是创建一个真实存在于文件系统中的文件,filename指定了文件名,而mode则指定了文件的读写权限;
mknod 是比较老的函数,而使用mkfifo函数更加简单和规范,所以建议在可能的情况下,尽量使用mkfifo而不是mknod。
访问命名管道
1)打开FIFO文件
与打开其他文件一样,FIFO文件也可以使用open调用来打开。注意,mkfifo函数仅仅只是创建一个FIFO文件,要使用命名管道还要将其打开。
注意点:
1、程序不能以O_RDWR模式打开FIFO文件进行读写操作,而其行为没有明确定义。因为假如一个管道以读/写方式打开,进程就会读回自己的输出,同时我们通常使用FIFO只是为了单向的数据传递
2、传递给open调用的是FIFO的路径名,而不是正常的文件。
打开FIFO文件通常有四种方式
open(const char *path, O_RDONLY); //只读 并默认是阻塞
open(const char *path, O_RDONLY | O_NOBLOCK); //无阻塞只读
open(const char *path, O_WRONLY); //只写 并默认是阻塞
open(const char *path, O_WRONLY | O_NONBLOCK); //无阻塞只写
open调用的阻塞是怎么一回事? 很简单,对于只读(O_RDONLY)打开的FIFO文件,如果open调用是阻塞的,除非有一个进程以写方式打开同一个FIFO, 否则它读不到数据就不会返回; 如果open调用是非阻塞的,则即使没有其他进程以写方式打开同一FIFO方式,open调用将成功并立即返回。
对于以只写方式(O_WRONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_WRONLY),open调用将被阻塞,直到有一 个进程以只读方式打开同一个FIFO文件为止;如果open调用是非阻塞的(即第二个参数为O_WRONLY | O_NONBLOCK),open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打 开。
使用FIFO实现进程间的通信
两个进程如何通过FIFO实习通信,首先要有两个进程,一个源文件为fifowrite.c, 它在需要时创建管道,然后向管道写入数据,数据有文件Data.txt提供,大小为10M, 内容都是字符‘0’, 另一个源文件为fiforead.c,它从FIFO中读取数据,并把读到的数据保存到另一个文件DataFormFIFO.txt中,
命名管道的安全问题
前面指的是两个进程之间通信问题,也就是说,一个进程向FIFO文件写数据,而另一个进程从FIFO文件中读取数据。
假如只使用一个FIFO文件,如果有多个进程同时向同一个FIFO文件写数据,而只有一个读FIFO进程在同一个FIFO文件中读取数据时,则会发生数据块的相互交错问题。而在实际中多个不同进程向一个FIFO读进程发送数据是很常见的;
为了解决这一问题,就是让写操作的原子化。即: 系统规定:在一个以O_WRONLY(阻塞方式)打开的FIFO中,如果写入的数据长度小于等待PIPE_BUF, 结果是 要么写入全部字节, 要么 一个字节都不写入。 如果所有的写请求都是发往一个阻塞的FIFO时,并且每个写请求的数据长度小于等于PIPE_BUF字节,系统就可以确保数据决不会交错在一起;
为了数据的安全,我们很多时候要采用阻塞的FIFO,让写操作变成原子操作。
//fiforead.c
1 #include <unistd.h> 2 #include <stdlib.h> 3 #include <stdio.h> 4 #include <fcntl.h> 5 #include <sys/types.h> 6 #include <limits.h> 7 #include <string.h> 8 9 int main() 10 { 11 int pipe_fd = -1; 12 int data_fd = -1; 13 const char *fifo_name = "./my_fifo"; 14 int res = 0; 15 int open_mode = O_RDONLY; 16 char buffer[PIPE_BUF + 1]; 17 int bytes_read = 0; 18 int bytes_write = 0; 19 20 memset(buffer, '