Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间考到内核缓存区,进程2再内核缓存区把数据读走,内核提供这种机制称为进程间通信(IPC,InterProcess Communication)。
在进程间完成数据传递需要借助操作系统提供的特殊的方法,如:文件、管道、信号、共享内存、消息队列、套接字、命名管道等。随着计算机的蓬勃发展,一些方法由于自身设计缺陷被淘汰或者弃用。现今常用的进程间通信方式有:管道(使用简单),信号(开销最小),共享映射区(无血缘关系),本地套接字(最稳定)
种类
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 资源共享:多个进程之间共享同样的资源。
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止
时要通知父进程)。 - 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另
一个进程的所有陷入和异常,并能够及时知道它的状态改变。
方法
管道(数据传输),共享内存(数据共享),消息队列(数据传输),信号量(进程控制)
目的: 每一个进程都有自己的独立的虚拟地址空间和页表结构,促使进程独立,导致进程之间相互协作工作,从而产生了进程间通信。
匿名管道
内核中的一块内存,内存为间进程间通信而开辟的缓冲区,分为匿名管道,命名管道
匿名管道特性(用fork来共享管道)
- 用于具有亲缘关系的进程
- 管道是一个半双工。数据流向只能是一个流向
- 提供相关字节流服务,若读取没有及时进行,那么写端此时写入的数据会追加在前面数据的后面。数据其实都在二进制存储的没有明确的数据边界,这样读端读到的数据就会出现错误
- 读端读数据会把管道中的数据拿走,而不是存留在管道内存中
- 生命周期随进程:PIPE_SIZE:64K
PIPE_BUF:1024,保证写入数据的原子性(当前操作不会打断)
创建匿名管道
int pipe(int fd[2]); fd[2]:是一个出参,使用该函数,传入int类型大小为2的数组,在函数内部会将数组的值填充,
调用完成后,能拿到读写端,0是读端,1是写端,返回值成功为0失败为-1
#include <stdio.h>
#include <unistd.h>
int main() {
int fd[2];
int ret = pipe(fd); //若没有读写,查看进程中的pipe会闪烁
if(ret < 0){
perror("pipe");
return 0;
}
//fd[0] : 读-文件描述符
//fd[1] : 写-文件描述符
//写
write(fd[1], "ZYF", 3); //系统没有这个文件,写在内存中
char buf[1024] = {
0};
read(fd[0], buf, sizeof(buf) - 1);
printf("buf [%s]\n", buf);
while(1)
sleep(1);
return 0;
}
管道读写规则
- 管道中没有数据,调用read会阻塞,等待写端进行写;
管道中数据满,调用write会阻塞,等待读端进行读。
#include <stdio.h>
#include