【Binder】Android 跨进程通信原理解析

article/2025/9/14 9:37:39

前言

在Android开发的过程中,用到跨进程通信的地方非常非常多,我们所使用的Activity、Service等组件都需要和AMS进行跨进程通信,而这种跨进程的通信都是由Binder完成的。
甚至一个看似简单的startActivity操作,就有可能发生 7 次的跨进程通信,不信的话我就带大家走一下Activity的启动流程看看。

Activity的启动流程

在日常的开发中,我们启动一个Activity通常是使用如下代码:

startActivity(Intent(this, NewActivity::class.java))

执行这行代码看似简单,其实里面运行的过程非常复杂,进行了多次IPC操作(Inter-Process Communication,进程间通信)。

通常有两种情况我们会使用到startActivity方法

  • 点击桌面图标打开这个app
  • 在同一个app中启动app的另一个Activity

第一种方式启动Activity,相对来说涉及的过程更加完整,所以我们就以这种方式来简单分析一下Activity的启动流程。我们要知道一点就是,其实桌面也是一个app且它的进程名叫Launcher进程。

下面我们来看看点击桌面图标到打开这个app的过程是怎么样的:

1、点击图标,首先会执行Activity的startActivity方法

2、然后会执行到Instrumentation的execStartActivity方法

3、然后会跨进程从Launcher进程进入到system_server进程,并执行AMS的startActivity方法(IPC)

4、AMS收到启动请求后,会告知Launcher进程执行handlePauseActivity()方法使当前Activity进入pause状态(IPC)

5、Launcher进程进入Paused状态后,会告知AMS已暂停(IPC)

6、然后AMS就会检查新的Activity是否已启动(是否已经有进程了)

7、如果没有启动,则创建一个socket,通过socket通知Zygote进程创建一个新的应用进程(IPC)

8、应用进程创建好后,执行该进程的ActivityThread.main()方法(这里是通过反射找到main方法的),然后再通过attachApplication()方法将Activity的ApplicationThread给到AMS(IPC)

9、AMS收到应用进程启动成功的消息后,就会通知应用进程的ActivityThread开始Activity的生命周期(IPC)

10、Activity生命周期执行完之后,又通知AMS:可以将上一个Activity置为stop状态了(IPC)

什么时候考虑使用多进程?

另外,在Android开发中,一个应用就有可能会开启许多个进程,例如QQ、微信等常见的app,保守估计,都分别至少开启了8个以上的进程,

那么为什么为一个app开启这么多个进程呢?我觉得可以从以下方面考虑:

1、突破内存限制:我们知道,虚拟机分配给各个进程的运行内存是有限制的,LMK(low momery kill)也会优先回收对系统资源占用多的进程,而Android中图片是内存的大户,如果一个应用的图片非常多,是不是就可以考虑将这个应用再划分出一个进程,这样是不是就可以用更多的内存了

2、功能稳定性:例如,我们可以考虑将应用中独立且重要的模块单独开一个进程出来管理,可以保障这个纯粹的进程的稳定性。比如微信就是这么做的,微信中有个进程叫push,它是专门用来管理网络通信的,用来保证网络连接的稳定性

3、隔离风险:对于不稳定的功能,我们可以为它开启一个单独的进程,隔离这个功能可能带给我们的风险,例如用一个独立的进程来专门管理WebView,阻隔内存泄漏导致的问题

既然多进程的应用这么广泛,那么跨进程通信就显得尤为重要了,因此我们必须要对Android的跨进程通信有一定的了解。

Android中的跨进程通信机制使用的Binder机制,这是基于谷歌的一个内部项目叫openBinder所设计的,也可以说Binder是一个虚拟物理设备驱动,因为它为跨进程通信提供了共享的物理空间。Binder连接了Client、Server、Server Manager和Binder驱动程序,形成一套C/S的通信架构。

好了,说了这么多题外话,现在回归正题。

本篇文章,将带大家一起聊聊Binder机制。

虚拟内存的划分

在这之前,我们需要对一些内存的概念有一定的了解。

我们在程序开发中所说的内存,比如这个bitmap占用多少内存,这个对象占用多少内存等等,通常就是指虚拟内存,而虚拟内存会被映射到物理内存上或者一部分会被映射到磁盘空间上。

同时,虚拟内存,会被操作系统划分两个部分:

  • 用户空间:这块空间是进程独有的,是用户程序代码运行的地方
  • 内核空间:这块空间是被所有进程共享的,是内核代码运行的地方

用户空间和内核空间是隔离的,也就是说即使用户空间的程序崩溃了,内核空间也不会受到影响。

进程的通信的关键的就是用户空间和内核空间之间的通信,那么既然用户空间和内核空间是隔离的,怎么让它们能够通信呢?

