管道通信(上)
(一)概述
Linux Shell 都允许重定向,而重定向使用的就是管道。例如,ps | grep vsftpd 。管道是单向的、先进先出的、无结构的、固定大小的字节流。管道是Linux由Unix那里继承过来的进程间的通信机制,它是Unix早期的一个重要通信机制。其思想是,在内存中创建一个共享文件,从而使通信双方利用这个共享文件来传递信息。由于这种方式具有单向传递数据的特点,所以这个作为传递消息的共享文件就叫做“管道”。它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的读端读出数据。数据读出之后将从管道中移走,其他读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道之前,进程将一直阻塞。同样,管道已经满时,进程在试图写管道,在其他进程从管道中移走数据之前,写进程将一直阻塞。
(二)匿名管道
匿名管道是具有共同祖先的进程之间的一种通信方式,因此通常用于父子进程或者兄弟进程之间的通信,在由父进程创建的子进程将会赋值父进程包括文件在内的一些资源。如果父进程创建子进程之前创建了一个文件,那么这个文件的描述符就会被父进程在随后所创建的子进程所共享。也就是说,父、子进程可以通过这个文件进行通信。
参数:长度为2的整型数组
返回值:成功返回0,失败返回-1,errno会被设置,可以通过perror打印错误信息。
函数使用示例:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<unistd.h> 5 int main(int argc, char const *argv[]) 6 { 7 int fd_pipe[2]; 8 if(pipe(fd_pipe) == -1) 9 { 10 perror("pipe"); 11 exit(1); 12 } 13 if(write(fd_pipe[1],"hello pipe",strlen("hello pipe")) == -1) 14 { 15 perror("write"); 16 exit(1); 17 } 18 char buf[128] = ""; 19 if(read(fd_pipe[0],buf,sizeof(buf)) == -1) 20 { 21 perror("read"); 22 exit(1); 23 } 24 printf("%s\n",buf); 25 close(fd_pipe[1]); 26 close(fd_pipe[0]); 27 return 0; 28 }
运行结果:
父子进程之间通信示例:父进程从终端读取内容,并将内容发送给子进程。
1 #include<unistd.h> 2 #include<stdio.h> 3 #include<string.h> 4 #include<stdlib.h> 5 int main(int argc, char const *argv[]) 6 { 7 int fd_pipe[2]; 8 pid_t pid; 9 if(pipe(fd_pipe) == -1) 10 { 11 perror("fail to pipe"); 12 exit(1); 13 } 14 if((pid = fork()) == -1) 15 { 16 perror("Fail to fork"); 17 exit(1); 18 } 19 else if (pid > 0) 20 { 21 char buf[20] = ""; 22 while (1) 23 { 24 if(read(STDIN_FILENO,buf,sizeof(buf)) == -1) 25 { 26 perror("Fail to read from std"); 27 exit(1); 28 } 29 buf[strlen(buf) - 1] = '\0'; 30 if(write(fd_pipe[1],buf,sizeof(buf)) == -1) 31 { 32 perror("Fail to write to pipe:"); 33 exit(1); 34 } 35 memset(buf,0,sizeof(buf)); 36 } 37 } 38 else if (pid == 0) 39 { 40 char buf[128] = ""; 41 while (1) 42 { 43 if(read(fd_pipe[0],buf,1024) == -1) 44 { 45 perror("Fail to read:"); 46 exit(1); 47 } 48 printf("read from parent by pipe message is %s\n",buf); 49 memset(buf,0,sizeof(buf)); 50 } 51 52 } 53 return 0; 54 }
运行结果:
(三)匿名管道读写规律
(1)只读不写——当管道有内容时,读取内容,无内容时会进入阻塞态,当我面修改属性为非阻塞时,程序会直接结束。
代码:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<unistd.h> 4 #include<string.h> 5 #include<fcntl.h> 6 int main(int argc, char const *argv[]) 7 { 8 int fd_pipe[2]; 9 if(pipe(fd_pipe) == -1) 10 { 11 perror("fail to pipe"); 12 exit(1); 13 } 14 char buf[128] = ""; 15 if(write(fd_pipe[1],"hello pipe",strlen("hello pipe")) == -1) 16 { 17 perror("write"); 18 exit(1); 19 } 20 if(read(fd_pipe[0],buf,sizeof(buf)) == -1) 21 { 22 perror("fail to read"); 23 exit(1); 24 } 25 printf("%s\n",buf); 26 memset(buf,0,sizeof(buf)); 27 printf("Read again\n"); 28 if(read(fd_pipe[0],buf,sizeof(buf)) == -1) 29 { 30 perror("fail to read"); 31 exit(1); 32 } 33 printf("%s\n",buf); 34 return 0; 35 }
运行结果:
我们首先在管道内写点数据,进程可以正常的从管道中读取数据,读完之后,管道内没有数据了,此时当我们再读的时候,就会进入阻塞态,我们再试一下,用fcntl使管道为非阻塞,看看会有怎样的效果。
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<unistd.h> 4 #include<string.h> 5 #include<fcntl.h> 6 int main(int argc, char const *argv[]) 7 { 8 int fd_pipe[2]; 9 if(pipe(fd_pipe) == -1) 10 { 11 perror("fail to pipe"); 12 exit(1); 13 } 14 char buf[128] = ""; 15 fcntl(fd_pipe[0],F_SETFL,O_NONBLOCK); 16 if(write(fd_pipe[1],"hello pipe",strlen("hello pipe")) == -1) 17 { 18 perror("write"); 19 exit(1); 20 } 21 if(read(fd_pipe[0],buf,sizeof(buf)) == -1) 22 { 23 perror("fail to read"); 24 exit(1); 25 } 26 printf("%s\n",buf); 27 memset(buf,0,sizeof(buf)); 28 printf("Read again\n"); 29 if(read(fd_pipe[0],buf,sizeof(buf)) == -1) 30 { 31 perror("fail to read"); 32 exit(1); 33 } 34 printf("%s\n",buf); 35 return 0; 36 }
(2)只写不读——管道写满之后就会进入阻塞态
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<unistd.h> 4 #include<string.h> 5 #include<fcntl.h> 6 int main(int argc, char const *argv[]) 7 { 8 int fd_pipe[2]; 9 if(pipe(fd_pipe) == -1) 10 { 11 perror("fail to pipe"); 12 exit(1); 13 } 14 if(write(fd_pipe[1],"hello pipe",strlen("hello pipe")) == -1) 15 { 16 perror("write"); 17 exit(1); 18 } 19 printf("write successful\n"); 20 if(write(fd_pipe[1],"hello pipe",strlen("hello pipe")) == -1) 21 { 22 perror("write"); 23 exit(1); 24 } 25 printf("write successful\n"); 26 if(write(fd_pipe[1],"hello pipe",strlen("hello pipe")) == -1) 27 { 28 perror("write"); 29 exit(1); 30 } 31 printf("write successful\n"); 32 close(fd_pipe[0]); 33 close(fd_pipe[1]); 34 return 0; 35 }
我们看到,三次都写成功了,但可以一直写吗?而且管道是有大小的,我们可以写多少呢?
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<unistd.h> 4 #include<string.h> 5 #include<fcntl.h> 6 int main(int argc, char const *argv[]) 7 { 8 int fd_pipe[2]; 9 if(pipe(fd_pipe) == -1) 10 { 11 perror("fail to pipe"); 12 exit(1); 13 } 14 int count = 0; 15 while (1) 16 { 17 char buf[1024] = ""; 18 if(write(fd_pipe[1],buf,sizeof(buf)) == -1) 19 { 20 perror("write"); 21 exit(1); 22 } 23 count++; 24 printf("count = %d\n",count); 25 sleep(1); 26 } 27 28 close(fd_pipe[0]); 29 close(fd_pipe[1]); 30 return 0; 31 }
从程序中不难看出,管道是有大小的,当我们写满之后,就会进入阻塞态。
(3)只有读端——程序会直接结束
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<unistd.h> 4 #include<string.h> 5 #include<fcntl.h> 6 int main(int argc, char const *argv[]) 7 { 8 int fd_pipe[2]; 9 if(pipe(fd_pipe) == -1) 10 { 11 perror("fail to pipe"); 12 exit(1); 13 } 14 close(fd_pipe[1]); 15 char buf[128] = ""; 16 if(read(fd_pipe[0],buf,sizeof(buf)) == -1) 17 { 18 perror("read"); 19 exit(1); 20 } 21 printf("%s\n",buf); 22 close(fd_pipe[0]); 23 return 0; 24 }
按道理来讲,此时读管道应该进入阻塞态的,但由于我们关闭了写端,所以程序就直接结束了。
(4)只有写端——管道会破裂
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<unistd.h> 4 #include<string.h> 5 #include<fcntl.h> 6 int main(int argc, char const *argv[]) 7 { 8 int fd_pipe[2]; 9 if(pipe(fd_pipe) == -1) 10 { 11 perror("fail to pipe"); 12 exit(1); 13 } 14 close(fd_pipe[0]); 15 if(write(fd_pipe[1],"hello pipe",strlen("hello pipe")) == -1) 16 { 17 perror("write"); 18 exit(1); 19 } 20 printf("write successful\n"); 21 close(fd_pipe[1]); 22 return 0; 23 }
我们运行发现程序直接结束了,其实这里大家要知道一点,就是只有写端的时候,我们写内容时是有一个SIGPIPE信号产生的,表明管道破裂了,我们可以通过一个函数——signal()来看。
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<unistd.h> 4 #include<string.h> 5 #include<fcntl.h> 6 #include<signal.h> 7 void hander(int sig) 8 { 9 printf("SIGPIPE occurrenced\n"); 10 exit(1); 11 } 12 int main(int argc, char const *argv[]) 13 { 14 signal(SIGPIPE,hander); 15 int fd_pipe[2]; 16 if(pipe(fd_pipe) == -1) 17 { 18 perror("fail to pipe"); 19 exit(1); 20 } 21 close(fd_pipe[0]); 22 if(write(fd_pipe[1],"hello pipe",strlen("hello pipe")) == -1) 23 { 24 perror("write"); 25 exit(1); 26 } 27 printf("write successful\n"); 28 close(fd_pipe[1]); 29 return 0; 30 }