概要

Android 采用了分层架构,分为四个层,从高层到底层分别是应用程序层、应用程序框架层、系统运行库层,Linux 内核层。

整体启动过程如下:

  1. init 进程是第一个用户进程, ContextManager、MediaServer、Zygote 进程都是其子进程。init 进程的任务就是对各种设备进行初始化,运行 Android Framework 所需要要的 Daemon、ContextManager、MediaServer、Zygote 等。
  2. ContextManager 进程是一个管理 Android 系统服务的重要进程,所有的 Android 系统服务都需要通过 Binder IPC 将其注册到 ContextManager。
  3. MediaServer 进程是一个启动 Native 系统服务的进程,用于启动除 SurfaceFlinger 之外的所有 Native 服务,包括但不限于:AudioFlinger、CameraService。
  4. Zygote 进程用于缩减 Android 程序加载的时间,每当执行一个 Android 应用程序,Zygote 就会通过 fork 创建一个子进程来执行。因此,Zygote 进程是系统中第一个 Java 进程,同时也是所有 Java 进程的父进程。
  5. SystemServer 进程是 Zygote 进程 fork 出来的第一个进程,用于启动 SurfaceFlinger 服务和所有的 Java 系统服务。

init 进程

Linux 小知识:
Linux 中所有的进程都是由 init 进程创建并运行。首先 Linux 内核启动,然后在用户空间中,启动 init 进程,再依次启动系统运行所需的其他进程。在系统启动完成后,init 进程会作为守护进程监视其他进程。若某个监视中的进程一旦终结,进入僵死状态,init 进程就会释放掉该进程占用的系统资源,防止系统进程表被僵尸进程耗尽。

init 是 Android 启动后,由内核启动的第一个用户进程。它的主要工作如下:

  1. 创建和挂载启动所需的文件目录
  2. 初始化和启动系统服务
  3. 解析 init.rc 配置文件,并启动 Zygote 进程

Zygote

启动 Zygote

Zygote 的实现是 Java 代码,不能由 init 进程直接启动,需要通过虚拟机才可以。因此会通过下面的流程创建 Zygote 进程。

app_process 进程

init.rc 文件解析过程中,会启动 app_process 进程:

  1. 创建 AppRuntime 对象,AppRuntime 类继承于 AndroidRuntime 类。AndroidRuntime 的职责就是创建虚拟机,为运行 Android 程序做好准备
  2. 调用 addVmArguments 方法,添加创建虚拟机所需要的参数
  3. 调用 set_process_name 方法,将进程名字由 app_process 改成 zygote
  4. 调用 start 方法,创建虚拟机,运行 ZygoteInit 类

app_process 进程的代码参考:app_main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main(int argc, const char* const argv[]) {
//...
AppRuntime runtime;
const char *arg;
//...
int i = runtime.addVmArguments(argc, argv);
//...
// Next arg is startup classname or "--zygote"
if (i < argc) {
arg = argv[i++];
if (0 == strcmp("--zygote", arg)) {
bool startSystemServer = (i < argc) ?
strcmp(argv[i], "--start-system-server") == 0 : false;
setArgv0(argv0, "zygote");
set_process_name("zygote");
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer);
} else {
//...
}
}
//...
}

创建虚拟机

通过上面的分析,我们知道虚拟机的创建过程就在 AndroidRuntime#start 方法中:

  1. 调用 startVm 方法创建虚拟机,最终会通过 JNI_CreateJavaVM 方法创建虚拟机
  2. 调用 startReg 方法注册在虚拟机中要用到的 JNI 方法,最终会调用到 register_jni_procs 方法
  3. 调用 Java 层代码,通过 JNI 调用 ZygoteInit 的 main 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void AndroidRuntime::start(const char* className, const bool startSystemServer) {
//...
if (startVm(&mJavaVM, &env) != 0){
//...
}
//...
if (startReg(env) < 0) {
//...
}
//...
slashClassName = strdup(className);
//...
startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
//...
} else {
startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");
if (startMeth == NULL) {
//...
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
//...
}
}
}

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) {
//...
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
//...
}
//...
}