我们可以将某个进程的用户空间中需要跨进程通信的数据拷贝一份放到内核空间,然后再将内核空间中的这份数据再次拷贝给到另一个进程的用户空间,这样就完成了跨进程通信,这就是进程通信的本质。

那么,怎么完成用户空间的数据拷贝到内核空间呢?

我们可以通过syscall,即系统调用,Linux操作系统给我们提供了两个API:

  • copy_from_user:将数据从用户空间 copy 到内核空间的内核缓存区
  • copy_to_user:将数据从内核空间 copy 到用户空间

Linux已有的进程通信方式

其实,Linux中已经有很多种跨进程通信了,如共享内存、管道、Socket等,既然已经有这么多种方式了,为什么Android还要另外使用一套机制来单独实现跨进程通信呢?要解答这个问题,我们就不得不对Linux中已有的一些进程通信方式进行一个简单的了解,并对比它们的优劣势进行对比。

共享内存Socket管道
拷贝次数022
特点控制复杂,易用性差,需要自行处理并发同步等问题基于C/S架构,传输效率低,开销大效率高,但局限性大
安全性共享内存的访问是开放的,不安全访问接入点是开放的,不安全一对一的关系,安全

1、管道:使用管道进程跨进程通信,数据需要拷贝两次,另外,进程间是一对一的关系,这种一对一的关系并不符合Android中的跨进程通信的需求

2、共享内存:所有进程共享同一份内存,使用共享内存的方式,不需要拷贝数据,但是利用共享内存来进行跨进程通信的话,控制起来非常复杂,易用性差,需要自行处理多进程并发同步等问题,另外,它最大的弊端在于它是不安全的

3、Socket:基于C/S架构,它是一对多的关系,使用Socket进行跨进程通信,需要将数据拷贝两次,性能较低,另外,Socket跨进程通信同样是不安全的

为什么说共享内存和Socket都是不安全的呢?

比如进程A需要向进程B发送数据,为了安全,进程A必要告知进程B自己的身份(pid),即进程A自己发送身份给进程B,然后由进程B来验证进程A的身份,如果验证是进程A的身份,就能正常通信。

这就存在一个问题了,那就是进程B收到的身份,有可能是被其他进程伪造出来的,而不是进程A自己的,那就使得其他进程可以通过伪造进程身份的方式来跟进程B通信,这就是一个通信不安全的问题!

Binder机制

通过以上的介绍,我们可以得知,管道、共享内存和Socket都不适合在Android中进行跨进程通信,因此,Android单独开发了一套机制来实现各个进程之间的通信问题,那就是Binder机制。

Binder机制是基于C/S架构实现的,使用Binder进行跨进程通信,只需要将数据拷贝一次,并且它是一种安全的跨进程通信机制。

安全性

Binder是怎么解决这个通信不安全的问题的呢?

在Binder机制中,有内核添加进程的身份标识,发送身份信息的数据包将由Linux内核来发送,不再由进程自己发送,这样就杜绝了其他进程伪造假身份来通信的可能

数据拷贝

那么,Binder又是怎么只进行一次数据拷贝就完成通信的呢?

在Binder机制中,某进程的用户空间中的一块内存区域vm_area_struct,会映射到Binder驱动提供的一块物理空间(binder mmap)上,同时,内核空间中的一块内存区域vm_struct,也会映射到物理空间 binder mmap 中。

这就使得某进程的用户空间和内核空间中的某块内存区域,共同对应了物理地址中的同一份物理空间,也就是说,这块物理地址的空间,被用户空间和内核空间共享了。

在Binder机制中的,物理地址空间和虚拟地址空间的映射关系如下图所示:

在这里插入图片描述

所以,为什么Binder跨进程通信只需要拷贝一次,我们再来结合这种图看,就非常容易理解了:

在这里插入图片描述

进程A(数据发送方)想给进程B(数据接收方)发送数据,进程A会把数据包通过系统的copy_from_user方法拷贝到内核空间的数据缓存区,注意,这块数据缓存区是映射在物理内存中的 binder mmap 这个空间中的,同时进程B的一块内存区域 vm_area_struct 也映射在物理内存中的 binder mmap 这个空间中,

那么,进程B想要取得进程A发送的数据包,实际上只需要从内核空间的数据缓存区直接取就行了。

因此,整个通信过程仅需要进行一次数据拷贝。

MMAP机制

MMAP,memory mapping,即内存映射,Linux操作系统通过将一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,这个过程就叫做内存映射。

在上面的图中,我们也看到,数据接收方和数据发送方是通过mmap()方法建立内存映射的。

为什么要建立映射关系呢?

我们在用户空间中,是不能直接访问磁盘上的内容的,比如读写磁盘文件。如果用户空间想要修改某个磁盘文件中的内容的话,可以分为以下几个步骤:

