Linux中的进程通信方式
- 进程间,用户空间的数据不可共享,所以用户空间相当于私有空间
- 进程间,内核空间的数据可共享,所以内核空间 相当于公共空间
进程间如果需要做到通信,需要通过共享空间对数据转换。转换过程需要调用系统的api,这个过程称为系统调用

可以看到进程间通信经历了两次拷贝,两次拷贝非常耗性能的,而两次拷贝可以再优化优化。
那binder是怎么做到一次拷贝的呢
我们先来看Android中的空间

那么为什么会有内核空间和用户空间呢
现代计算机都有两种以上的运行模式(普通模式、特权模式),
linux系统只有两层:
用户空间
- 高优先级模式(特权模式)
- 低优先级模式(普通模式)。
linux系统在高优先级模式中运行系统**内核代码**以及与**硬件密切**相关的代码。**低优先级**运行**应用程序**与硬件无关部分。
应用程序不能**直接操控硬件**或者调用内核函数,需借助一系列接口函数申请让系统调用相关代码在内核空间运行,获取代码运行权限。

1.2好处
1 应用程序崩溃不会造成内核崩溃,拿windows举例来说,QQ崩溃掉不会造成程序死机。
2 每个应用程序或者进程都会有自己特定的地址、私有数据空间,程序之间一般不会相互影响
例如QQ崩溃不会造成微信的崩溃。空间的隔离极大地提高了系统运行的稳定性。
1.3计算机蓝屏带来的启示
计算机蓝屏主要是因为计算机**硬件驱动不兼容**问题造成,**硬件驱动代码运行在内核空间**,与kernel运行在相同空间内,所以驱动程序发生问题容易造成系统的崩溃。将用户空间与内核空间隔离开,可减少系统崩溃的可能,提高系统的稳定性。毕竟现实情况中,应用程序崩溃的情况比蓝屏出现的概率要多的多得多。在linux中这种情况可以类比。
window有上百个驱动,如蓝牙驱动,主板驱动,声卡驱动,麦克风驱动,显卡驱动,USB驱动等等!驱动过多容易造成蓝屏的出现。他们都运行在内核中。为什么会有这么多驱动。电脑中每一个配件都可以随意组合。形成一个完整的电脑
Android 发现
**在Android系统中,虽然也是基于Linux系统,但是这些驱动在Android厂商都已经消失了。全部集成化了、除了Binder驱动外,屏幕驱动,蓝牙驱动外几乎看不到其他驱动,这也是蓝屏比较少的原因**
1.4 在linux中
每一个系统进程都拥有自己私有的地址空间和数据,用户空间造成的进程错误会被局部化,而不会影响到内核或者其他进程。(上面所说QQ和微信的例子)。
当用户进程需要完成在特权模式下才能完成的某些工作时,通过linux向上提供的系统调用接口进入特权模式,然后执行调用所提供的有限功能
应用程序正常情况下都是运行在普通模式下,这部分代码运行的空间称为用户空间,当代码通过系统调用计入到特权级别运行的时候,对应的代码执行空间称为内核空间。
linux系统中每个进程占有4G空间(虚拟空间,并不一定真实占用)
空间分布如下:
- 用户空间: 0~(3G-1) 普通的应用程序代码运行在此部分空间中
- 内核空间: 3G~(4G-1) 内核代码段,其中驱动就是运行在此部分空间中
1.5 用户空间与内核空间交流
用户空间应用程序往往需要调用硬件(QQ调用相机拍照)或者运行与系统核心相关的内容(360清理进程),免不了与内核打交道,他们之间调用关系又是怎样呢?
交流关系以open()文件打开函数为例
上层应用在用户空间执行到 open() API函数时,会触发系统软中断,系统调用 系统调用函数 sys_open()系统调用函数,在内核空间执行open代码#,这样用户空间的open函数内部代码就取得了在内核空间运行的权限,可以做一些比较牛比较核心的事情。
应用层API函数还有很多,大约有250个左右,涵盖范围包括文件操作、进程控制、网络操作等等。调用原理大致相同。
Binder驱动映射

谈谈你对 binder 的理解
binder 是 Android 中主要的跨进程通信方式,binder 驱动和 service manager 分别相当于网络协议中的路由器和 DNS,并基于 mmap 实现了 IPC 传输数据时只需一次拷贝。
binder 包括 BinderProxy、BpBinder 等各种 Binder 实体,以及对 binder 驱动操作的 ProcessState、IPCThreadState 封装,再加上 binder 驱动内部的结构体、命令处理,整体贯穿 Java、Native 层,涉及用户态、内核态,往上可以说到 Service、AIDL 等,往下可以说到 mmap、binder 驱动设备,是相当庞大、繁琐的一个机制。

怎么理解页框和页?
页框是指一块实际的物理内存,页是指程序的一块内存数据单元。内存数据一定是存储在实际的物理内存上,即页必然对应于一个页框,页数据实际是存储在页框上的。
页框和页一样大,都是内核对内存的分块单位。一个页框可以映射给多个页,也就是说一块实际的物理存储空间可以映射给多个进程的多个虚拟内存空间,这也是 mmap 机制依赖的基础规则。
简单说下 binder 的整体架构吧

Client 通过 ServiceManager 或 AMS 获取到的远程 binder 实体,一般会用 Proxy 做一层封装,比如 ServiceManagerProxy、 AIDL 生成的 Proxy 类。而被封装的远程 binder 实体是一个 BinderProxy。
**BpBinder** 和 BinderProxy 其实是一个东西:远程 binder 实体,只不过一个 Native 层、一个 Java 层,BpBinder 内部持有了一个 binder 句柄值 handle。
**ProcessState** 是进程单例,负责打开 Binder 驱动设备及 mmap;**IPCThreadState** 为线程单例,负责与 binder 驱动进行具体的命令通信。
由 Proxy 发起 transact() 调用,会将数据打包到 Parcel 中,层层向下调用到 BpBinder ,在 BpBinder 中调用 IPCThreadState 的 transact() 方法并传入 handle 句柄值,IPCThreadState 再去执行具体的 binder 命令。
由 binder 驱动到 Server 的大概流程就是:Server 通过 IPCThreadState 接收到 Client 的请求后,层层向上,最后回调到 **Stub** 的 onTransact() 方法。
当然这不代表所有的 IPC 流程,比如 Service Manager 作为一个 Server 时,便没有上层的封装,也没有借助 IPCThreadState,而是初始化后通过 binder_loop() 方法直接与 binder 驱动通信的。
网友评论