int AndroidRuntime::startReg(JNIEnv* env) {
//...
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
//...
}
//...
}

AndroidRuntime 代码参考:AndroidRuntime.cpp

初始化 Zygote

通过上面的分析,我们知道虚拟机初始化完成后就会通过 ZygoteInit#main 方法进行 Zygote 的初始化:

  1. 调用 registerZygoteSocket 方法,绑定套接字,接受新 Android 应用程序的运行请求
  2. 调用 preloadClasses,preloadResources 方法加载 Android Framework 使用的类和资源
  3. 调用 startSystemServer 方法运行 SystemServer 进程
  4. 调用 runSelectLoopMode 方法用于处理 Android 应用程序运行请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String argv[]) {
//...
registerZygoteSocket();
//...
preloadClasses();
preloadResources();
//...
if (argv[1].equals("true")) {
startSystemServer();
}
//...
if (ZYGOTE_FORK_MODE) {
runForkMode();
} else {
runSelectLoopMode();
}
}

ZygoteInit 代码参考:ZygoteInit.java

Zygote 套接字

Zygote 绑定的 socket 是 UDS(Unix Domain Socket),对应 /dev/socket/zygote。该 socket 在 init.rc 文件解析的时候被创建,并被记录在 ANDROID_SOCKET_zygote 环境变量中。

1
2
3
4
5
6
7
8
9
private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";

private static void registerZygoteSocket() {
//...
String env = System.getenv(ANDROID_SOCKET_ENV);
fileDesc = Integer.parseInt(env);
sServerSocket = new LocalServerSocket(createFileDescriptor(fileDesc));
//...
}

关于Unix域套接字
Zygote 与 /dev/socket/zygote

SystemServer

  1. 和运行其他 Android 进程不同,SystemServer 进程通过 Zygote#forkSystemServer 方法创建。
  2. 运行普通 Android 进程,系统不会检查是否创建成功。SystemServer 进程不同,它是必须要运行的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static boolean startSystemServer() {
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003",
"--capabilities=130104352,130104352",
"--runtime-init",
"--nice-name=system_server",
"com.android.server.SystemServer",
};
//...
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, debugFlags, null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities
);
//...
if (pid == 0) {
handleSystemServerProcess(parsedArgs);
}
//...
}

启动 SystemServer

SystemServer 进程后经过一系列复杂的调用后会通过反射调用到 SystemServer#main 方法:

  1. 加载 android_servers 库,调用 Native 方法 init1
  2. init1 方法会调用到 system_init 方法,这里面会启动 SurfaceFlinger,同时会调用 SystemServer#init2 方法
  3. init2 方法主要是启动 ServerThread 线程用于创建各种 Java 层服务,例如:WindowManagerService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public static void main(String[] args) {
//...
System.loadLibrary("android_servers");
init1(args);
}
extern "C" status_t system_init() {
//...
sp<IServiceManager> sm = defaultServiceManager();
//...
if (strcmp(propBuf, "1") == 0) {
SurfaceFlinger::instantiate();
}
//...
runtime->callStatic("com/android/server/SystemServer", "init2");
//...
}
public static final void init2() {
Thread thr = new ServerThread();
thr.setName("android.server.ServerThread");
thr.start();
}

class ServerThread extends Thread {
//...
@Override
public void run() {
//...
Looper.prepare();
//...
AlarmManagerService alarm = new AlarmManagerService(context);
ServiceManager.addService(Context.ALARM_SERVICE, alarm);
//...
wm = WindowManagerService.main(context, power,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
//...
imm = new InputMethodManagerService(context, statusBar);
ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
//...
notification = new NotificationManagerService(context, statusBar, lights);
ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification);
//...
Looper.loop();
}
}

SystemServer 调用逻辑可以参考:SystemServer进程启动过程源码分析
SystemServer 代码可以参考:SystemServer.java
system_init.cpp 代码可以参考:system_init.cpp

启动 新进程