1、先将磁盘文件的内容拷贝到内核空间的内存中

2、将内容从内核空间拷贝到用户空间的内存中

3、在用户空间中对文件的内容进行修改

4、修改完毕后再把数据拷贝到内核空间

5、内核空间再把数据拷贝至磁盘文件上

由此可见,如果用户空间想要修改某个磁盘文件中的内容,必须要进行四次拷贝,这种间接访问磁盘的方式效率是比较低的

而mmap机制,就是用来解决这个问题的,通过mmap,可以将用户空间上的一块虚拟内存区域映射到物理空间上,建立这个映射关系后,如果向这块虚拟内存区域写入数据,就相当于也写入到了与其映射关联的物理空间上了,这样就提高了文件读写的效率

mmap()方法,它的作用就是让一块虚拟内存指向一块已知的物理内存。对文件进行mmap后,会在进程的虚拟内存分配地址空间,建立映射关系,实现这样的映射关系后,就可以采用指针的方式读写操作这一段内存了。

下面我们介绍一下怎么使用mmap()方法,我们看下它是怎么定义的:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

它的各个参数的含义如下:

这里对其进行说明,首先,m_size这个变量是指共享内存区域(映射区域)的大小,m_fd这个变量是个指针,指向一个物理地址。返回值m_ptr也是一个指针,指向的是与这块物理空间有映射关联的虚拟内存地址。

  • addr:指定映射的虚拟内存地址,可以设置为NULL,让 Linux 内核自动选择合适的虚拟内存地址
  • length:共享内存区域(映射区域)的大小,单位是byte
  • prot:映射内存的保护模式,可选值如下:
    1. PROT_READ:可以被读取
    2. PROT_WRITE:可以被写入
    3. PROT_NONE:不可访问
    4. PROT_EXEC:可以被执行
  • flags:指定映射的类型,常用的可选值如下:
    1. MAP_SHARED:与其它所有映射到这个文件的进程共享映射空间(可实现共享内存)
    2. MAP_PRIVATE:建立一个写时复制(Copy on Write)的私有映射空间
    3. MAP_LOCKED:锁定映射区的页面,从而防止页面被交换出内存
    4. MAP_FIXED:使用指定的起始虚拟内存地址进行映射
  • fd:进行映射的文件句柄(文件的物理地址)
  • offset:文件偏移量(从文件的何处开始映射),通常设置为0

我们来看下这个方法是怎么使用的:

// 打开文件    
int fd = open(filepath, O_RDWR, 0644);           
// 对文件进行映射
void *addr = mmap(NULL, 1024, PROT_WRITE, MAP_SHARED, fd, 0); 

这里对其进行说明,首先,打开一个文件,并得到这个文件的句柄 fd(可理解为一个指针,指向一个这个文件的物理地址),

然后执行mmap()方法,将共享内存区域(映射区域)的大小设置为1024字节,并把 fd 传入,

mmap()方法得到的返回值 addr ,指向的是与 fd 有映射关联的虚拟内存地址

通过这个函数调用,就使得,addrfd指向了同一块内存区域(这块内存区域由Binder驱动提供)。

通过addr这个变量指向的内存地址,我们就可以对文件进行读写操作了。

以上就是mmap的原理了,介绍至此,我们可以知道,使用 mmap 对文件进行读写操作时可以减少内存拷贝的次数,从而提高对读写文件操作的效率。


http://chatgpt.dhexx.cn/article/CNjJUTMd.shtml

相关文章

Linux进程通信和线程通信

1、进程和线程是什么 简单的描述:进程好比是工厂,线程是工厂里的生产线,一个进程里面可以包含多个线程。 专业术语:进程是cpu资源分配的最小单位,线程是cpu调度的最小单位。 每个进程都有独立的代码和数据空间&#xf…

操作系统实验三、进程通信

文章目录 操作系统实验三、进程通信一、实验目的二、实验内容三、设计原理(或方案)及相关算法四、结果分析五、源代码 操作系统实验三、进程通信 一、实验目的 ​ 1、了解和熟悉Linux支持的消息通信机制、管道道通信、共享存储区机制及信息量机制。 2…

C++进程和Python进程通信

项目研发过程中用到了进程通信,由C应用程序创建共享内存及两个同步事件(Event1、Event2),然后阻塞等待外部进程激活事件Event1,Event1激活后,C应用程序读取共享内存中的数据,完成数据解析后执行相应指令,并…

Linux 进程通信

Linux 进程通信 1.传统进程通信 1.1 信号 信号机制是在软件层次上对中断机制的一种模拟。 信号的捕获与处理也成为系统的“软中断”机制。 1.1.1 常用信号 每个信号都有一个编号和宏定义的名称,这些名字都已SIG开头。宏定义在signal.h头文件中。 1.1.2 信号的…

