Android 筑基 – Linux 系统进程间通信

微信扫一扫,分享到朋友圈

Android 筑基 – Linux 系统进程间通信

未完待续~

进程间通讯算是一个老大难问题,哪哪都有它的身影,面试、开源库、业务组件、功能模块,今天给小白们科普一下

┗|`O′|┛ 嗷~~ 奥利给 ~~ 加其油来 ~~ 你还看的动!!!

Linux 文件系统

在说其他之前,必须先看 Linux 的 File 文件系统,简单说一下:

Linux 系统中,只有内核才有影响的机器指令权限直接操作硬件,所以所有想要操作硬件的操作都会被中转到系统内核中执行,但后再返回结果给用户进程

具体到 File 也是一样,进程A想要打开一个 File文件 open("./myFile.txt") ,那么该操作会通过系统服务API被转移到内核中执行,内核会在内核空间产生一个文件结构体,文件结构体内部有个缓冲区,用来存放文件内容,你读写文件都是读写这个内核中的缓冲区

Linux 7种文件类型:

文件类型 符号 对应方法
普通文件 _ open()
目录文件 d mkdir()
链接文件 l ln-s
管道文件 p mkfifo()/pipe()
套接字文件 s
字符设备文件 c
块设备文件

其中: 有名管道、字符设备、块设备、套接字 都只有文件节点,不占磁盘空间

进程间为什么不能直接通信


因为每一个进程都有自己的、单独的、虚拟内存地址,即便进程A、B中相同的虚拟内存地址: 0110 ,其纸箱的也是不同的物理地址,实际上数据是不同的

物理地址通过虚拟内存地址被认为分割,相互之间不能访问,数据在进程间不可见,为的就是安全,保护有些进程干坏事,偷窃、篡改、别的进程数据

进程间如何通信

这就需要借助上面说的 File 文件了,大家考虑这种情况: 2个进程打开同一个文件

问:2个进程同时打开同一个文件,获得的文件描述符是不一样的?

答:2个进程打开同一个 File 文件,每个进程获得的文件描述符是不同的,文件描述符是每个进程独有的。但是同一个 File 文件映射到内核中的缓冲区有切只有一份

这样就留出了可操作的余地缓冲区成了一个桥梁,A进程写数据到缓冲区,B进程就可以从缓冲区把这个数据读取出来,进程间通信就是利用的 File 的机制来实现的,这个内核中的 File 对象类型不同,就是不同的进程间通信方式

所以有好多人说 Linux 进程通信都是基于文件 IO 的思路,内核空间是所有进程共享的,这是所有进程通信技术的大前提,总得有一个大家都能访问的中介,要不相互隔离的对象之间是真的无法实现通信的

linux 进程通信方式有如下几种:

  • 管道 -> 使用最简单
    • 无名管道
      • 有名管道
  • 文件
  • 信号 -> 开销最小
  • 共享内存
  • 消息队列
  • Socket 套接字 最稳定

注意 Socket,前面说的都是2个进程在同一个内核中的通信,而 Socket 指的是2个进程在通过2个内核通信,Socket 的2端,客户端、服务端各自都有格子的内核,IPC 实在2个内核之间实现的,本质还是操作内核中的文件结构体

来看看其他的解释:

我们 open() 函数打开或者创建、打开一个文件,内核空间就会开辟一块缓存 buffer,有了这个缓存,用户空间通过 write()、read() 函数就可以往缓存里读写数据,close() 函数就是释放这个缓存。Linux 中所有的 IPC 手段都是基于 IO 文件的,就是 open 函数的形式不一样

进程通信和线程通信区别:

进程间通信:
线程间通信:

管道

管道是个特殊的文件,本质是1个队列,FIFE 先入先出算法,队列的左边是 write() 函数写入数据,队列的右边是 read() 函数读取数据,没有数据时 read() 读取的进程会被阻塞,进入 浅睡眠状态


队列内部储存的是每一次写入的数据,这个模型看着就是 Linux 同步机制中介绍的: 生产者、消费者模型 ,队列2端分别有2个信号量控制写入和读取,达到写入和读取的排他操作

特点:

进程结束,空间释放,管道就不存在了
管道中的东西,读完管道就删除了
管道中没有东西可读,则会阻塞
管道中没有空间可以写了,写操作也会阻塞,进程也会 S 浅睡眠状态

数据的流动是: 进程A用户空间 -> 内核空间 -> 进程B用户空间 ,数据在内存中经历2次拷贝

无名管道

使用: pipe() 函数创建无名管道

  • 参数: 需要传一个2个大小的 int 数组,里面是2文件描述父,一个用来写,一个用来读
  • 返回值: -1 表示管道创建失败
int file[2];
char value[] = "hello world!";
char readBuffer[128] = {0};
// 创建管道
int buffer = pipe(file);
if( buffer < 0 ){
// 创建管道失败
return -1;
}
// 往管道里写入数据
int result = write(file[0],value,sizeof(value));
if( result==-1 ){
// 写入失败
}
// 创建子进程
int pid = fork();
if( pid==0 ){
// 这里是子进程,读取管道中数据
int result = read(file[1],readBuffer,sizeof(readBuffer));
if( result==-1 ){
// 读取失败
}
}
复制代码

pipe 的大小是 64K,可以通过命令查看: ulimit -a ,但是也有说是 4K,反正必须是一页 4K 的倍数

缺点:

  • 只能实现父子进程之间的通信,因为只有父子进程之间才是内存一样,代码连续的,大家看无名管道这里,file[0],file[1] 这要是2个不相关的进程,去哪找 file[0],file[1] 这个对象去

微信扫一扫,分享到朋友圈

Android 筑基 – Linux 系统进程间通信

京东数科科创板上市敲定:刘强东表决权74.77%

上一篇

理论 | 当 Spring Boot 遇上了消息队列......

下一篇

你也可能喜欢

Android 筑基 – Linux 系统进程间通信

长按储存图像,分享给朋友