Android 的 Binder 机制是一种独特的跨进程通讯(IPC)体系,在整个Android体系中都发挥着至关重要的效果。这种机制在操作体系的内核层、 Android 结构层(Framework)以及应用层(Java)都有其详细完成,从底层到上层,每一层都为 IPC 机制供给了必要的支撑,构成了一个高效且灵敏的通讯体系。

本系列 Binder 文章,会从内核层到 Framework 层,再到 Java 层,深入浅出,介绍整个 Binder 的规划:

  • 图解Binder:初始化
  • 图解Binder:体系调用 open
  • 图解Binder:业务
  • 图解Binder:线程池
  • 图解Binder:内存办理
  • 图解Binder:ServiceManager
  • 图解Binder:AIDL

Binder 内核层

Binder 初始化

Binder 驱动是 Android 体系的一部分,是在编译期编译到内核中的。Binder 驱动利用 initcall 机制,将它的初始化函数 binder_init() 添加到 initcall 行列中。当内核启动时,binder_init() 函数就会被执行,然后完结 Binder 驱动的初始化。

在Binder驱动的初始化过程中, binder_init() 首要作业是:

  • 注册 binder 设备:调用 misc_register(),向虚拟文件体系(VFS)注册一个名为 “binder” 的 misc 设备,该设备的设备文件路径是 “/dev/binder”。一旦注册成功,用户空间的进程就能够经过翻开 “/dev/binder” 设备文件,对 Binder 驱动进行操作,然后完成跨进程通讯。
  • 注册 binder 文件体系:调用 register_filesystem(),向 VFS 注册 binder 文件体系。binder 文件体系会在 init 进程启动时进行挂载。当经过体系调用 open() 翻开 “/dev/binder” 设备文件时,就会沿着 VFS,终究定位到 binder 文件体系,调用其对应的 binder_open() 完成,完结 binder 驱动的翻开。
图解 Binder:概述

《图解 Binder:初始化》一文将详细论述 Binder 的 binder_init(),以及如何经过 initcall 机制进行体系初始化。

Binder 的几个体系调用

每个要运用 Binder 进行通讯的进程,都会调用 Framework 层的 ProcessState::initWithDriver() ,翻开 Binder 驱动。initWithDriver() 首要涉及了几个体系调用,它们的详细的完成,首要是在内核层。相关调用如下:

ProcessState.cpp

└─initWithDriver()

└──init()

└───ProcessState()

└────open_driver()

└─────open()

└─────ioctl() // 发送 ioctl 指令 BINDER_VERSION,检测 Binder 驱动版本号

└─────ioctl() // 发送 ioctl 指令 BINDER_SET_MAX_THREADS,设置 Binder 线程池的最大线程数

└─────ioctl() // 发送 ioctl 指令 BINDER_ENABLE_ONEWAY_SPAM_DETECTION

└────mmap()

上面的调用链路,最要害是几个体系调用:

  • open():翻开 Binder 驱动设备。终究会调用 Binder 驱动的 binder_open()。
  • ioctl():发送各种 ioctl 指令,与 Binder 驱动进行通讯。终究会调用 Binder 驱动的 binder_ioctl()。
  • mmap():进行 mmap 映射,供给一块虚拟地址空间,用于树立接纳其他进程业务音讯的缓冲区。终究会调用 Binder 驱动的 binder_mmap()。

这几个体系调用,终究都是经过虚拟文件体系,进入到内核层。

图解 Binder:概述

在《图解 Binder:体系调用 open》里,咱们会介绍是如何沿着虚拟文件体系,终究定位到 binder 文件体系,调用到 binder_open()。

Binder 业务

一旦Binder驱动被成功翻开,咱们就能建议Binder业务。业务是Binder通讯的基础。

Binder 业务,便是一个进程(客户端)在调用另一个进程(服务端)中的一个函数。这个”函数调用”的过程便是经过发送一个 Binder 业务来建议,”返回值“便是 Binder 业务的回复。

图解 Binder:概述

在《图解 Binder:业务》中,咱们会围绕 Binder 业务,介绍与它相关的一些概念:binder 音讯、ioctl 指令、binder实体、binder 代理、binder 节点和 binder 引证以及它们的作业机制。