进程间通信

文章目录 前言1、进程间通信的目的2、管道1.1 匿名管道1.2 命名管道 3、共享内存2.1 shmget函数2.2 shmctl函数2.3 shmat函数2.4 shmdt函数 4、消息队列5、信号量 前言 进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或…

【操作系统】进程通信

什么是进程通信?进程为什么需要通信? 进程通信:进程通信就是进程之间的信息交换。 进程通信的目的: 数据传输:一个进程需要将它的数据发送给另一个进程。通知事件:一个进程需要向另一个或一组进程发送消息…

【Linux】进程通信之管道通信详解

🍎作者:阿润菜菜 📖专栏:Linux系统编程 文章目录 一、什么是管道通信1. 管道通信是一种在进程间传递数据的方法2.看看接口:匿名管道和命名管道3. 管道通信的本质是什么? 二、管道通信的实现和深入理解1.如何…

进程通信方式总结与盘点

​ 进程通信是指进程之间的信息交换。这里需要和进程同步做一下区分,进程同步控制多个进程按一定顺序执行,进程通信是一种手段,而进程同步是目标。从某方面来讲,进程通信可以解决进程同步问题。 ​ 首先回顾下我们前面博文中讲到…

《操作系统》实验报告——进程通信

理论知识 Linux——Linux C语言编程基础知识 Linux——进程通信 一、实验目的 (1) 熟悉并掌握管道机制,并实现进程间通信 (2) 熟悉并掌握共享内存机制,并实现进程间通信 二、实验内容 任务一: (1)阅读以上父子…

多进程通信

多进程: 首先,先来讲一下fork之后,发生了什么事情。 由fork创建的新进程被称为子进程(child process)。该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则…

进程间通信详解

目录 一.进程间通信介绍 1.进程间通信的目的 2.进程间通信的本质 3.进程间通信分类 二.什么是管道 三.匿名管道 1. 匿名管道只能用于具有亲缘关系的进程之间进行通信,常用于父子。 2.pipe函数 3. 匿名管道的使用 4.管道的读写…

QProcess实现进程通信

QProcess实现进程通信的方式有点类似于管道。 QProcess父进程通过write来写入标准输入stdin,通过ReadyRead信号来接收子进程的消息。 QProcess子进程通过QFile来读取标准输入来接收父进程信息。通过QFile绑定QSocketNotifier来接收标准输入的实时信号,…

进程通信原理

目录 进程通信原理 1、同主机间的消息通讯机制 1.1 管道(pipe),流管道(s_pipe)和有名管道(FIFO) 1.2 信号(signal) 1.3 消息队列 1.4 共享内存 信号量 套接字(socket) 进程间通信各种方式效率比…

Linux进程通信

笔者在学习linux的过程中对linux进程通信进行记录学习。现在在 Linux 中使用较多的进程间通信方式主要有以下几种。 (1)管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信…

【Linux】进程间通信

目录 1. 进程间通信 1.1. 进程间通信的目的 1.2. 如何实现进程间通信 2. 管道通信 2.1. 匿名管道 2.1.1 创建匿名管道 2.1.2 . 深入理解匿名管道 2.2. 命名管道 2.2.1. 创建命名管道 3. system V 标准进程间通信 3.1. 共享内存 3.1.1. 实现原理 3.1.2. 代码实现 3…

认知智能理论三体论介绍简介

三体论是探索研究宇宙,信息和人类大脑三者关系的理论体系。是认知智能的奠基理论体系之一。宇宙和信息,信息和人类大脑,人类大脑和宇宙,三者之间存在着某种未被完全揭示的奥秘。三体论的核心思想体系就是基于宇宙,信息…

认知智能三大技术体系之类脑模型简介

认知智能三大技术体系之类脑模型简介 类脑模型是认知智能核心三大技术体系之一,类脑结构,功能机制的总称。类脑模型技术体系核心指导思想来自认知智能三大奠基理论,脑科学,心理学,逻辑学,情感学&#xff0c…

认知智能CI和人工智能AI的区别介绍简介

认知智能CI和人工智能AI的区别 人工智能和认知智能都是计算机科学的分支学科之一。人工智能是智能时代的第二个阶段,认知智能是智能时代的第三个阶段。认知智能也并不是智能时代发展的最终阶段,最终阶段应该是通用智能强智能时代。 本文主要就人工智能和…

什么是认知智能?

认知智能是计算机科学的一个分支科学,是智能科学发展的高级阶段,它以人类认知体系为基础,以模仿人类核心能力为目标,以信息的理解、存储、应用为研究方向,以感知信息的深度理解和自然语言信息的深度理解为突破口&#…