管道读写规则:
1、当没有数据可读时
O_NONBLOCK disable(文件状态标志)未设置非阻塞的话 : read调用阻塞,即进程暂停执行,一直等到有数据来。
O_NONBLOCK enable: 设置非阻塞模式的话,无数据可读时,read调用返回-1,errno值为EAGAIN。
总之:阻塞模式的话,如果read无数据可读就阻塞;如果非阻塞模式的话,read无数据可读,则立即返回-1.
int fcntl(int fd,int cmd) cmd=F_GETFLF_SETFL: 获取或者设置文件状态标志
1 #include<unistd.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 #include<stdlib.h> 6 #include<stdio.h> 7 #include<errno.h> 8 #include<string.h> 9 10 #include<signal.h> 11 #define ERR_EXIT(m) 12 do 13 { 14 perror(m); 15 exit(EXIT_FAILURE); 16 }while(0) //宏要求一条语句 17 int main(int argc,char*argv[]) 18 { 19 int pipefd[2]; 20 if(pipe(pipefd)==-1) 21 ERR_EXIT("pipe error"); 22 pid_t pid; 23 if((pid=fork())==-1) 24 ERR_EXIT("fork error"); 25 else if(pid==0){//子进程发送数据给父进程 26 sleep(3);//未设置文件状态标识时,文件对read阻塞,前三秒没有数据可读 27 close(pipefd[0]); 28 write(pipefd[1],"hello",5); 29 close(pipefd[1]); 30 exit(EXIT_SUCCESS); 31 } 32 close(pipefd[1]); 33 char buf[10]; 34 int flags=fcntl(pipefd[0],F_GETFL);//获取文件状态标识 35 fcntl(pipefd[0],F_SETFL,flags|O_NONBLOCK);//设置文件状态标识,非阻塞模式 36 if(read(pipefd[0],buf,10)==-1) ERR_EXIT("read error");//默认文件pipefd[0]阻塞 37 printf("buf=%s ",buf); 38 close(pipefd[0]); 39 return 0; 40 }
2、如果所有管道写端对应的文件描述符被关闭,则read返回0。
1 #include<unistd.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 #include<stdlib.h> 6 #include<stdio.h> 7 #include<errno.h> 8 #include<string.h> 9 10 #include<signal.h> 11 #define ERR_EXIT(m) 12 do 13 { 14 perror(m); 15 exit(EXIT_FAILURE); 16 }while(0) //宏要求一条语句 17 int main(int argc,char*argv[]) 18 { 19 int pipefd[2]; 20 if(pipe(pipefd)==-1) 21 ERR_EXIT("pipe error"); 22 pid_t pid; 23 if((pid=fork())==-1) 24 ERR_EXIT("fork error"); 25 else if(pid==0){ 26 close(pipefd[1]);//子进程写端关闭 27 exit(EXIT_SUCCESS); 28 } 29 close(pipefd[1]);//父进程写端关闭 30 sleep(1); 31 char buf[10]={0}; 32 int ret=read(pipefd[0],buf,10);//读操作返回0 33 printf("ret=%d ",ret); 34 return 0; 35 }
3、如果所有管道读端对应的文件描述符被关闭,则write操作符产生信号SIGPIPE.
#include<unistd.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<stdlib.h> #include<stdio.h> #include<errno.h> #include<string.h> #include<signal.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0) //宏要求一条语句 void handler(int sig)
{
printf("recv a sig=%d ",sig);
}
int main(int argc,char*argv[]) {
signal(SIGPIPE,handler); int pipefd[2]; if(pipe(pipefd)==-1) ERR_EXIT("pipe error"); pid_t pid; if((pid=fork())==-1) ERR_EXIT("fork error"); else if(pid==0){//子进程发 close(pipefd[0]); //关闭子进程读端 exit(EXIT_SUCCESS); } close(pipefd[0]); //关闭父进程读端 sleep(1); int ret= write(pipfd[1],"hello",5);
if(ret == -1)
printf("write error"); //捕捉信号的话会执行到这,否则默认处理函数之间结束进程 return 0; }
4、当管道满的时候(不断往管道写入),write函数也有两种可能:
如果文件状态标志(O_NONBLOCK disable)阻塞(未设置),write调用阻塞;
文件状态标志(O_NONBLOCK)非阻塞, write返回-1 errno=EAGAIN.
同时这个例子还可以获取管道内核缓冲区的大小。
#include<unistd.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<stdlib.h> #include<stdio.h> #include<errno.h> #include<string.h> #include<signal.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0) //宏要求一条语句 int main(int argc,char*argv[]) { int pipefd[2]; if(pipe(pipefd)==-1) ERR_EXIT("pipe error"); int ret; int count=0; int flags=(pipefd[1],F_GETFL); fcntl(pipefd[1],F_SETFL,flags|O_NONBLOCK);//改为非阻塞,写满的话再写就会出错。默认阻塞,写满write就会阻塞 while(1) { ret=write(pipefd[1],"a",1);//一个一个写会阻塞,若一下子写的超过64k要写完才返回。 if(ret==-1){
printf("err=%s ",strerror(errno)); break;
} count++; } printf("count=%d ",count);//65536字节.管道容量64k,管道容量不等于PIPE_BUF的大小,PIPE_BUF一般4k return 0; }
5、当要写入的数据量不大于PIPE_BUF时,LINUX将保证写入的原子性,指多个进程向管道写入数据,各个进程数据连续,不被插入其他进程数据;当要写入的数据量大于PIPE_BUF时,LINUX将不再保证写入原子性。可能会被其他进程插入数据。PIPE_BUF一般为4KB。
阻塞模式并且n<=PIPE_BUF:所有数据原子性写入。可以打印出来,在头文件<linux/limits.h>中。不一定等同于管道容量;
非阻塞模式并且n<=PIPE_BUF:若空间不够,失败返回,不写入字符;
阻塞模式并且n>PIPEBUF:不保证原子性,直到n个字节全部被写完才返回;
非阻塞模式且n>PIPEBUF:失败同样不写入;若可以但不保证原子性。
1 #include<unistd.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 #include<stdlib.h> 6 #include<stdio.h> 7 #include<errno.h> 8 #include<string.h> 9 10 #include<linux/limits.h> 11 #define TEST_SIZE 68*1024 12 #define ERR_EXIT(m) 13 do 14 { 15 perror(m); 16 exit(EXIT_FAILURE); 17 }while(0) //宏要求一条语句 18 int main(int argc,char*argv[]) 19 { 20 printf("pipe_buf=%d ",PIPE_BUF); 21 int ret; 22 char a[TEST_SIZE];//68KB 23 char b[TEST_SIZE]; 24 memset(a,'A',sizeof(a)); 25 memset(b,'B',sizeof(b)); 26 int pipefd[2]; 27 if(pipe(pipefd)==-1) 28 ERR_EXIT("pipe error"); 29 30 pid_t pid; 31 //创建第一个子进程 32 pid=fork(); 33 if(pid==0) 34 { 35 close(pipefd[0]);//关闭读端 36 ret=write(pipefd[1],a,sizeof(a));//往管道写端写入68K数据。管道64k 37 printf("apid=%d write %d bytes to pipe ",getpid(),ret); 38 exit(0); 39 } 40 //再创建一个子进程,写入68k b 41 pid=fork(); 42 if(pid==0) 43 { 44 close(pipefd[0]); 45 ret=write(pipefd[1],b,sizeof(b));//写完68k才返回。管道大小64K. 46 printf("bpid=%d write%d bytes to pipe ",getpid(),ret); 47 exit(0); 48 } 49 //父进程 50 close(pipefd[1]); 51 sleep(1);//使子进程先到write() 52 int fd=open("test.txt",O_WRONLY|O_CREAT|O_TRUNC,0644); 53 char buf[1024*4]={0};//每次接收4k数据 54 int n=1; 55 while(1) 56 { 57 ret=read(pipefd[0],buf,sizeof(buf));//每次接收4k数据 58 if(ret==0) 59 break;//子进程都发完数据了,管道写端都关闭了。跳出循环 60 //没发完的话,每次收到4k,打印最后一个字符。会有穿插,68k远远超出PIPE_BUF 61 printf("n=%02d pid=%d read %d bytes from pipe buf[4095=]%c ",n++,getpid(),ret,buf[4095]);//打印输出收到的数据 62 write(fd,buf,ret);//每次4k写入文件。 63 } 64 return 0; 65 }