Linux 新创建一个进程:

  1. 调用 fork 函数创建新的子进程 A^,新创建的进程 A^ 共享父进程的内存结构与库链接信息。
  2. 子进程 A^ 调用 exec(B),将进程 B 的代码加载到内存中。此时父进程 A 的内存信息被清除,并重新分配内存,以便运行被装载的 B 进程,接着形成新的库连接信息。如果 B 进程需要用到的共享库还没有被装载至内存中,还需要将相关库装载到内存中。

Android 新创建一个进程:

  1. Zygote 进程调用 fork 函数创建新的子进程 Zygote^,这里同样共享父进程的内存结构与库链接信息。
  2. Zygote^ 不会调用 exec 函数装载代码,而是将新的 Android 程序动态的加载到虚拟机上,然后 Zygote^ 进程将执行流程交给新的 Android 程序。

这样新的 Android 程序会使用 Zygote 中已经初始化并运行的 Zygote 虚拟机,通过使用已加载到内存中的类和资源来加快运行速度。

内存复制的开销很大,因此 Zygote 进程并没有调用 exec 函数,而是直接使用父进程内存相关信息。因此当子进程在引用父进程的内存空间时,不进行复制,而是直接共享父进程的内存空间。当需要修改共享内存中的信息时,子进程才会将父进程中相关的内存信息复制到自身的内存空间中,并进行修改。这就是 COW(Copy on Write) 技术。

服务概述

系统服务

Android 系统服务提供系统最基本、最核心的功能,这些服务分别位于 Application Framework 和 Libraries 层之中。

本地系统服务

本地系统服务使用 C++ 编写,运行在 Libraries 层,主要包含:SurfaceFlinger、AudioFlinger。

SurfaceFlinger 服务能够将各种应用程序的 Surface 组合后,渲染到 Frame Buffer 设备中。

AudioFlinger 服务和 Surface 类似,不过它是混合多种 Android 应用程序的音频数据,并发送到耳机、扬声器等音频输出设备中。

Java 系统服务

Java 系统该服务由 SystemServer 进程启动,比较重要的服务如下:

  1. ActivityManagerService:管理所有 Activity 的生命周期和堆栈
  2. WindowManagerService:位于 SurfaceFlinger 之上,将要绘制到机器画面上的信息传递给 SurfaceFlinger
  3. PackageManagerService:加载 apk 文件的信息,提供信息显示系统中设置了哪些包,以及加载了哪些包

应用程序服务

应用程序服务就是四大组件之一的 Service,当创建服务的客户端与所创建的服务在同一进程,该服务就是本地服务,否则是远程服务。

运行系统服务

系统服务和应用程序服务不同,不需要使用前启动,直接通过 getSystemService 使用即可,因为在系统启动的时候,init 进程已经启动了这些服务。
在 Android 启动时,系统服务具体由 MediaServer 和 SystemServer 两个进程启动。MediaServer 进程用于启动除了 SurfaceFlinger 以外的本地系统服务,SystemServer 进程用于启动 Java 系统服务和 SurfaceFlinger。

Binder

Binder 原本是 IPC 工具,但在 Android 中,它主要用于支持 RPC,使得当前进程调另一个进程的函数时候就像调用自身函数一样轻松、简单。

在了解 Binder 之前,我们先来了解下有关 Linux 内核内存空间的知识。Android 进程和 Linux 进程一样,它们只运行在进程固有的虚拟地址空间中,一个 4 GB 的虚拟地址空间,其中 3 GB 是用户空间,剩余的 1 GB 是内核空间。

因此,一个进程如何向另一个进程传递数据呢?显然是需要通过两个进程共享的内核空间。Binder 使用运行在内核空间中的抽象驱动程序 Binder Driver 实现进程间通信。

Linux 本身就提供了 IPC 工具,为什么 Android 选择 Binder 进行进程间通信呢?

  1. Binder 采用 Linux 中优秀的内存管理技术,通过内核空间传递数据时能确保数据的可靠性
  2. 由于使用用户空间无法访问的内核空间交换数据,所以 IPC 间的安全问题得到解决

Binder IPC 数据流

IPC 数据包含函数调用的相关内容,它有待调服务号(Handle),待调函数名(RPC 代码),传递的参数(RPC 数据),IPC 数据的处理方法(Binder 协议)组成。

