管道(pipe):最基本的IPC机制,单向通信
管道是Linux中很重要的一种通信方式,是把一个程序的输出直接连接到另一个程序的输入,常说的管道多是指无名管道,无名管道只能用于具有亲缘关系的进程之间,这是它与有名管道的最大区别。
1、分类:
(1)、管道(无名管道):pipe 管道是用环形队列实现的
#include <unistd.h>
int pipe(int filedes[2]);
pipe 函数:filedes参数传出给用户程序两个文件描述符(filedes[0]指向管道的读端,filedes[1]指向管道的写端)
通过read(filedes[0])或者write(filedes[1]),向这个文件读写数据其实是在读写内核缓冲区。
pipe函数调用成功返回0,调用失败返回-1。
步骤:1.创建管道; 2.fork创建子进程;
3.父子各关闭不需要的进程描述符(可利用read()、write())。
(2)、命名管道(有名管道):FIFO
创建:
#include <sys/types.h>
#include <sys/stat.h>
int mknod(const char *path,mode_t mod,dev_t dev);
int mkfifo(const char *path,mode_t mode);
调用成功都返回0,失败都返回-1
2、管道大小及工作特点:
在Linux中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现为:
限制管道的大小。实际上,管道是一个固定大小的缓冲区。在Linux中,该缓冲区的大小为1页,即4K字节(4096字节),使得它的大小不象文件那样不加检验地增长。
使用单个固定缓冲区也会带来问题。即:管道满时,写阻塞;空时,读阻塞。
注:从管道读数据是一次性操作,数据一旦被读,它就从管道中被抛弃,释放空间以便写更多的数据。
3、管道的环形队列:
管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个片段。
管道的一端连接一个进程的输出,这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。
管道空时,读等待;管道满时,写等待;当两个进程都终结的时候,管道也自动消失。
4、管道的实现方式:
管道:单向通信
创建管道,并执行通信:
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<sys/wait.h> int main() { int _pipe_fd[2]={-1,-1};//create pipe if(pipe(_pipe_fd) < 0) { perror("pipe"); exit(1); } pid_t _pid=fork();//create init if(_pid < 0) { perror("fork"); exit(2); } else if(_pid == 0)//Child { close(_pipe_fd[0]);//close child read int count=10;//child write char buf[]="hello friends"; while(count--) { write(_pipe_fd[1],buf,strlen(buf)); sleep(1); } //close(_pipe_fd[1]);//close child write exit(0);//exit can return and close file by itself } else//Father { close(_pipe_fd[1]);//close father write char buf[1024];//father read while(1) { memset(buf,'\0',sizeof(buf)); ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1); if(_size > 0) { buf[_size]='\0'; printf("%s\n",buf); } else if(_size == 0) { printf("Pipe is empty,child quit\n"); exit(3); } } } return 0; }
运行结果:
(1).如果所有指向管道写端的文件描述符都关闭了(管道写端的引用计数等于0),而仍然有
进程 从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像
读到文件末尾一样。
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<sys/wait.h> int main() { int _pipe_fd[2]={-1,-1};//create pipe if(pipe(_pipe_fd) < 0) { perror("pipe"); exit(1); } pid_t _pid=fork();//create init if(_pid < 0) { perror("fork"); exit(2); } else if(_pid == 0)//Child { close(_pipe_fd[0]);//close child read int count=10;//child write char buf[]="hello friends"; while(count--) { write(_pipe_fd[1],buf,strlen(buf)); sleep(1); } //close(_pipe_fd[1]);//close child write exit(0);//exit can return and close file by itself } else//Father { close(_pipe_fd[1]);//close father write char buf[1024];//father read while(1) { memset(buf,'\0',sizeof(buf)); ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1); if(_size > 0) { buf[_size]='\0'; printf("%s\n",buf); } else if(_size == 0) { printf("Pipe is empty,child quit\n"); exit(3); } } } return 0; }
运行结果:
(2).如果有指向管道写端的文件描述符没关闭(管道写端的引用计数大于0),而持有管道写
端的 进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数
据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<sys/wait.h> int main() { int _pipe_fd[2]={-1,-1};//create pipe if(pipe(_pipe_fd) < 0) { perror("pipe"); exit(1); } pid_t _pid=fork();//create init if(_pid < 0) { perror("fork"); exit(2); } else if(_pid == 0)//Child { close(_pipe_fd[0]);//close child read int count=10;//child write char buf[]="hello friends"; while(count) { if(count > 5)//write not close all { write(_pipe_fd[1],buf,strlen(buf)); } sleep(1); count--; } //close(_pipe_fd[1]);//close child write exit(0);//exit can return and close file by itself } else//Father { close(_pipe_fd[1]);//close father write char buf[1024];//father read while(1) { memset(buf,'\0',sizeof(buf)); ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1); if(_size > 0) { buf[_size]='\0'; printf("%s\n",buf); } else if(_size == 0) { printf("Pipe is empty,child quit\n"); exit(3); } } } return 0; }
运行结果:
(3).如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数等于0),这时有进
程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<sys/wait.h> int main() { int _pipe_fd[2]={-1,-1};//create pipe if(pipe(_pipe_fd) < 0) { perror("pipe"); exit(1); } pid_t _pid=fork();//create init if(_pid < 0) { perror("fork"); exit(2); } else if(_pid == 0)//Child { close(_pipe_fd[0]);//close child read int count=10;//child write char buf[]="hello friends"; while(count--) { write(_pipe_fd[1],buf,strlen(buf)); sleep(1); } //close(_pipe_fd[1]);//close child write exit(0);//exit can return and close file by itself } else//Father { close(_pipe_fd[1]);//close father write char buf[1024];//father read while(1) { if(count==0) { close(_pipe_fd[0]);//close father read break; } memset(buf,'\0',sizeof(buf)); ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1); if(_size > 0) { buf[_size]='\0'; printf("%s\n",buf); } else if(_size == 0) { printf("Pipe is empty,child quit\n"); exit(3); } } int status=0; if(waitpid(_pid,&status,0) == _pid) { printf("code; %s sig: %s\n",(status >> 8)& 0xFF,status & 0xFF); } } return 0; }
运行结果;
(4).如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0),而持有管道读
端的 进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时
再 次write会阻塞,直到管道中有空位置了才写入数据并返回。
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<sys/wait.h> int main() { int _pipe_fd[2]={-1,-1};//create pipe if(pipe(_pipe_fd) < 0) { perror("pipe"); exit(1); } pid_t _pid=fork();//create init if(_pid < 0) { perror("fork"); exit(2); } else if(_pid == 0)//Child { close(_pipe_fd[0]);//close child read int count=10;//child write char buf[]="hello friends"; while(count--) { write(_pipe_fd[1],buf,strlen(buf)); sleep(1); } //close(_pipe_fd[1]);//close child write exit(0);//exit can return and close file by itself } else//Father { close(_pipe_fd[1]);//close father write char buf[1024];//father read while(1) { memset(buf,'\0',sizeof(buf)); ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1); if(_size > 0) { buf[_size]='\0'; printf("%s\n",buf); } else if(_size == 0) { printf("Pipe is empty,child quit\n"); exit(3); } } int status=0; if(waitpid(_pid,&status,0) == _pid) { printf("code; %s sig: %s\n",(status >> 8)& 0xFF,status & 0xFF); } } return 0; }
运行结果: