pipe函数

管道的概念

管道是一种最基本的IPC机制,也称匿名管道,应用于有血缘关系的进程之间,完成数据传递。调用pipe函数即可创建一个管道。

img

有如下特质:

管道的本质是一块内核缓冲区

由两个文件描述符引用,一个表示读端,一个表示写端。

规定数据从管道的写端流入管道,从读端流出。

当两个进程都终结的时候,管道也自动消失。

管道的读端和写端默认都是阻塞的。

管道的原理

管道的实质是内核缓冲区,内部使用环形队列实现。

默认缓冲区大小为4K,可以使用ulimit -a命令获取大小。

实际操作过程中缓冲区会根据数据压力做适当调整。

管道的局限性

数据一旦被读走,便不在管道中存在,不可反复读取。

数据只能在一个方向上流动,若要实现双向流动,必须使用两个管道

只能在有血缘关系的进程间使用管道。

总结:

1.管道的本质是一块内核缓冲区,内部的实现是环形队列

2.管道有读写两端,读写两端是两个文件描述符

3.数据的流向是从管道的写端流到管道的读端(数据的流向是单向的)

4.数据被读走之后,在管道中就消失

5.pipe只能用于有血缘关系的进程间通信

6.管道的读写两端是阻塞的(写满数据阻塞,没读到数据阻塞)

7.管道的大小默认是4K,但是会根据实际情况做适当调整

创建管道pipe函数

函数作用:

创建一个管道

头文件

1
#include <unistd.h>

函数原型:

1
int pipe(int fd[2]);

函数参数:

若函数调用成功,fd[0]存放管道的读端,fd[1]存放管道的写端

返回值:

成功返回0;

失败返回-1,并设置errno值。

​ 函数调用成功返回读端和写端的文件描述符,其中fd[0]是读端, fd[1]是写端向管道读写数据是通过使用这两个文件描述符进行的,读写管道的实质是操作内核缓冲区。

管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢?

父子进程使用管道通信

一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在血缘关系,这里的血缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)。父子进程间具有相同的文件描述符,且指向同一个管道pipe,其他没有关系的进程不能获得pipe()产生的两个文件描述符,也就不能利用同一个管道进行通信。

第一步:父进程创建管道

img

第二步:父进程fork出子进程

img

第三步:父进程关闭fd[0],子进程关闭fd[1]

img

创建步骤总结:

父进程调用pipe函数创建管道,得到两个文件描述符fd[0]和fd[1],分别指向管道的读端和写端。

父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管。

父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出,这样就实现了父子进程间通信。

pipe用于父子进程间通信:

1.父进程创建pipe

2.父进程调用fork函数创建子进程

3.父进程关闭一端

4.子进程关闭一端

5.父进程和子进程分别执行read或者write操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
//创建管道
//int pipe(int pipefd[2]);
int fd[2];
int ret = pipe(fd);
/*子进程会复制一份fd,然后内核计数读和写fd各变成2*/
if(ret<0)
{
perror("pipe error");
return -1;
}

//创建子进程
pid_t pid = fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(pid>0)
{
//关闭读端
close(fd[0]);
sleep(5);
write(fd[1], "hello world", strlen("hello world"));

wait(NULL);
}
else
{
//关闭写端
close(fd[1]);

char buf[64];
memset(buf, 0x00, sizeof(buf));
int n = read(fd[0], buf, sizeof(buf));
printf("read over, n==[%d], buf==[%s]\n", n, buf);

}

return 0;
}