前面我们讲了进程间通信的一种方式,匿名管道。
我们知道,匿名管道只能用于父子关系的进程之间。那么没有这种关系的进程之间该如何进行数据传递呢?
1.什么是命名管道
匿名管道是在缓存中开辟的输出和输入文件流的空间,只能用于父子关系的进程之间。因为父子进程的输入和输出文件描述符是一致的。
命名管道是一种实际存在的FIFO文件,称作“管道文件”,用于不同进程之间,命名管道进程间打开同一个FIFO文件,进行数据传递。
我们可以像普通文件一样操作FIFO文件。
不同进程,引用同一个FIFO文件,进行数据传递。
2.创建命名管道
mkfifo函数:创建一个命名管道
int mkfifo(const char *filename,mode_t mode);
filename:指定FIFO文件的名称
mode:指定文件的读写权限
3.访问命名管道
打开FIFO文件有四种方式:
open(const char *filename,O_RDONLY); open(const char *filename,O_RDONLY|O_NONBLOCK); open(const char *filename,O_WRONLY); open(const char *filename,O_WRONLY|O_NONBLOCK);
需要注意的是,不能以O_RDWR模式打开FIFO文件,
因为这样一个进程写入的数据会被该进程读取,FIFO一般只用做单向的数据传递。
open函数的第二个参数,表示是读管道,还是写管道。
O_NONBLOCK表示FIFO管道的读写是非阻塞的,默认的话,是阻塞的。
那么何为阻塞呢?
一个进程写模式打开管道的时候,必须有另一个进程以读模式打开;
或读模式的时候,必须有另一个进程写写模式打开,否则该进程open函数阻塞,直到满足以上关系。
非阻塞,意味着open函数会立即返回,若没有其他进程以只读方式打开,open返回-1,并且FIFO也不会被打开。
4.FIFO管道使用示例
下例有两个程序,fifowrite.c和fiforead.c分别写管道和读管道。
fifowrite.c中将一个文本文件data.txt,写到管道。
fiforead.c中从管道读取数据,并写到dataformfifo.txt文件中。
程序使用了默认的阻塞模式。
示例代码如下:
fifowrite.c
#include<sys/types.h> #include<stdlib.h> #include<stdio.h> #include<fcntl.h> #include<limits.h> int main() { const char *fifo_name = "/tmp/my_fifo"; int pipe_fd = -1; int data_fd = -1; int res = 0; const int open_mode = O_WRONLY; char buffer[PIPE_BUF+1]; if(access(fifo_name,F_OK)==-1) { res = mkfifo(fifo_name,0777); if(res!=0) { fprintf(stderr,"could not create fifo\n"); exit(EXIT_FAILURE); } } printf("process %d opening fifo O_WRONLY\n",getpid()); pipe_fd = open(fifo_name,open_mode); data_fd = open("data.txt",O_RDONLY); printf("process %d result %d\n",getpid(),pipe_fd); if(pipe_fd!=-1) { int bytes_read = 0; bytes_read = read(data_fd,buffer,PIPE_BUF); while(bytes_read>0) { res = write(pipe_fd,buffer,bytes_read); if(res==-1) { fprintf(stderr,"write error\n"); exit(EXIT_FAILURE); } bytes_read = read(data_fd,buffer,PIPE_BUF); buffer[bytes_read]=‘\0‘; } close(pipe_fd); close(data_fd); } else{ exit(EXIT_FAILURE); } printf("process %d finished.\n",getpid()); exit(EXIT_SUCCESS); }
fiforead.c
#include<stdlib.h> #include<stdio.h> #include<sys/types.h> #include<fcntl.h> #include<limits.h> int main() { const char *fifo_name = "/tmp/my_fifo"; int pipe_fd = -1; int data_fd = -1; int res = 0; int open_mode = O_RDONLY; char buffer[PIPE_BUF+1]; int bytes_read = 0; int bytes_write = 0; memset(buffer,‘\0‘,sizeof(buffer)); printf("process %d opening FIFO O_RDONLY\n",getpid()); pipe_fd = open(fifo_name,open_mode); data_fd = open("dataformfifo.txt",O_WRONLY|O_CREAT,0644); printf("process %d result %d\n",getpid(),pipe_fd); if(pipe_fd!=-1) { do{ res = read(pipe_fd,buffer,PIPE_BUF); bytes_write = write(data_fd,buffer,res); bytes_read +=res; }while(res>0); close(pipe_fd); close(data_fd); } else{ exit(EXIT_FAILURE); } printf("process %d finished,%d bytes read\n",getpid(),bytes_read); exit(EXIT_SUCCESS); }
输出结果:
我们在shell中输入命令 ls -l /tmp/my_fifo查看FIFO管道文件的属性
可以看到,FIFO文件生成了,第一个字符‘p’,表示该文件是一个管道文件。
5.多个进程同时写管道
当多个进程同时写管道时,读管道取得的数据是杂乱的。
此时,我们可以控制每个进程,当要写入的数据超过某个大小时,才写管道,另外要以阻塞的方式打开FIFO。确保写操作的原子性。