管道由pipe函数创建
#include<unistd.h>
int pipe(int filedes[2]);
调用pipe函数在内核中开辟一块缓冲区(就是管道)用于通信,filedes[0]指向管道的读端,filedes[1]指向管道的写端。pipe函数调用成功返回0,调用失败返回-1。
比如,父进程关闭读端,子进程关闭写端。代码如下:
1 #include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5 #include<stdlib.h>
6 int main()
7 {
8 int _pipe_fd[2]={-1,-1};
9 if(pipe(_pipe_fd)<0)
10 {
11 perror("pipe");
12 exit(1);
13 }
14 pid_t _pid=fork();
15 if(_pid<0)
16 {
17 perror("fork");
18 exit(2);
19 }
20 else if(_pid==0)
21 {
22 close(_pipe_fd[0]);
23 int count=10;
24 char buf[]="hello world";
25 while(count--)
26 {
27 write(_pipe_fd[1],buf,strlen(buf));
28 sleep(1);
29 }
30 exit(0);
31 }
32 else
33 {
34 close(_pipe_fd[1]);
35 char buf[1024];
36 while(1)
37 {
38 memset(buf,'\0',sizeof(buf));
39 ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1);
40 if(_size>0)
41 {
42 buf[_size]='\0';
43 printf("%s\n",buf);
44 }
45 }
46 }
47 }
运行结果:
使用管道有限制:两个进程通过一个管道只能实现单向通信。
使用管道需要注意以下四种情况:
1.如果所有指向管道写端的文件描述符都关闭了(管道写端的引用计数为0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。
代码如下:
1 #include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5 #include<stdlib.h>
6 int main()
7 {
8 int _pipe_fd[2]={-1,-1};
9 if(pipe(_pipe_fd)<0)
10 {
11 perror("pipe");
12 exit(1);
13 }
14 pid_t _pid=fork();
15 if(_pid<0)
16 {
17 perror("fork");
18 exit(2);
19 }
20 else if(_pid==0)
21 {
22 close(_pipe_fd[0]);
23 int count=10;
24 int i=0;
25 char buf[]="hello world";
26 while(count--)
27 {
28 if(i==5)
29 {
30 printf("I want to sleep");
31 break;
32 }
33 write(_pipe_fd[1],buf,strlen(buf));
34 sleep(1);
35 i++;
36 }
37 close(_pipe_fd[1]);
38 }
39 else
40 {
41 close(_pipe_fd[1]);
42 char buf[1024];
43 while(1)
44 {
45 memset(buf,'\0',sizeof(buf));
46 ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1);
47 if(_size>0)
48 {
49 buf[_size]='\0';
50 printf("%s\n",buf);
51 }
52 }
53 }
54 }
运行结果:
2.如果有指向管道写端的文件描述符没有关闭(管道写端的引用计数大于0);而管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,当管道中剩余的数据都被读取后,再次read会阻塞,只有管道中有数据可读了才读取数据并返回。
代码如下:
1 #include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5 #include<stdlib.h>
6 int main()
7 {
8 int _pipe_fd[2]={-1,-1};
9 if(pipe(_pipe_fd)<0)
10 {
11 perror("pipe");
12 exit(1);
13 }
14 pid_t _pid=fork();
15 if(_pid<0)
16 {
17 perror("fork");
18 exit(2);
19 }
20 else if(_pid==0)
21 {
22 close(_pipe_fd[0]);
23 int count=10;
24 int i=0;
25 char buf[]="hello world";
26 while(count--)
27 {
28 if(i==5)
29 {
30 printf("I am child\n");
31 sleep(10);
32 }
33 write(_pipe_fd[1],buf,strlen(buf));
34 sleep(1);
35 i++;
36 }
37 exit(0);
38 close(_pipe_fd[1]);
39 }
40 else
41 {
42 close(_pipe_fd[1]);
43 char buf[1024];
44 while(1)
45 {
46 memset(buf,'\0',sizeof(buf));
47 ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1);
48 if(_size>0)
49 {
50 buf[_size]='\0';
51 printf("%s\n",buf);
52 }
运行结果:
3.如果指向所有管道读端的文件描述符都关闭了(管道读端的文件描述符为0),当有进程向管道的写端写数据时,该进程会收到信号SIGPIPE,通常会导致进程异常终止。
代码如下:
1 #include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5 #include<stdlib.h>
6 int main()
7 {
8 int _pipe_fd[2]={-1,-1};
9 if(pipe(_pipe_fd)<0)
10 {
11 perror("pipe");
12 exit(1);
13 }
14 pid_t _pid=fork();
15 if(_pid<0)
16 {
17 perror("fork");
18 exit(2);
19 }
20 else if(_pid==0)
21 {
22 close(_pipe_fd[0]);
23 int count=10;
24 char buf[]="hello world";
25 while(count--)
26 {
27 write(_pipe_fd[1],buf,strlen(buf));
28 sleep(1);
29 }
30 close(_pipe_fd[1]);
31 }
32 else
33 {
34 close(_pipe_fd[1]);
35 char buf[1024];
36 int j=5;
37 while(j--)
38 {
39 if(j==3)
40 {
41 close(_pipe_fd[0]);
42 }
43 memset(buf,'\0',sizeof(buf));
44 ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1);
45 if(_size>0)
46 {
47 buf[_size]='\0';
48 printf("%s\n",buf);
49 }
50 }
51 int status=0;
52 pid_t _id=waitpid(_pid,&status,0);
53 printf("waitpid:%d,status:%d\n",_id,status&0xff);
54 }
55 return 0;
56 }
运行结果:
4.如果有指向管道读端的文件描述符没有关闭(管道写端的引用计数大于0),而管道读端的进程也没有从管道中读数据,这时有进程从管道写端写数据,在管道被写满时,再次写会阻塞,只有管道中有空位置了才写入数据并返回。
代码如下:
1 #include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5 #include<stdlib.h>
6 int main()
7 {
8 int _pipe_fd[2]={-1,-1};
9 if(pipe(_pipe_fd)<0)
10 {
11 perror("pipe");
12 exit(1);
13 }
14 pid_t _pid=fork();
15 if(_pid<0)
16 {
17 perror("fork");
18 exit(2);
19 }
20 else if(_pid==0)
21 {
22 close(_pipe_fd[0]);
23 int count=10;
24 char buf[]="hello world";
25 while(count--)
26 {
27 write(_pipe_fd[1],buf,strlen(buf));
28 sleep(1);
29 }
30 close(_pipe_fd[0]);
31 }
32 else
33 {
34 close(_pipe_fd[1]);
35 int status=0;
36 pid_t id=waitpid(_pid,&status,0);
37 if(id==_pid)
38 {
39 printf("id:%d,status:%d",id,status);
40 }
41
42 }
43 }
运行结果:
总结:匿名管道有以下特点:
(1)匿名管道提供的通信是单向的;
(2)匿名管道通信用于有关系的进程间,通常用于父子进程之间(命名管道将针对于这一问题进行解决);
(3)匿名管道能够实现进程间的同步与互斥;
(4)匿名管道的生命周期随进程。