Binder 线程池

每个运用 Binder 通讯的进程,都会在 Framework 层树立自己的线程池,处理来自不同进程的业务。内核层也会为线程池线程维护相应的数据结构

《图解Binder:线程池》将提醒 Binder 线程池的作业原理,也会介绍 Binder 驱动里的几个作业行列。

Binder 内存办理

Binder 的高效性离不开其独特的内存办理机制。

每个进程在运用 Binder 进程通讯之前,都会先经过 mmap 进行映射,用于树立接纳其他进程业务音讯的缓冲区。mmap 是 Binder 完成进程通讯一次复制的要害。

图解 Binder:概述

内核也为缓冲区的高效分配、开释,规划了杂乱的数据结构。这也是 Binder 内存办理不可或缺的精彩部分。

在《图解Binder:内存办理》一文中,咱们将深入探讨虚拟内存、mmap、缓冲区分配和开释、物理内存页分配和开释,以及内存缩减器等机制。它们一起提高 Binder 通讯的功能。

Binder Framework 层

Android 的 Framework 层是由 Java 代码和 C++ 代码一起组成的。Java 代码通常是根据 C++ 代码,进行封装的。

在讲 Binder 内核层的时候,其实就会涉及了不少 Framework 层的代码,比如:Binder 线程池、Binder 业务发送与接纳的代码等(首要是 C++ 代码)。

ServiceManager

在 Android Framework 层,Binder 的另一个首要组成部分是 ServiceManager。

ServiceManager 是 Binder 驱动中的 0 号 Binder 节点,用于办理体系级服务的 Binder 引证。AMS、WMS 这些体系级服务,都会将自己注册到 ServiceManager 中:

图解 Binder:概述

当其他进程需求运用这些服务的时候,能够经过 ServiceManager 来查找和获取:

图解 Binder:概述

在《图解Binder:ServiceManager》中,咱们会介绍 ServiceManager 的作业原理。

Binder 应用层

Framework 在 Java 层为应用层供给了不少与 native 层对应的类,如 Binder、BinderProxy 等类。这些类,本质上便是对 native 层的封装,终究还是会调用 native 层的对应函数。

应用层调用这些 Java 类,就能够经过 Binder,完成跨进程通讯。可是直接调用这些 Java 类,会比较杂乱,需求深入了解 Binder 通讯的机制。所以,Binder 为咱们供给了 AIDL 来处理这个困难。

AIDL

在 Java 层,Binder 还供给了 AIDL,便于咱们完成跨进程的调用。

AIDL(Android Interface Definition Language)是一种支持跨进程通讯 (IPC) 的接口定义语言。AIDL 的效果便是为咱们生成一些模板代码,减轻开发者的作业量。

在《图解Binder:AIDL》一文中,咱们将介绍 AIDL 的运用及作业原理。

代码文件目录

Binder 内核层的代码,首要在 common/drivers/android 目录下。

负责与内核通讯的 Framework 层的 C++ 代码,如 Binder 线程池、Binder 业务发送与接纳的代码等,首要在 frameworks/native/libs/binder 目录下。

Framework 层的 ServiceManager 相关代码,首要在 frameworks/native/cmds/servicemanager/ServiceManager 目录下。

Framework 的 Java 层的相关代码,首要在 frameworks/base/core/java/android/os 目录下。

源码阅览技巧

本系列文章,都是根据Android platform 分支 android-13.0.0_r1 和内核分支 common-android13-5.15解 析。

一些要害代码的链接,可能会由于源码的变动,产生位置偏移、丢失等现象。能够查找函数名,重新进行定位:

图解 Binder:概述

假如和当时 Android 最新代码相差不大,建议 Framework 层代码直接切换到 master 分支,内核层代码直接切换到 common-android-mainline 分支。这样就能够像 IDEA 一样,点击相关函数进行跳转,或许检查哪些函数调用了该函数(非主分支代码,无法进行跳转)。

Framework 层代码直接切换到 master 分支 :

图解 Binder:概述

内核层代码直接切换到 common-android-mainline 分支:

图解 Binder:概述