Binder Driver 是字符设备驱动程序。应用程序在通过 Binder 尝试 RPC 操作时,会进行 open 系统调用,获取 Binder Driver 的文件描述符。而后,通过 mmap 系统调用,在内核中开辟一块内存区域,以便存放接受的 IPC 数据。最后调用 ioctl 函数,将 IPC 数据作为参数,传递给 Binder Driver。

服务层:包含一系列提供特定功能的服务函数。服务客户端虚拟调用特定的服务函数,而实际调用是由 SystemServer/MediaServer 进行的。
RPC 层:服务客户端会在这一层生成用于调用服务函数的 RPC 代码和 RPC 数据。SystemServer/MediaServer 会根据传递的 RPC 代码查找相应的函数,并将 IPC 数据传递给查找到的函数。
IPC 层:该层将 RPC 层生成的 RPC 代码和 RPC 数据封装成 Binder IPC 数据,以便将它们传递给 Binder Driver。
Binder Driver 层:接受来自 IPC 层的 Binder IPC 数据查找包含指定服务的进程(SystemServer 进程 或 MediaServer 进程),并将 IPC 数据传递给查找到的进程(SystemServer 进程 或 MediaServer 进程)。

Binder 协议

Binder 协议包含在 Binder IPC 数据中,根据传递方向,Binder 协议分为两种。一种是从 IPC 层传递给 Binder Driver 的 “BINDER COMMAND PROTOCOL”,另一种是从 Binder Driver 传递给 IPC 层的 “BINDER RETURN PROTOCOL”。
Binder 协议类似网络协议,是数据接收方与发送方共同遵守的规定或规则:

  1. BC_TRANSACTION:指的是 “BINDER COMMAND PROTOCOL”,用于发送端通过 Binder Driver 向接收端发送 IPC 数据
  2. BR_TRANSACTION:指的是 “BINDER RETURN PROTOCOL”,用于接受端分析处理 IPC 数据

Binder Driver 接受 IPC 数据后会分析其中的协议,若为 BC_TRANSACTION,则根据 IPC 数据中的 Handle 信息查找包含指定服务的进程(SystemServer 进程 或 MediaServer 进程)。然后 Binder Driver 会把协议更改为 BR_TRANSACTION,重新插入到 IPC 数据中,然后传递给指定的服务。

Binder 寻址

ContextManager 对应的进程是 servicemanager,查看 init.rc 脚本可以知道,ContextManager进程先于 MediaServer 和 SystemServer 进程运行。
ContextManager 进程为每个服务分配一个 Handler 的编号,并提供服务的添加、检测等功能。ContextManager 自身的 Handle 值被设置为 0。

Binder 寻址:Binder Driver 根据 IPC 数据中的 Handler 查找对应的 Binder 节点(binder_node 结构体)

Binder Driver 分析

binder_proc 结构体:用来管理 Binder IPC 所需要的各种信息,包括打开 Binder Driver 的进程信息、接受 IPC 的 Buffer信息(binder_buffer),Binder 节点列表(红黑树存储的 binder_node)
binder_node 结构体:每个服务对应一个 Binder 节点,并且其拥有 binder_proc 的指针,用于查找 binder_proc 结构体。

服务注册: ContextManager 进程和服务所在的进程(例如:SystemServer)之间的 IPC,指的是将 SystemServer 中的服务注册到 ContextManager 的服务目录中
服务检索:ContextManager 进程和服务客户端之间的 IPC,指的是服务客户端在使用 SystemServer 中的服务时,向 ContextManager 请求服务编号的过程
服务使用指的是:服务客户端和服务所在的进程(例如:SystemServer)之间的 IPC,指的是服务客户端使用 SystemServer 中的服务

