01. 进程通信简介
进程通信工具分为数据传输工具和共享内存两类。这里我们讨论进程通信工具(IPC
)里面的管道、system V
和共享内存。在理解阶层通信之间,我们先了解用户空间缓冲区和内核空间缓冲区两个概念。
1.1 用户空间缓冲区
存在于用户态的进程用户空间,进程直接读取。用于存储待发送或已接收的数据,减少频繁的系统调用开销。
代码示例:
char buf[1024];
write(fd, buf, sizeof(buf)); // 将用户缓冲区buf数据写到fd指向内核
read(fd, buf, sizeof(buf)); // 从内核读取数据到用户缓冲区
**PS:**可以使用 fnprintf
先写入用户缓冲区,满后再通过 write
系统调用写入内核(后面有提到)。
1.2 内核空间缓冲区
存在于内核态作为管道通信的中转站,操作系统进行管理,使用系统调用访问
02. 管道(pipe)
我们再使用Linux
的ls | wc -l
命令时,会用到 |
这个符号表示的即是一个管道。为了执行这么命令创建了两个进程分别执行ls
和 wc
。
2.1 管道特点
为了通信,一个进程从用户内存向内核内存(进程通信)工具中写入数据,另外一个进程从内核内存读取数据到用户内存,这样便完成了数据的写入和读取,两个过程不能同时进行,且有以下要求:
- 血缘关系:仅适用于有共同祖先(血缘关系)的进程;通常,一个管道由一个进程创建,然后该进程调用
fork()
,此后父、子进程之间就可应用该管道(下方有涉及) - 内核缓冲区:数据通过内核缓冲区传输,默认大小通常为
4KB
- 阻塞机制:当管道为空时,读操作阻塞;当管道满时,写操作阻塞;进程退出,管道释放
- 半双工:数据只能单向流动若需双向通信,需创建两个管道
2.2管道的创建和使用
int pipe(int pipefds[2])
//成功返回0,失败返回-1并且errno报错
pipe()
系统调用创建一个新管道。成功的pipe()
调用会在pipefds数组
中返回两个打开的文件描述符:pipefds[0]
表示读取端,pipefds[1]
表示写入端。可以使用 read()和 write()系统调用来在管道上执行 I/O。一旦向管道写入数据之后立即就能从管道的读取端读取数据。但当管道为空时阻塞read()调用读取的是min
{请求的字节数,当前管道存在字节数}。
2.2.1 匿名管道
父子进程通信过程:
-
父进程创建管道,得到两个⽂件描述符指向管道的两端
-
父进程fork出子进程,⼦进程也有两个⽂件描述符指向同⼀管道
-
子进程关闭
pipefds[0]
读端,父进程关闭pipefds[1]
写端即可实现了进程间通信
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
int pipefds[2] = {0};
int n = pipe(pipefds);
assert(n == 0)
// 0:读取端 1:写入端
pid_t id = fork();
assert(id >= 0) ;
if (id == 0)
{ // 子进程写入
close(pipefds[0]); // 子进程关闭读端
const char *msg = "hello Linux!";
int cnt = 0;
while (1)
{
char buf[1024];
fnprintf(buf, sizeof buf, "child is sauing:%s[%d] ", msg, cnt);
write(pipefds[1], buf, strlen(buf));
sleep(1);
}
exit(0);
}
// 父进程读取
close(pipefds[1]); // 父进程关闭写端
while (1)
{
char buf[1024];
ssize_t s = read(pipefds[0], buf, sizeof(buf) - 1); // 保证从二进制文件读取到用户进程某位空一个\0
if (s == 0)
{ // 意味着子进程退出了
printf("child quit\n");
break;
}
else if (s > 0)
{
buf[s] = 0;
printf("child say to father# %s\n", buffer);
}
else
{
printf("read error\n");
break;
}
}
return 0;
}
父子进程读取和写入分别在文件描述符中fd[3]
和fd[4]
,然后子进程为了写入关闭fd[3]
,父进程为了读取则需要关闭fd[4]
。
2.2.2 命名管道
system V共享内存
即允许一个进程将数据放到进程共享内存块中让其他进程读取这些数据来完成信息交换,此过程不涉及变换状态(内核态和用户态切换),使其速度非常快。
system V共享内存
即允许一个进程将数据放到进程共享内存块中让其他进程读取这些数据来完成信息交换,此过程不涉及变换状态(内核态和用户态切换),使其速度非常快。