Linux 管道
1.管道的基本概念
管道是一种两个进程间进行单向通信的机制。当一个进程连接数据流到另一个进程时,我们使用术语管道(pipe)。
通常是把一个进程的输出通过管道连接到另外一个进程的输入。
2.pipe
linux下管道由pipe系统调用函数创建,提供一个单向数据流。首先看一下它的原型
#include <unistd.h> int pipe(int fildes[2]);
函数参数为一个由两个整数类型的文件描述符组成的数组的指针,保存两个文件描述符。管道创建后,fildes[0]打开来读,fildes[1]打开来写。
创建成功函数返回0,失败返回-1并设置errno来表明失败的原因.
两个返回的文件描述符以一种特殊的方式连接起来,写到fildes[1]的所有数据都可以从fildes[0]读回来。数据基于先进先出的原则。所以读到的
数据流顺序就是写入时的顺序。
注意:我们操作管道时不时直接操作文件流,而是操作文件描述符。
管道的优势在于可以在可以在两个进程之间传递数据,我们看一个父子进程通信的例子。
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main() { int data_processed; int file_pipes[2]; const char some_data[] = "123"; char buffer[BUFSIZ + 1]; pid_t fork_result; memset(buffer,'\0',sizeof(buffer)); if(pipe(file_pipes) == 0) { fork_result = fork(); if(fork_result == -1) { fprintf(stderr,"Fork failure"); } if(fork_result == 0) { data_processed = read(file_pipes[0],buffer,BUFSIZ); printf("Read %d bytes: %bs\n",data_processed,buffer); exit(EXIT_SUCCESS); } else { data_processed = write(file_pipes[1],some_data,strlen(some_data)); printf("Wrote %d bytes\n",data_processed); } } exit(EXIT_SUCCESS); }
该例来自于《Linux程序设计》13章第5节
执行./pipe
Wrote 3 bytes
Read 3 bytes: 123
3.管道与exec函数
管道用于在两个进程之间进行单向通信,但是不能只限于父子间进程,我们使用exec函数替换子进程,实现任意进程
之间的管道通信。
//write_fork.c #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main() { int data_processed; int file_pipes[2]; const char *some_data = "123"; char buffer[BUFSIZ+1]; pid_t fork_result; memset(buffer,'\0',sizeof(buffer)); if(pipe(file_pipes) == 0) { fork_result = fork(); if(fork_result == (pid_t)-1) { fprintf(stderr,"Fork failure"); exit(EXIT_FAILURE); } if(fork_result == 0) { sprintf(buffer,"%d",file_pipes[0]); (void)execl("read_fork","read_fork",buffer,(char*)0); exit(EXIT_FAILURE); } else { data_processed = write(file_pipes[1],some_data, strlen(some_data)); printf("%d - wrote %d bytes\n",getpid(),data_processed); } } exit(EXIT_SUCCESS); }
//read_fork.c #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main(int argc,char *argv[]) { int data_processed; char buffer[BUFSIZ + 1]; int file_descriptor; memset(buffer,'\0',sizeof(buffer)); sscanf(argv[1],"%d",&file_descriptor); data_processed = read(file_descriptor,buffer,BUFSIZ); printf("%d - read %d bytese: %s\n",getpid(),data_processed,buffer); exit(EXIT_SUCCESS); }
在进程read_fork中进程读操作,在write_fork中,将子进程替换为read_fork。
在write_fork进程中,将数据写入到fildes[1]中,将fildes[0]作为参数传给read_fork
的main函数的参数,在read_fork中,读出管道中的数据。
4.管道的读操作
通常读取读取数据的程序并不知道有多少数据需要读取,所以常采用循环的方法读取数据,
当没有数据可以读取时,read调用通常会阻塞,即3他将暂停进程来等待直到有数据到达为止。