服务注册

  1. 调用 open 方法初始化 ContextManager 进程的 binder_proc 结构体。接着调用 mmap 方法在内核中分配一块用于接受 IPC 数据的 Buffer,并被保存到 binder_buffer 结构体中,该结构体会被注册到 binder_proc 结构体中。最后调用 ioctl 方法,进入待机状态,直到有其他进程向其发送 IPC 数据。
  2. 调用 open 方法初始化 SystemServer 进程的 binder_proc 结构体。接着调用 mmap 方法在内核中分配一块用于接受 IPC 数据的 Buffer,并被保存到 binder_buffer 结构体中,该结构体会被注册到 binder_proc 结构体中。最后生成 IPC 数据,调用 ioctl 方法发送。
  3. IPC 数据传送到 Binder Driver 中后,会根据 IPC 中的 Handle 查找到 ContextManager 进程对应的 binder_node 结构体,同时可以找到 binder_proc 结构体。为 SystemServer 要注册的服务生成 binder_node 结构体,BInder Driver 将该 binder_node 分别注册到 ContextManager 和 SystemServer 中的 binder_proc 结构体中。
  4. Binder Driver 查找 ContextManager 的 binder_proc 结构体,向其中的 binder_buffer 对应的 Buffer 发送 IPC 数据。Binder Driver 会记录 IPC 数据发送进程的 binder_proc 结构体,以便发送应答数据。
  5. Binder Driver 让 SystemServer 处于待机状态,并唤醒 ContextManager 接受 IPC 数据。ContextManager 根据接受的 IPC 数据注册指定的服务,而后生成 IPC 应答数据传给 Binder Driver。
  6. Binder Driver 根据记录的发送进程 binder_proc,向其中的 binder_buffer 对应的 Buffer 发送 IPC 应答数据。唤醒 SystemServer,接受 IPC 应答数据,进行相应的处理。

IPC 数据:

  • Handler:0
  • RPC 代码:ContextManager 的注册函数 - ADD_SERVICE
  • RPC 数据:要注册的服务名称

服务检索


检索过程和注册过程基本一致,区别在于:

  1. Binder Driver 不会生成 binder_node 节点,直接查找 ContextManager 的 binder_proc 结构体,向其中的 binder_buffer 对应的 Buffer 发送 IPC 数据。
  2. ContextManager 在服务目录中查找请求的服务,把服务的编号插入到 IPC 应答数据中,传递给 Binder Driver。
  3. Binder Driver 接收到服务的编号后,查找对应的 binder_node 结构体,而后将其注册到服务客户端对应的 binder_proc 结构体中。binder_node 结构体用于在服务使用阶段查找 SystemServer 的 binder_proc 结构体。
  4. Binder Driver 将查找到的 binder_bode 结构体编号插入到 IPC 应答数据中,传递给服务客户端。这个编号就是服务使用时的 Handle。

IPC 数据:

  • Handler:0
  • RPC 代码:ContextManager 的注册函数 - GET_SERVICE
  • RPC 数据:要使用的服务名称

服务使用


使用过程和注册过程、检索过程基本一致,区别在于:

  1. 之前发送数据都是发送给 ContextManager 对应的 Buffer,这里是发给 SystemServer 对应的 Buffer。发送的 IPC 数据包括 Handle,Binder Driver 根据这个 Handle 找到 binder_node 结构体,再找到其持有的 binder_proc 结构体,也就是 SystemServer 对应的。这个时候就可以把 IPC 数据发送到 binder_proc 持有的 binder_buffer 对应的 Buffer 中。
  2. Binder Driver 会让服务客户端进入待机状态,唤醒 SystemServer 进程接受 IPC 数据,并通过 IPC 数据中的 RPC 代码、RPC 数据调用相应的服务函数。
  3. 服务函数执行完之后,SystemServer 会生成 IPC 应答数据,并将其传给 Binder Driver。然后 Binder Driver 将 IPC 应答数据传给服务客户端。

IPC 数据:

  • Handler:指定服务的 handle
  • RPC 代码:服务函数
  • RPC 数据:服务函数的参数

面试问题

Binder 的一次拷贝:Binder 为什么只需要拷贝一次
Binder 的优势:android常用机制,11.Android 常见面试题

服务框架

ServiceManager

ServiceManager 相当于 ContextManager 的服务代理,它分为 Java 层和 C++ 层两种。

本地系统服务

TODO

Java 系统服务

TODO

参考

Android Frameworks 总结