B站Android面试小记

起因

看着我同学最近也在到处投简历,我想着我也投一下看看行情,于是在2022-02-28号,我向B站投出了第一封简历,说实话当初只是想练练手,没想到最后接了B站的offer,也是造化弄人了

一面

技术面,45min左右,基本围绕你简历上写的亮点和你的工作经历展开

  • 自我介绍

  • 你在项目中负责什么

  • 用过什么设计模式,或在Android中常常会碰见的设计模式

    单例模式,策略模式,责任链模式(问了一下使用场景),工厂模式等

    Android中的观察者模式,适配器模式等

  • 有没有做过什么比较有难度的模块

    camera2,自定义照片裁剪View

  • 你对自定义View有什么了解

    回答了一些Path绘制以及触摸事件的处理

  • Android动画

    属性动画,ObjectAnimator

  • 多线程并发(锁、信号量、syncnorized),syncnorized对象和class有什么区别

  • ConcurrentHashMap线程安全的原理

    1.8之前用的分段式锁,1.8之后用的synchronized,至于具体的细节没有答上来,因为确实也没看过这边源码

  • jni,如何定位jni崩溃

    这个我当时回答的是打log,因为项目中用到jni的地方确实不多,当然jni也是可以断点调试的

  • 你所开发的应用有多进程吗?进程间是怎么通信的

    这个我当时只回答了mmap,稍微聊了一下mmap原理和binder性能对比,后来复盘想起来项目中用到的Broadcastaidl binder通信都没有回答

  • Webviewnative怎么交互的

    • onUrlLoading拦截Schema

    • 注册js方法(addJavascriptInterface

  • Android编译打包过程

    aapt -> class -> dex -> 签名

  • 插桩

    ASM插桩,字节码操作

  • 性能监控

    因为我之前做过一个性能监控库,cpumem使用TOP命令解析,Anr通过给MainLooper设置Printer

  • LeakCanary原理

    WeakReference + ReferenceQueue,加了一些改进点:new一个弱引用的Object,等这个Object确认被回收后再确认Activity是否正常被回收

  • Jetpack Compose

    稍微谈了一下看法,是否在项目中用过

  • 算法题:最长公共前缀

    LeetCode 14题,easy难度:https://leetcode-cn.com/problems/longest-common-prefix/

二面

一面结束后5min左右,B站HR就给我打电话过来约了二面

二面也是技术面,20min左右,因为是晚上8点面的,估计人家急着想下班(笑)

  • 自我介绍

  • 工作职责

  • 工作中有什么亮点

    • 拍照裁剪业务

    • 单元测试库

    • 性能监控

    • 内存泄漏检测

  • 单元测试的库是怎么做的

    基于MockitoRobolectric:

    1. 封装了一个反射库用来方便测试
    2. 做了一个AutoCloser类用来自动关闭释放mock的资源,这里提到了使用MockedStatic,如果在使用完后没有释放,那在下一次使用到同一个类的MockedStatic的时候会报错,这里我自定义了一个注解@MockedStatic用来自动mock和释放资源
    3. 针对kotlin做了一些mock工具,比如说顶层函数的mock(这个在我以前的文章Android-Kotlin单元测试之 如何配合Mockito模拟顶层函数中介绍过)
  • 开发模式(流程规范):

    开发规范参考了阿里的Java规范和Android规范,选取了一些比较重要的条例和一些自己长时间开发的经验做成了一篇文档

  • 崩溃率的优化,做了哪些事情

    感觉这里没答好,有点答非所问的意思,我就说了说目前处理bug的一个流程,没有谈到怎么解决一个bug

  • 数据打点是怎么做的

    我们用的是神策第三方服务

  • 内存泄漏工具是怎么做的

    这部分同一面LeakCanary原理

  • 看你之前做过一个MQTT协议的客户端,是出于个人兴趣吗

    是的,当时是想要做一个IM应用

  • 在项目中有遇到需要3D渲染展示的内容吗

    目前没有

  • 两个Activity跳转时方法执行的顺序

    一个Activity创建是:onCreate -> onStart -> onResume(之后便在屏幕上显示了)

    假设从A Activity跳转到B Activity:A.onPause -> B.onCreate -> B.onStart -> B.onResume -> A.onStop

    B返回到A:B.onPause -> A.onRestart -> A.onResume -> B.onStop -> B.onDestory

  • 两个Activity传递数据可以通过什么方式

    • Intent

    • 如果是同一个进程的话,可以用全局变量或者单例等

    • SharedPreference

    • 文件

  • 什么时候使用Service

    后台任务,比如说后台播放音乐等,这里提了一下IntentService是开了一个子线程的

  • Service怎么启动,怎么停止

    • startService <—> stopService

    • bindService <—> unbindService

  • 包体积优化

    清理资源(字体、图片、代码等)

HR面

二面结束后过了2-3天,HR发微信过来恭喜我进入下一轮面试,我问她接下来是还有三面和HR面吗,她回答我说后面就直接是HR面了,说实话我还是挺惊讶的

HR面15min左右,大概就问了一下,为什么要从上家公司离职,我们是一个新部门,处于项目初期,有什么看法之类的,然后问了一下目前的薪资和期望薪资,over~

总结

说实话感觉这次面试太简单了,有点白瞎了我准备了那么多,还做了查漏补缺 ㄟ( ▔, ▔ )ㄏ ,最后祝大家都能找到心仪的工作 (๑•̀ㅂ•́)و✧


Android源码分析 - Binder驱动(下)

开篇

本篇以aosp分支android-11.0.0_r25,kernel分支android-msm-wahoo-4.4-android11作为基础解析

上一篇文章Android源码分析 - Binder驱动(中),我们分析了binder_ioctl中的写操作binder_thread_write部分,了解了binder请求的发起与调度,接下来我们就进行binder驱动的最后一部分分析,binder_thread_read

binder_ioctl_write_read

我们还是先从binder_ioctl后的BINDER_WRITE_READ命令码开始

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
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;
...
//将用户空间ubuf拷贝至内核空间bwr
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
...
//当读缓存中有数据,执行binder读操作
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
//如果todo队列中有未处理的任务,唤醒等待状态下的线程
binder_inner_proc_lock(proc);
if (!binder_worklist_empty_ilocked(&proc->todo))
binder_wakeup_proc_ilocked(proc);
binder_inner_proc_unlock(proc);
if (ret < 0) {
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
...
out:
return ret;
}

binder_thread_read

这是进行binder读操作的函数,这个函数也是比较长,我们同样将它分成几个部分:

  1. 等待可用的binder_work
  2. 循环获取todo队列中的binder_work,并根据binder_worktype,执行一定的处理
  3. 处理binder_transaction以及binder_transaction_data,并将binder_transaction_data拷贝回用户空间

第一部分:等待工作

binder_thread.looper

在此之前我们需要先看一下之前提到的,在binder_thread中的域成员looper,前面我们只是注释了这个域表示线程状态,这里我们介绍一下它有哪些取值:

  • BINDER_LOOPER_STATE_REGISTERED:表示该binder线程是非主binder线程
  • BINDER_LOOPER_STATE_ENTERED:表示该binder线程是主binder线程
  • BINDER_LOOPER_STATE_EXITED:表示该binder线程马上就要退出了
  • BINDER_LOOPER_STATE_INVALID:表示该binder线程是无效的(比如原来是binder主线程,后续用户又发送了一个BC_REGISTER_LOOPER请求)
  • BINDER_LOOPER_STATE_WAITING:表示当前binder线程正在等待请求
  • BINDER_LOOPER_STATE_NEED_RETURN:表示该binder线程在处理完transaction后需要返回到用户态

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
//用户空间传进来的需要将数据读到的地址
//实际上只是传输一些命令码和一个binder_transaction_data_secctx结构体
//真正的数据已经映射到用户虚拟内存空间中了,根据binder_transaction_data中所给的地址直接读就可以了
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
//起始地址 = 读数据的首地址 + 已读数据大小
void __user *ptr = buffer + *consumed;
//结束地址 = 读数据的首地址 + 读数据的总大小
void __user *end = buffer + size;

int ret = 0;
int wait_for_proc_work;

if (*consumed == 0) {
//向用户空间写一个binder响应码,该响应码不做任何操作
if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
}

retry:
binder_inner_proc_lock(proc);
//检查是否有可用的工作需要处理
wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
binder_inner_proc_unlock(proc);

//将线程的状态置为等待中
thread->looper |= BINDER_LOOPER_STATE_WAITING;

//如果没有可用的工作,可以等待进程todo队列中的工作
if (wait_for_proc_work) {
//这个binder线程既不是主线程,也没有被注册成binder子线程
//这里条件在binder_available_for_proc_work_ilocked中已经做了判断
//似乎永远不会进入到这个case中?
if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))) {
binder_user_error("%d:%d ERROR: Thread waiting for process work before calling BC_REGISTER_LOOPER or BC_ENTER_LOOPER (state %x)\n",
proc->pid, thread->pid, thread->looper);
//进程进入休眠状态,等待唤醒
wait_event_interruptible(binder_user_error_wait,
binder_stop_on_user_error < 2);
}
//恢复优先级
binder_restore_priority(current, proc->default_priority);
}

if (non_block) { //如果是非阻塞模式(这里似乎不会执行到)
//线程和进程的todo队列中都没有工作
if (!binder_has_work(thread, wait_for_proc_work))
ret = -EAGAIN;
} else { //如果是阻塞模式
//等待binder工作到来
ret = binder_wait_for_work(thread, wait_for_proc_work);
}

//将线程的等待中状态解除
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;

if (ret)
return ret;
...
}

这一部分先检查是否有可用的binder_work待处理,如果有的话进入到下一部分,如果没有的话则需要等待

具体的,我们先看这个函数中的wait_for_proc_work变量,这个变量表示是否需要等待binder_proc中的工作,当在binder_thread中找不到事务栈并且todo队列为空时,此变量值为true

1
2
3
4
5
6
7
static bool binder_available_for_proc_work_ilocked(struct binder_thread *thread)
{
return !thread->transaction_stack &&
binder_worklist_empty_ilocked(&thread->todo) &&
(thread->looper & (BINDER_LOOPER_STATE_ENTERED |
BINDER_LOOPER_STATE_REGISTERED)); //这个binder线程既不是主线程,也没有被注册成binder子线程
}

在上一篇文章Android源码分析 - Binder驱动(中),我们分析过是有分支会将binder_work直接添加到binder_proctodo队列中的,当binder_thread中找不到工作的话,可能就需要从binder_proc中找了

然后会将当前线程的状态置为等待中,等到有可处理的binder_work后再解除这个状态

之后会判断传入的参数是否为阻塞模式,这个是由framework层执行ioctl时传入的flags所决定的,根据framework中的binder代码,这里应该恒为阻塞模式,在阻塞模式下,会调用binder_wait_for_work函数,等待存在可用的binder_work

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
46
47
48
49
50
static int binder_wait_for_work(struct binder_thread *thread,
bool do_proc_work)
{
//定义一个等待队列项
DEFINE_WAIT(wait);
struct binder_proc *proc = thread->proc;
int ret = 0;

freezer_do_not_count();
binder_inner_proc_lock(proc);
for (;;) {
//准备睡眠等待
prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE);
//检查确认是否有binder_work可以处理
if (binder_has_work_ilocked(thread, do_proc_work))
break;
//可以处理binder_proc.todo中的工作的话
if (do_proc_work)
//将该binder线程加入到binder_proc中的等待线程中
list_add(&thread->waiting_thread_node,
&proc->waiting_threads);
binder_inner_proc_unlock(proc);
//睡眠
schedule();
binder_inner_proc_lock(proc);
//将该binder线程从binder_proc中的等待线程中移除
list_del_init(&thread->waiting_thread_node);
//检查当前系统调用进程是否有信号处理
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
}
//结束等待
finish_wait(&thread->wait, &wait);
binder_inner_proc_unlock(proc);
freezer_count();

return ret;
}

static bool binder_has_work_ilocked(struct binder_thread *thread,
bool do_proc_work)
{
//thread->process_todo为false时会延时执行
return thread->process_todo ||
thread->looper_need_return ||
(do_proc_work &&
!binder_worklist_empty_ilocked(&thread->proc->todo));
}

这个函数的结构和Linux函数wait_event_interruptible非常相似,我们先来看看它是怎么做到阻塞等待的


Linux进程调度

DEFINE_WAIT

这是一个宏,它定义了一个wait_queue_t结构体

1
2
3
4
5
6
7
8
#define DEFINE_WAIT_FUNC(name, function)				\
wait_queue_t name = { \
.private = current, \
.func = function, \
.task_list = LIST_HEAD_INIT((name).task_list), \
}

#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)

这个结构体中的private域指向的即是当前执行系统调用所在进程的描述结构体,func域指向唤醒这个队列项进程所执行的函数

prepare_to_wait

这个函数将我们刚刚定义的wait队列项加入到一个等待队列中(在binder中即是加入到thread->wait中),然后将进程的状态设置为我们指定的状态,在这里为TASK_INTERRUPTIBLE(可中断的睡眠状态)

schedule

schedule是真正执行进程调度的地方,由于之前进程状态已经被设置成TASK_INTERRUPTIBLE状态,在调用schedule后,该进程就会让出CPU,不再调度运行,直到该进程恢复TASK_RUNNING状态

wake_up_interruptible

我们在上一篇文章Android源码分析 - Binder驱动(中)中提到过,当binder_transaction工作处理完后,会调用wake_up_interruptible函数唤醒目标binder线程的等待队列

这个函数会唤醒TASK_INTERRUPTIBLE状态下的进程,它会循环遍历等待队列中的每个元素,分别执行其唤醒函数,也就对应着我们DEFINE_WAIT定义出来的结构体中的func域,即autoremove_wake_function,它最终会调用try_to_wake_up函数将进程置为TASK_RUNNING状态,这样后面的进程调度便会调度到该进程,从而唤醒该进程继续执行

signal_pending

这个函数是用来检查当前系统调用进程是否有信号需要处理的,当一个进程陷入系统调用并处于等待状态时,如果此时产生了一个信号,仅仅是在该进程的thread_info中标识了一下,所以我们唤醒进程后需要检查一下是否有信号需要处理,如果有的话,返回-ERESTARTSYS,先处理信号,后续Linux上层库函数会根据-ERESTARTSYS此返回值重新执行这个系统调用

finish_wait

最后一步,当进程被唤醒后,调用finish_wait函数执行清理工作,将当前进程置为TASK_RUNNING状态,并把当前wait队列项从等待队列中移除


了解了上面这些知识,我们再看binder_wait_for_work函数应该比较清晰了,这里有一点需要注意,我们在上一篇文章Android源码分析 - Binder驱动(中)提到,在没有TF_ONE_WAY标志的情况下,会使用binder_enqueue_deferred_thread_work_ilocked函数将tcomplete插入到事务发起binder线程的todo队列中,这个函数区别于binder_enqueue_thread_work_ilocked函数,它没有将thread->process_todo设为true,所以结合着binder_has_work_ilocked函数看,我们可以发现,当thread->process_todofalse时,整个binder_has_work_ilocked返回false,即会进入到睡眠状态,延迟执行BINDER_WORK_TRANSACTION_COMPLETE,这么设计可以让binder接收端优先处理事务,提高了性能

第二部分:获取工作,根据type做一定的处理

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
//用户空间传进来的需要将数据读到的地址
//实际上只是传输一些命令码和一个binder_transaction_data_secctx结构体
//真正的数据已经映射到用户虚拟内存空间中了,根据binder_transaction_data中所给的地址直接读就可以了
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
//起始地址 = 读数据的首地址 + 已读数据大小
void __user *ptr = buffer + *consumed;
//结束地址 = 读数据的首地址 + 读数据的总大小
void __user *end = buffer + size;
...
retry:
...
//循环处理todo队列中的工作
while (1) {
uint32_t cmd;
struct binder_transaction_data_secctx tr;
struct binder_transaction_data *trd = &tr.transaction_data;
struct binder_work *w = NULL;
struct list_head *list = NULL;
struct binder_transaction *t = NULL;
struct binder_thread *t_from;
size_t trsize = sizeof(*trd);

binder_inner_proc_lock(proc);
//找到需要处理的todo队列
if (!binder_worklist_empty_ilocked(&thread->todo))
list = &thread->todo;
else if (!binder_worklist_empty_ilocked(&proc->todo) &&
wait_for_proc_work)
list = &proc->todo;
else {
binder_inner_proc_unlock(proc);

/* no data added */
//只跳过了数据头部的命令码,没有读取任何数据
if (ptr - buffer == 4 && !thread->looper_need_return)
goto retry;
break;
}

//传输过来的数据大小不符合
if (end - ptr < sizeof(tr) + 4) {
binder_inner_proc_unlock(proc);
break;
}
//从todo队列中出队一项binder_work
w = binder_dequeue_work_head_ilocked(list);
if (binder_worklist_empty_ilocked(&thread->todo))
thread->process_todo = false;

switch (w->type) {
case BINDER_WORK_TRANSACTION: {
binder_inner_proc_unlock(proc);
//根据binder_work找到binder_transaction结构
t = container_of(w, struct binder_transaction, work);
} break;
case BINDER_WORK_RETURN_ERROR: {
...
} break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
binder_inner_proc_unlock(proc);
cmd = BR_TRANSACTION_COMPLETE;
//回复给用户进程BR_TRANSACTION_COMPLETE响应码
if (put_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);

//更新统计数据
binder_stat_br(proc, thread, cmd);
//释放
kfree(w);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
} break;
case BINDER_WORK_NODE: {
...
} break;
case BINDER_WORK_DEAD_BINDER:
case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
...
} break;
}
...
}
done:
...
}

这里先创建了一个binder_transaction_data_secctx结构体,后续会将它拷贝到用户空间去,然后创建了一个指针trd指向tr.transaction_data的地址,这样后续操作trd就相当于操作tr.transaction_data

当进程从睡眠中唤醒,意味着有可用的binder_work了,这时候理论上来说,binder_threadbinder_proc其中总有一个todo队列不为空,这里优先处理binder_threadtodo队列,如果两者都为空,且还未读取过任何数据,重新gotoretry处等待

接着就是将binder_work从相应的todo队列中出队,再根据其类型执行不同的处理操作,这里我们只针对BINDER_WORK_TRANSACTIONBINDER_WORK_TRANSACTION_COMPLETE这两种最重要的类型分析

当类型为BINDER_WORK_TRANSACTION时,表示是别的进程向自己发起binder请求,此时,我们根据binder_work找到对应的binder_transaction结构

当类型为BINDER_WORK_TRANSACTION_COMPLETE时,表示发起的请求BC_TRANSACTION已经完成了,此时将回复给用户空间BR_TRANSACTION_COMPLETE响应码,然后更新统计数据,释放资源

第三部分:处理binder_transaction,拷贝回用户空间

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
//用户空间传进来的需要将数据读到的地址
//实际上只是传输一些命令码和一个binder_transaction_data_secctx结构体
//真正的数据已经映射到用户虚拟内存空间中了,根据binder_transaction_data中所给的地址直接读就可以了
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
//起始地址 = 读数据的首地址 + 已读数据大小
void __user *ptr = buffer + *consumed;
//结束地址 = 读数据的首地址 + 读数据的总大小
void __user *end = buffer + size;
...
retry:
...
//循环处理todo队列中的工作
while (1) {
uint32_t cmd;
struct binder_transaction_data_secctx tr;
struct binder_transaction_data *trd = &tr.transaction_data;
struct binder_work *w = NULL;
struct list_head *list = NULL;
struct binder_transaction *t = NULL;
struct binder_thread *t_from;
size_t trsize = sizeof(*trd);
...
//只有在type == BINDER_WORK_TRANSACTION的情况下,t才会被赋值
if (!t)
continue;

if (t->buffer->target_node) { //binder实体不为NULL,对应着BC_TRANSACTION请求
struct binder_node *target_node = t->buffer->target_node;
struct binder_priority node_prio;

//binder实体在用户空间中的地址
trd->target.ptr = target_node->ptr;
//携带的额外数据
trd->cookie = target_node->cookie;
//优先级
node_prio.sched_policy = target_node->sched_policy;
node_prio.prio = target_node->min_priority;
binder_transaction_priority(current, t, node_prio,
target_node->inherit_rt);
//设置响应码
cmd = BR_TRANSACTION;
} else { //binder实体为NULL,对应着BC_REPLY请求
trd->target.ptr = 0;
trd->cookie = 0;
//设置响应码
cmd = BR_REPLY;
}
//表示要对目标对象请求的命令代码
trd->code = t->code;
//事务标志,详见enum transaction_flags
trd->flags = t->flags;
//请求发起进程的uid
trd->sender_euid = from_kuid(current_user_ns(), t->sender_euid);

//获取发起请求的binder线程
t_from = binder_get_txn_from(t);
if (t_from) {
struct task_struct *sender = t_from->proc->tsk;
//设置发起请求的进程pid
trd->sender_pid =
task_tgid_nr_ns(sender,
task_active_pid_ns(current));
} else {
trd->sender_pid = 0;
}

//数据大小
trd->data_size = t->buffer->data_size;
//偏移数组大小
trd->offsets_size = t->buffer->offsets_size;
//设置数据区首地址(这里通过内核空间地址和user_buffer_offset计算得出用户空间地址)
trd->data.ptr.buffer = (binder_uintptr_t)
((uintptr_t)t->buffer->data +
binder_alloc_get_user_buffer_offset(&proc->alloc));
//偏移数组紧挨着数据区,所以它的首地址就为数据区地址加上数据大小
trd->data.ptr.offsets = trd->data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));

tr.secctx = t->security_ctx;
if (t->security_ctx) {
cmd = BR_TRANSACTION_SEC_CTX;
trsize = sizeof(tr);
}

//回复给用户进程对应的响应码
if (put_user(cmd, (uint32_t __user *)ptr)) {
... //Error
}
ptr += sizeof(uint32_t);
//将binder_transaction_data拷贝至用户空间
if (copy_to_user(ptr, &tr, trsize)) {
... //Error
}
ptr += trsize;
//更新数据统计
binder_stat_br(proc, thread, cmd);

//临时引用计数减1
if (t_from)
binder_thread_dec_tmpref(t_from);
//允许释放这个buffer
t->buffer->allow_user_free = 1;
if (cmd != BR_REPLY && !(t->flags & TF_ONE_WAY)) {
//非异步处理
binder_inner_proc_lock(thread->proc);
//将这个事务插入到事务栈中
t->to_parent = thread->transaction_stack;
//设置目标处理线程
t->to_thread = thread;
thread->transaction_stack = t;
binder_inner_proc_unlock(thread->proc);
} else {
binder_free_transaction(t);
}
break;
}

done:
//更新已读数据大小
*consumed = ptr - buffer;
binder_inner_proc_lock(proc);
//请求线程数为0且没有等待线程,已启动线程数小于最大线程数
//且这个binder线程既不是主线程,也没有被注册成binder子线程
if (proc->requested_threads == 0 &&
list_empty(&thread->proc->waiting_threads) &&
proc->requested_threads_started < proc->max_threads &&
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
/*spawn a new thread if we leave this out */) {
//向用户空间发送BR_SPAWN_LOOPER响应码,创建新binder线程
proc->requested_threads++;
binder_inner_proc_unlock(proc);
if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return -EFAULT;
//更新统计信息
binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
} else
binder_inner_proc_unlock(proc);
return 0;
}

只有当type == BINDER_WORK_TRANSACTION的情况下,才会走后面的这些逻辑,这一部分主要做的工作是,将处理事务所需要的信息(命令码、PID等)和数据(数据区首地址和偏移数组地址)准备好,拷贝到用户空间,交给用户空间处理这个事务,还有一些细节都在上面代码注释中标出来了,应该还是比较清晰的

结束

到这里,从clientbinder驱动发起请求,到binder驱动找到server并将请求传递给server这一整个流程我们基本上是看完了,对于被TF_ONE_WAY标记的事务(无需返回),在server中处理完,这一整条binder进程通信流程也就结束了,而对于需要返回的事务,则还需要serverbinder驱动发起BC_REPLY事务请求,进行一次进程间通信,将处理后的返回值传给client进程

总结

我们已经将binder驱动所承担的工作分析的七七八八了,但肯定还有很多地方大家云里雾里(实际上我也是这样的),这是因为binder进程间通信不仅需要binder驱动的处理,还需要framework层的协助,很多事情,比如说binder对象的压扁打包就是在framework层做的,我们在binder驱动层只关注了传输,但却不知道传输的是什么,结构是怎样的,当然会产生各种各样的疑惑,后面我们就将会对framework层对binder提供的支持接着做分析


Android ASM插桩

简介

ASM插桩在网上其实已经有很多资料了,我之所以再写这篇文章呢,一是因为好久前学习的ASM,现在已经忘的差不多了,需要再回顾一下,二来是记录一下学习过程,以后如果再有细节记不清楚可以很方便的就能查到,三来再学习的过程中也踩了一些坑,收获了一些心得,这些也需要一个地方记录一下。

好了,废话就说到这里,接下来开始正文。

插桩技术指在保证原有程序逻辑完整性的基础上,在程序中插入探针,通过探针采集代码中的信息(方法本身、方法参数值、返回值等)在特定的位置插入代码段,从而收集程序运行时的动态上下文信息。

插桩技术大体可以分为两类:

  • APT(Annotation Process Tools),在编译的时候,动态生成 Java 文件,之后编译器将生成的 Java 文件编译成 class 文件,像 ButterKnifeDagger 就是通过 APT 的方式生成代码的。

    • 代表工具:ButterKnife
  • AOP(Aspect Oriented Programming),生成 class 文件后,修改 class 文件的字节码,达到修改代码的目的。

    • 代表工具:听云

工具

我们这次选用AOP技术,我们看看有哪些工具可以帮助我们完成插桩工作:

  • AspectJ,成熟稳定,使用者不需要对字节码文件有深入的理解,使用简单。但是其切入点相对固定,对于字节码文件的操作自由度以及开发的掌控度就大打折扣。并且,他会额外生成一些包装代码,对性能以及包大小有一定影响。

  • ASM,可以修改现有的字节码文件,也可以动态生成字节码文件,完全从字节码去操作字节码的框架,更加灵活,功能更加强大,可以根据需求自定义修改、插入、删除,性能也十分出色,但是要对字节码文件有比较深入的了解,上手也更难。

我们使用ASM来完成插桩,在介绍Android字节码插桩之前,需要先了解一下Java字节码的概念和Android程序打包过程。

字节码

我们知道,Java程序是运行在JVMJava虚拟机)上的,Java源代码首先会由编译器(Java Compiler)编译成包含了Bytecode(字节码)的.class文件,程序执行时,由类加载器(class loader)将该类的字节码加载到JVM中,JVM会解释执行相应的Bytecode。如下图所示:

Java编译执行过程

为什么不直接彻底编译成机器码,而需要字节码这个中间产物呢?Java是一门跨平台的语言,为了实现一份源码,处处运行的效果,每个平台都有对应不同的JVM,它会将源码对应的指令翻译成对应平台能够理解的机器指令。那为什么不从源码直接解释执行呢,我个人认为这是因为直接从源码开始的编译,速度非常慢,出于性能的考虑,先将源码做一些预处理,处理为字节码,来减轻运行前的编译的性能开销。

在做插桩之前,我们先要记住一点:Java 字节码指令是基于堆栈操作的,因为大部分的Java虚拟机对字节码的执行是基于堆栈的(AndroidDalvik虚拟机是基于寄存器的,不过不影响我们的插桩,因为在我们对java字节码插完桩后,才会执行从java字节码转换到dex文件的过程)

Android打包过程

Android打包过程

Android插桩过程

Android插桩点

Android插桩点

实战

这次,我们模仿听云,做一个Activity生命周期执行时间检测的插件。

我们先梳理一下功能点:

  1. 针对Activity
  2. 针对生命周期方法
  3. 支持插件自定义配置

我们用Java代码把我们想要插入的逻辑写一遍:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Test {

//这里取这个名字是为了防止和代码本身的成员变量产生冲突
private long _$_timeRecorder;

public void onCreate(Bundle savedInstanceState) {
//向实际代码前插入代码
_$_timeRecorder = -System.currentTimeMillis();

... //这里是实际代码

//向实际代码后插入代码
_$_timeRecorder += System.currentTimeMillis();
System.err.println("Time spent: " + _$_timeRecorder + "ms, when " + className + ".onCreate");
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (StackTraceElement element : stackTraceElements) {
System.err.println(element.getClassName() + "." + element.getMethodName() + ":" + element.getLineNumber());
}
}
}

接下来正式开始编写插件

新建插件工程

由于Android Studio没有新建gradle脚本的选项,我们先新建一个Empty Activity Project,在此基础上进行改造。

  1. 新建module
  2. 更改modulebuild.gradle文件
  3. 新建groovy源代码目录
  4. 新建groovy类实现Plugin<Project>接口
  5. 新建resource/META_INF/xxx.properites文件(xxx为插件的id名)
  6. properites文件中声明插件的实现类

为插件提供可配置的功能

  1. 新建一个实体类用来保存配置信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class AsmConfigModel {
/**
* 以此参数为开头的类(全限定类名)才插桩
* 如果不配此参数则代表所有类都可插桩
*/
public List<String> startWithPatterns;
/**
* 排除列表(全限定类名)
*/
public List<String> excludes;
/**
* 排除列表(全限定类名)
* 以文件形式
*/
public File excludesByFile;
}
  1. 在插件apply的时候创建这个配置类,以提供给使用者配置
1
2
3
4
5
@Override
void apply(Project project) {
println 'apply AsmPlugin'
mConfig = project.extensions.create("asmConfig", AsmConfigModel.class)
}
  1. 在使用该插件的module下的build.gradle文件中配置
1
2
3
4
asmConfig {
startWithPatterns = ['com.shanbay']
excludesByFile = new File(projectDir, "asm-excludes.txt")
}
  1. 新建asm-excludes.txt文件,配置exclude信息
1
com/xxx/xxx/BaseActivity

这里是举个例子,在工程中很有可能有的Activity继承自一些基类Activity,对这些类插桩就重复了

使用Transform Api

根据官网介绍,Transform Api允许第三方 Plugin 在打包 dex 文件之前的编译过程中操作.class 文件,下图是Transform Api的工作流程

Transform Api工作流程

可以看到,一次App的编译打包可能会经历多次TransformTransform将输入进行处理,然后写入到指定的目录下作为下一个 Transform 的输入源。

使用插桩工具,我们需要借助于Transform Api实现

  1. 首先,我们需要让我们的插件继承自Transform
  2. 然后,我们要在插件apply时注册Transform
1
2
3
4
5
6
7
@Override
void apply(Project project) {
println 'apply AsmPlugin'
def android = project.extensions.getByType(AppExtension.class)
android.registerTransform(this)
mConfig = project.extensions.create("asmConfig", AsmConfigModel.class)
}
  1. 最后,需要实现Transform类中的抽象方法

Transform抽象方法

  • getName 这个方法是指定这个Transform的名称
1
2
3
4
@Override
String getName() {
return 'AsmPlugin'
}
  • getInputTypes 这个方法是指定输入类型

Transform输入类型

Transform输入类型

这里,我们选用TransformManager.CONTENT_CLASS就可以了

  • getScopes 这个方法是指定插桩的作用域

Transform作用域

Transform作用域

这里我们选择TransformManager.SCOPE_FULL_PROJECT,代表插桩范围包括此工程和它依赖的所有包

  • isIncremental 这个方法代表是否开启增量编译

如果开启的话可以减少编译时间,但需要增加额外的判断条件,所以这里就先不开启了

  • transform 这个方法是核心方法,我们要对输入内容进行处理然后输出

transform()方法的参数 TransformInvocation 是一个接口,提供了一些关于输入输出的一些基本信息。下图是transform中我们需要走的流程

Transform流程

这里以directoryInputs举例,directoryInputs就是本地源码编译后产生的class文件

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
private void handleDirectory(DirectoryInput input, TransformOutputProvider outputProvider) {
File file = input.file

if (file.isDirectory()) {
//递归遍历该文件夹下面所有的子文件夹以及子文件
file.eachFileRecurse { subFile ->
def fileName = subFile.name
//初步判断这个文件(或文件夹)是否可插桩
if (fileName.endsWith(".class") && !fileName.startsWith("R$")
&& "R.class" != fileName && "BuildConfig.class" != fileName) {
//ClassReader: 字节码的读取与分析引擎
ClassReader classReader = new ClassReader(subFile.bytes)
//ClassWriter: 它实现了ClassVisitor接口,用于拼接字节码
//COMPUTE_MAXS: 自动计算栈的最大值以及本地变量的最大数量
//COMPUTE_FRAMES: 包含COMPUTE_MAXS,且会自动计算方法的栈桢
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
//ClassVisitor: 定义在读取Class字节码时会触发的事件,如类头解析完成、注解解析、字段解析、方法解析等
ClassVisitor cv = new AsmClassVisitor(api, classWriter, mConfig)
//使给定的ClassVisitor访问传递给此构造函数的jvm类文件结构
//EXPAND_FRAMES: 展开栈帧的标志位
classReader.accept(cv, ClassReader.EXPAND_FRAMES)
FileOutputStream fos = new FileOutputStream(
subFile.parentFile.absolutePath + File.separator + fileName)
fos.write(classWriter.toByteArray())
fos.close()
}
}
}

def dest = outputProvider.getContentLocation(
input.name,
input.contentTypes,
input.scopes,
Format.DIRECTORY
)
FileUtils.copyDirectoryToDirectory(file, dest)
}

可以用以下流程图大概描述一下一个class文件的修改过程

class文件修改流程

自定义ClassVisitor

我们开始继承ClassVisitor来实现我们对类的修改

读取配置

读取配置

访问类

访问类方法

通过这个方法我们可以获得这个类的访问控制,全限定类名,父类名,实现的接口名等信息

这里,我们通过全限定类名和读取出的配置做比对,进一步验证是否需要对此类进行插桩

验证类是否可插桩

验证类是否可插桩

访问类内方法

访问类内方法

通过这个方法我们可以获得这个类的所有方法的名称和描述符,我们通过它们来判断该方法是否需要插桩

判断方法是否需要插桩

如果有需要插桩的方法,就将mNeedStubClass标志位置为true,这个标识是为了我们后续判断是否要在该类中插入成员变量,然后使用我们自定义的MethodVisitor替换原始的MethodVisitor

插入成员变量

插入成员变量

在最后,如果有需要插桩的方法,我们需要将private long _$_timeRecorder这个成员变量插入到类中去

自定义MethodVisitor

之前说了,Java 字节码指令是基于栈操作的,基本上任何操作都会改变栈状态

在方法执行之前插入代码

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
/**
* 以下代码会以栈的形式注释出来,以左边为栈顶,右边为栈底
* 示例:[栈顶 <------------------> 栈底]
* [this, StringBuilder, System.out]
* 此时,this为栈顶,System.out为栈底
*/
@Override
public void visitCode() {

/*
假设此时栈为空
*/

//aload_0: 将this压入栈顶
mv.visitVarInsn(Opcodes.ALOAD, 0);

/*
此时栈内容:
[this]
*/

//invokestatic: 调用静态方法System.currentTimeMillis(),返回值为基础类型long
//第二个参数代表类的全限定名,第三个参数代表方法名,第四个参数代表函数签名,()J的意思是不接受参数,返回值为J (J在字节码里代表基础类型long)
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);

/*
此时栈内容:
[System.currentTimeMillis()的结果值, this]
*/

//lneg: 将栈顶的long类型取负并将结果压入栈顶
mv.visitInsn(Opcodes.LNEG);

/*
此时栈内容:
[System.currentTimeMillis()的结果值取负, this]
*/

//putfield: 为该类的此实例变量赋值
//以(栈顶 - 1)为执行对象,为其赋值为栈顶值 (this._$_timeRecorder = -System.currentTimeMillis())
mv.visitFieldInsn(Opcodes.PUTFIELD, mClassName, TIMER_NAME, "J");
super.visitCode();
}

在方法return之前插入代码

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
/**
* 以下代码会以栈的形式注释出来,以左边为栈顶,右边为栈底
* 示例:[栈顶 <------------------> 栈底]
* [this, StringBuilder, System.out]
* this为栈顶,System.out为栈底
*/
@Override
public void visitInsn(int opcode) {
if (opcode == Opcodes.RETURN) {
Label labelEnd = new Label();

/*
假设此时栈为空
*/

//aload_0: 将this压入栈顶
mv.visitVarInsn(Opcodes.ALOAD, 0);
//dup: 将栈顶的值复制一份压入栈顶
mv.visitInsn(Opcodes.DUP);

/*
此时栈内容:
[this, this]
*/

//以当前栈顶的值为主体,获取当前类的成员变量_$_timeRecorder,类型为long
//相当于this._$_timeRecorder
mv.visitFieldInsn(Opcodes.GETFIELD, mClassName, TIMER_NAME, "J");

/*
此时栈内容:
[this._$_timeRecorder, this]
*/

//执行System.currentTimeMillis(),并将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);

/*
此时栈内容:
[System.currentTimeMillis()执行后的结果值, this._$_timeRecorder, this]
*/

//将栈顶两long值相加,并将结果压入栈顶
//即this._$_timeRecorder + System.currentTimeMillis
mv.visitInsn(Opcodes.LADD);

/*
此时栈内容:
[System.currentTimeMillis() + this._$_timeRecorder, this]
*/

//将栈顶的值存入(栈顶 - 1)._$_timeRecorder中
//即this._$_timeRecorder = this._$_timeRecorder + System.currentTimeMillis
mv.visitFieldInsn(Opcodes.PUTFIELD, mClassName, TIMER_NAME, "J");

/*
此时栈为空
*/

//L: 对象类型,以分号结尾,如Ljava/lang/Object;
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");

/*
此时栈内容:
[System.out]
*/

//构建字符串
//创建一个StringBuilder对象,此时还并没有执行构造方法
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
//因为执行构造函数会将栈顶的StringBuilder对象弹出,为了后续能继续使用这个对象,所以这里需要先复制一份
mv.visitInsn(Opcodes.DUP);

/*
此时栈内容:
[StringBuilder, StringBuilder, System.out]
*/

//以栈顶的StringBuilder调用构造方法
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);

/*
此时栈内容:
[StringBuilder, System.out]
*/

//将常量压入栈顶
mv.visitLdcInsn("Time spent: ");

/*
此时栈内容:
["Time spent: ", StringBuilder, System.out]
*/

//以栈顶的值为参数,(栈顶 - 1)的引用为主体执行StringBuilder.append()方法,将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

/*
此时栈内容:
[StringBuilder, System.out]
*/

//将this压入栈顶
mv.visitVarInsn(Opcodes.ALOAD, 0);

/*
此时栈内容:
[this, StringBuilder, System.out]
*/

//以当前栈顶的值为主体,获取当前类的成员变量_$_timeRecorder,类型为long
//相当于this._$_timeRecorder
mv.visitFieldInsn(Opcodes.GETFIELD, mClassName, TIMER_NAME, "J");

/*
此时栈内容:
[this._$_timeRecorder, StringBuilder, System.out]
*/

//以栈顶的值为参数,(栈顶 - 1)的引用为主体执行StringBuilder.append()方法,将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false);

/*
此时栈内容:
[StringBuilder, System.out]
*/

//将常量压入栈顶
mv.visitLdcInsn("ms, when " + mFormatClassName + "." + mMethodName + ":" + mMethodDescriptor);

/*
此时栈内容:
[字符串常量, StringBuilder, System.out]
*/

//以栈顶的值为参数,(栈顶 - 1)的引用为主体执行StringBuilder.append()方法,将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

/*
此时栈内容:
[StringBuilder, System.out]
*/

//以栈顶的值为主体,执行StringBuilder.toString()方法,将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);

/*
此时栈内容:
[String, System.out]
*/

//以栈顶的值为参数,(栈顶 - 1)的引用为主体执行PrintStream.println()方法
//相当于System.out.println(String)
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

/*
此时栈为空
*/

//执行Thread.currentThread(),并将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;", false);

/*
此时栈内容:
[Thread.currentThread()执行的结果]
*/

//以栈顶的值为主体,执行getStackTrace()方法,将返回值压入栈顶
//相当于Thread.currentThread().getStackTrace()
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Thread", "getStackTrace", "()[Ljava/lang/StackTraceElement;", false);

/*
此时栈内容:
[StackTraceElement数组]
*/

//astore: 将一个引用类型对象保存到局部变量表index为2的位置(index1: this, index2: onCreate方法的参数)
//使用一个临时变量保存StackTraceElement数组
mv.visitVarInsn(Opcodes.ASTORE, 2);
//将局部变量表index2处的引用对象压入栈顶
mv.visitVarInsn(Opcodes.ALOAD, 2);

/*
此时栈内容:
[StackTraceElement数组]
此时局部变量表中:
[ 0 1 2 ]
[this | Bundle | StackTraceElement数组]
*/

//StackTraceElement数组备份
mv.visitVarInsn(Opcodes.ASTORE, 3);
mv.visitVarInsn(Opcodes.ALOAD, 3);

/*
此时栈内容:
[StackTraceElement数组]
此时局部变量表中:
[ 0 1 2 3 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组]
*/

//获得栈顶位置数组的长度
mv.visitInsn(Opcodes.ARRAYLENGTH);

/*
此时栈内容:
[StackTraceElement数组长度]
此时局部变量表中:
[ 0 1 2 3 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组]
*/

//将数组length保存至局部变量表index4的位置
mv.visitVarInsn(Opcodes.ISTORE, 4);

/*
此时栈为空
此时局部变量表中:
[ 0 1 2 3 4 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度]
*/

//将int常量0压入栈顶
mv.visitInsn(Opcodes.ICONST_0);
//将栈顶的0取出保存(用作循环下标index)
mv.visitVarInsn(Opcodes.ISTORE, 5);

/*
此时栈为空
此时局部变量表中:
[ 0 1 2 3 4 5 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index]
*/

//循环开始处
//插入一个label用来做后续循环跳转的标志
Label labelLoop = new Label();
mv.visitLabel(labelLoop);
//将循环标志位的值压入栈顶
mv.visitVarInsn(Opcodes.ILOAD, 5);
//将数组长度值压入栈顶
mv.visitVarInsn(Opcodes.ILOAD, 4);

/*
此时栈内容:
[循环标志位, 数组长度]
此时局部变量表中:
[ 0 1 2 3 4 5 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index]
*/

//if_icmpge: 比较栈顶两int型数值大小, 当结果大于等于0时跳转
mv.visitJumpInsn(Opcodes.IF_ICMPGE, labelEnd);

/*
此时栈为空
此时局部变量表中:
[ 0 1 2 3 4 5 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index]
*/

//将StackTraceElement数组压入栈顶
mv.visitVarInsn(Opcodes.ALOAD, 3);
//将循环index的值压入栈顶
mv.visitVarInsn(Opcodes.ILOAD, 5);

/*
此时栈内容:
[循环index, StackTraceElement数组]
此时局部变量表中:
[ 0 1 2 3 4 5 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index]
*/

//将引用类型数组指定索引的值推送至栈顶(var3[var5])
mv.visitInsn(Opcodes.AALOAD);

/*
此时栈内容:
[StackTraceElement数组中的某个值(以循环index作为下标)]
此时局部变量表中:
[ 0 1 2 3 4 5 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index]
*/

//将该索引下的值保存
mv.visitVarInsn(Opcodes.ASTORE, 6);

/*
此时栈为空
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//将System.out入栈
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");

/*
此时栈内容:
[System.out]
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//new StringBuilder()
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);

/*
此时栈内容:
[StringBuilder, System.out]
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//取出StackTraceElement数组中的某个值(以循环index作为下标)
mv.visitVarInsn(Opcodes.ALOAD, 6);

/*
此时栈内容:
[StackTraceElement数组中的某个值(以循环index作为下标), StringBuilder, System.out]
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//使用栈顶对象,执行getClassName方法,将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StackTraceElement", "getClassName", "()Ljava/lang/String;", false);

/*
此时栈内容:
[ClassName, StringBuilder, System.out]
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//以ClassName作为参数,执行(栈顶 - 1)对象的append方法,将返回值压入栈顶
//即StringBuilder.append(ClassName)
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

/*
此时栈内容:
[StringBuilder, System.out]
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//将常量入栈
mv.visitLdcInsn(".");
//以常量作为参数,执行(栈顶 - 1)对象的append方法,将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

/*
此时栈内容:
[StringBuilder, System.out]
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//将StackTraceElement数组中的某个值(以循环index作为下标)入栈
mv.visitVarInsn(Opcodes.ALOAD, 6);
//调用它的getMethodName方法,将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StackTraceElement", "getMethodName", "()Ljava/lang/String;", false);

/*
此时栈内容:
[MethodName, StringBuilder, System.out]
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//以MethodName作为参数,执行(栈顶 - 1)对象的append方法,将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

/*
此时栈内容:
[StringBuilder, System.out]
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//将常量入栈
mv.visitLdcInsn(":");
//以常量作为参数,执行(栈顶 - 1)对象的append方法,将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);

/*
此时栈内容:
[StringBuilder, System.out]
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//将StackTraceElement数组中的某个值(以循环index作为下标)入栈
mv.visitVarInsn(Opcodes.ALOAD, 6);
//调用它的getLineNumber方法,将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StackTraceElement", "getLineNumber", "()I", false);

/*
此时栈内容:
[LineNumber, StringBuilder, System.out]
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//以LineNumber作为参数,执行(栈顶 - 1)对象的append方法,将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", false);

/*
此时栈内容:
[StringBuilder, System.out]
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//调用栈顶对象的toString方法,将返回值压入栈顶
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);

/*
此时栈内容:
[String, System.out]
此时局部变量表中:
[ 0 1 2 3 4 5 6 ]
[this | Bundle | StackTraceElement数组 | StackTraceElement数组 | 数组长度 | 循环index | StackTraceElement数组中的某个值(以循环index作为下标)]
*/

//以String作为参数,执行(栈顶 - 1)对象System.out的println方法
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

//iinc: 将指定int型变量增加指定值(index++)
mv.visitIincInsn(5, 1);
//跳转到labelLoop插入的位置
mv.visitJumpInsn(Opcodes.GOTO, labelLoop);

//插入结束Label,作为循环终止的跳转标志
mv.visitLabel(labelEnd);
}

super.visitInsn(opcode);
}

这样我们的方法插桩工作就完成了,接下来我们运行一下看看

运行

clean build,再build,查看控制台信息,build完成后查看class文件

运行App,查看Logcat信息,可以看到打印出来了我们想要的信息。

结语

这样我们就通过插桩的方式,实现了一个简单的无任何代码侵入的性能检测工具

通过这一次实践,我对java的编译运行字节码,Android的打包流程有了更深的理解

完整项目地址:https://github.com/dreamgyf/AsmPluginDemo


Android源码分析 - Binder驱动(中)

开篇

本篇以aosp分支android-11.0.0_r25,kernel分支android-msm-wahoo-4.4-android11作为基础解析

上一篇文章Android源码分析 - Binder驱动(上),我们已经了解了binder驱动设备是如何注册的,并且分析了binder_openbinder_mmap操作函数,接下来我们继续分析binder驱动中最重要的部分binder_ioctl

ioctl

我们先简单介绍一下ioctl函数,这个函数是用来控制设备的,函数原型如下:

1
int ioctl(int fd , unsigned long cmd , .../* args */);

第一个参数fd为设备的文件描述符

第二个参数cmd为命令码,它由驱动方自定义,用户通过命令码告诉设备驱动想要它做什么

后面为可选参数,具体内容和cmd有关,是传入驱动层的参数

命令码

Linux内核是这么定义一个命令码的

设备类型 序列号 方向 数据尺寸
8 bit 8 bit 2 bit 8~14 bit

这样,一个命令就变成了一个整数形式的命令码了,为了使用起来方便,Linux定义了一些生成命令码的宏:

1
2
3
4
_IO(type,nr)        //没有参数的命令
_IOR(type,nr,size) //从驱动中读数据
_IOW(type,nr,size) //写数据到驱动中
_IOWR(type,nr,size) //双向读写

binder驱动命令码

了解了ioctl和它的命令码后,我们来看看binder驱动定义了哪些命令码,以及它们分别有什么作用

binder驱动命令码被定义在include/uapi/linux/android/binder.h中,其中有几个貌似未使用,我就不列出来了

1
2
3
4
5
6
7
8
#define BINDER_WRITE_READ		_IOWR('b', 1, struct binder_write_read)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32)
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32)
#define BINDER_THREAD_EXIT _IOW('b', 8, __s32)
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
#define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info)
#define BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref)
#define BINDER_SET_CONTEXT_MGR_EXT _IOW('b', 13, struct flat_binder_object)
  • BINDER_WRITE_READ:读写命令,用于数据传输,binder IPC通信中的核心
  • BINDER_SET_MAX_THREADS:设置最大线程数
  • BINDER_SET_CONTEXT_MGR:设置成为binder上下文管理者
  • BINDER_THREAD_EXITbinder线程退出命令,释放相关资源
  • BINDER_VERSION:获取binder驱动版本号
  • BINDER_GET_NODE_DEBUG_INFO:获得binder节点的debug信息
  • BINDER_GET_NODE_INFO_FOR_REF:从binder引用获得binder节点信息
  • BINDER_SET_CONTEXT_MGR_EXT:和BINDER_SET_CONTEXT_MGR作用相同,携带额外参数

了解了这些binder驱动命令码,我们就可以开始正式分析binder_ioctl

binder_ioctl

这个函数位于drivers/android/binder.c文件中

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
46
47
48
49
50
51
52
53
54
55
56
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
//从命令参数中解析出用户数据大小
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
...
//进入休眠状态,等待被唤醒
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
//根据请求系统调用的线程的pid,查找对应的binder_thread,没有则新建一个
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}

switch (cmd) {
case BINDER_WRITE_READ:
...
break;
case BINDER_SET_MAX_THREADS: {
...
break;
}
case BINDER_SET_CONTEXT_MGR_EXT: {
...
break;
}
case BINDER_SET_CONTEXT_MGR:
...
break;
case BINDER_VERSION: {
...
break;
}
case BINDER_GET_NODE_INFO_FOR_REF: {
...
break;
}
case BINDER_GET_NODE_DEBUG_INFO: {
...
break;
}
default:
ret = -EINVAL;
goto err;
}
ret = 0;
err:
...
return ret;
}

从整体上来看还是比较清晰的,我们对一些点做一下详解

__user

__user是一个宏,它告诉编译器不应该解除这个指针的引用(因为在当前地址空间中它是没有意义的),(void __user *)arg表示arg是一个用户空间的地址,不能直接进行拷贝等,要使用copy_from_usercopy_to_user等函数。

wait_event_interruptible

wait_event_interruptible(wq, condition)是一个宏,它是用来挂起进程直到满足判断条件的

binder_stop_on_user_error是一个全局变量,它的初始值为0,binder_user_error_wait是一个等待队列

在正常情况下,binder_stop_on_user_error < 2这个条件是成立的,所以不会进入挂起状态,而当binder因为错误而停止后,调用binder_ioctl,则会挂起进程,直到其他进程通过wake_up_interruptible来唤醒binder_user_error_wait队列,并且满足binder_stop_on_user_error < 2这个条件,binder_ioctl才会继续往后运行

binder_thread结构体

我们需要关注一个重要的结构体binder_thread,它在后续的代码中会频繁的出现,这个结构体描述了进程中的工作线程

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
struct binder_thread {
//binder线程所属的进程
struct binder_proc *proc;
//红黑树节点
struct rb_node rb_node;
//链表节点
struct list_head waiting_thread_node;
//进程pid
int pid;
//描述了线程当前的状态
int looper; /* only modified by this thread */
bool looper_need_return; /* can be written by other thread */
//binder事务栈(链表形式,内部存在前后节点)
struct binder_transaction *transaction_stack;
//todo队列,为需要处理的工作的链表
struct list_head todo;
//binder_thread_write后是否立即执行完成binder_thread_read
//false的情况下会在binder_thread_read中休眠,延迟执行BINDER_WORK_TRANSACTION_COMPLETE
bool process_todo;
struct binder_error return_error;
struct binder_error reply_error;
//等待队列,当处理binder事务需要依赖别的binder事务的时候,则会以此等待队列睡眠
//直到它所依赖的binder事务完成后唤醒
wait_queue_head_t wait;
//统计信息
struct binder_stats stats;
//临时引用计数
atomic_t tmp_ref;
//是否死亡
bool is_dead;
//线程信息结构体
struct task_struct *task;
};

binder_get_thread

接下来我们看一下binder_ioctl是怎么获得binder_thread

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{
struct binder_thread *thread;
struct binder_thread *new_thread;

binder_inner_proc_lock(proc);
thread = binder_get_thread_ilocked(proc, NULL);
binder_inner_proc_unlock(proc);
if (!thread) {
new_thread = kzalloc(sizeof(*thread), GFP_KERNEL);
if (new_thread == NULL)
return NULL;
binder_inner_proc_lock(proc);
thread = binder_get_thread_ilocked(proc, new_thread);
binder_inner_proc_unlock(proc);
if (thread != new_thread)
kfree(new_thread);
}
return thread;
}

我们可以看到里面有锁操作,使用的就是上一章Android源码分析 - Binder驱动(上)中所介绍过的spinlock,使用的是binder_proc结构体中的inner_lock

简单浏览一下代码我们就可以知道,binder_get_thread首先试着从binder_proc获得binder_thread,如果没能获得,就新建一个,这两种情况都调用了binder_get_thread_ilocked函数

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
static struct binder_thread *binder_get_thread_ilocked(
struct binder_proc *proc, struct binder_thread *new_thread)
{
struct binder_thread *thread = NULL;
struct rb_node *parent = NULL;
struct rb_node **p = &proc->threads.rb_node;

while (*p) {
parent = *p;
thread = rb_entry(parent, struct binder_thread, rb_node);

if (current->pid < thread->pid)
p = &(*p)->rb_left;
else if (current->pid > thread->pid)
p = &(*p)->rb_right;
else
return thread;
}
if (!new_thread)
return NULL;
thread = new_thread;
//binder_thread对象创建计数加1
binder_stats_created(BINDER_STAT_THREAD);
thread->proc = proc;
thread->pid = current->pid;
//引用计数加1
get_task_struct(current);
thread->task = current;
atomic_set(&thread->tmp_ref, 0);
init_waitqueue_head(&thread->wait);
//初始化todo队列
INIT_LIST_HEAD(&thread->todo);
//插入红黑树
rb_link_node(&thread->rb_node, parent, p);
rb_insert_color(&thread->rb_node, &proc->threads);
thread->looper_need_return = true;
thread->return_error.work.type = BINDER_WORK_RETURN_ERROR;
thread->return_error.cmd = BR_OK;
thread->reply_error.work.type = BINDER_WORK_RETURN_ERROR;
thread->reply_error.cmd = BR_OK;
INIT_LIST_HEAD(&new_thread->waiting_thread_node);
return thread;
}

这个函数分为前后两个部分,前半部分通过binder_proc->threads这个红黑树查找当前系统调用进程pid所对应的binder_thread,后半部分初始化了传入的new_thread,并将其插入到红黑树中(binder_proc->threads


接下来就是判断命令码cmd,来执行相应的工作了,我们只分析比较重要的几个命令码

BINDER_WRITE_READ

binder驱动中最重要的命令码肯定非BINDER_WRITE_READ莫属了,这个命令用来进行binder读写交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
...
switch (cmd) {
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
...
default:
ret = -EINVAL;
goto err;
}
ret = 0;
err:
...
return ret;
}

switch case命令码后,直接调用了binder_ioctl_write_read函数

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;

//校验用户传入arg数据大小
if (size != sizeof(struct binder_write_read)) {
ret = -EINVAL;
goto out;
}
//将用户空间ubuf拷贝至内核空间bwr
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
...
//当写缓存中有数据,执行binder写操作
if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
if (ret < 0) {
//有错误发生,将已读数据大小设为0
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
//当读缓存中有数据,执行binder读操作
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
//如果todo队列中有未处理的任务,唤醒等待状态下的线程
binder_inner_proc_lock(proc);
if (!binder_worklist_empty_ilocked(&proc->todo))
binder_wakeup_proc_ilocked(proc);
binder_inner_proc_unlock(proc);
if (ret < 0) {
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
...
//将内核空间修改后的bwr拷贝至用户空间ubuf
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
out:
return ret;
}

binder_write_read结构体

BINDER_WRITE_READ命令码所接受的参数为一个binder_write_read结构体,我们先来了解一下它

1
2
3
4
5
6
7
8
struct binder_write_read {
binder_size_t write_size; /* bytes to write */
binder_size_t write_consumed; /* bytes consumed by driver */
binder_uintptr_t write_buffer;
binder_size_t read_size; /* bytes to read */
binder_size_t read_consumed; /* bytes consumed by driver */
binder_uintptr_t read_buffer;
};
  • write_size:写数据的总大小
  • write_consumed:已写数据大小
  • write_buffer:写数据的虚拟内存地址
  • read_size:读数据的总大小
  • read_consumed:已读数据大小
  • read_buffer:读数据的虚拟内存地址

整个binder_ioctl_write_read函数结构是比较简单的,首先校验了一下用户空间所传的参数argbinder_write_read结构体,接着将其从用户空间拷贝至内核空间bwr,接下来便是分别检查写缓存读缓存中是否有数据,有的话则执行相应的写读操作。这里需要注意的是,读写操作所传入的write_consumedread_consumed是以地址的形式,即会对这两个值进行修改,不管读写操作是否执行,成功或者失败,最后都会调用copy_to_userbwr从内核空间复制到用户空间ubuf

看到这里,可能有些同学会觉得有些奇怪,说好binder只进行一次复制的呢?其实是这样的没错,这里的copy_from_user或者copy_to_user只是复制了binder_write_read结构体,得到了需要IPC数据的虚拟内存地址而已,真正的复制操作是在binder读写操作中进行的

binder_thread_write

先看binder写操作,这个函数首先从传入的参数中,计算出待写的起始地址和结束地址,因为可能数据中含有多个命令和对应数据要处理,所以这里开了一个循环,在循环中,首先调用get_user,从用户空间读取一个值到内核空间中来,这个值就是binder请求码,然后将指针向后移动32位,使其指向对应请求码的数据头,接着根据binder请求码去完成不同的工作,处理完后修改已写数据大小

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
uint32_t cmd;
struct binder_context *context = proc->context;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
//可能含有多个命令和对应数据要处理
while (ptr < end && thread->return_error.cmd == BR_OK) {
int ret;

//获得binder请求码
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
//使指针指向数据头
ptr += sizeof(uint32_t);
trace_binder_command(cmd);
//记录binder数据信息
if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]);
atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]);
atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]);
}
//根据binder请求码,执行不同工作
switch (cmd) {
case BC_INCREFS:
case BC_ACQUIRE:
case BC_RELEASE:
case BC_DECREFS: {
...
break;
}
case BC_INCREFS_DONE:
case BC_ACQUIRE_DONE: {
...
break;
}
case BC_ATTEMPT_ACQUIRE:
pr_err("BC_ATTEMPT_ACQUIRE not supported\n");
return -EINVAL;
case BC_ACQUIRE_RESULT:
pr_err("BC_ACQUIRE_RESULT not supported\n");
return -EINVAL;

case BC_FREE_BUFFER: {
...
break;
}

case BC_TRANSACTION_SG:
case BC_REPLY_SG: {
...
break;
}
case BC_TRANSACTION:
case BC_REPLY: {
...
break;
}

case BC_REGISTER_LOOPER:
...
break;
case BC_ENTER_LOOPER:
...
break;
case BC_EXIT_LOOPER:
...
break;

case BC_REQUEST_DEATH_NOTIFICATION:
case BC_CLEAR_DEATH_NOTIFICATION: {
...
} break;
case BC_DEAD_BINDER_DONE: {
...
} break;

default:
pr_err("%d:%d unknown command %d\n",
proc->pid, thread->pid, cmd);
return -EINVAL;
}
//设置已写数据大小
*consumed = ptr - buffer;
}
return 0;
}

binder请求码

binder请求码用于用户空间程序向binder驱动发送请求消息,以BC开头,被定义在enum binder_driver_command_protocol中(include/uapi/linux/android/binder.h

命令 | 说明 | 参数类型 |
| —————————– | —————————- | ——————– |
| BC_TRANSACTION | Binder事务,即:Client对于Server的请求 | binder_transaction_data |
| BC_REPLY | 事务的应答,即:Server对于Client的回复 | binder_transaction_data |
| BC_FREE_BUFFER | 通知驱动释放Buffer | binder_uintptr_t |
| BC_ACQUIRE | 强引用计数+1 | __u32 |
| BC_RELEASE | 强引用计数-1 | __u32 |
| BC_INCREFS | 弱引用计数+1 | __u32 |
| BC_DECREFS | 弱引用计数-1 | __u32 |
| BC_ACQUIRE_DONE | BR_ACQUIRE的回复 | binder_ptr_cookie |
| BC_INCREFS_DONE | BR_INCREFS的回复 | binder_ptr_cookie |
| BC_ENTER_LOOPER | 通知驱动主线程ready | void |
| BC_REGISTER_LOOPER | 通知驱动子线程ready | void |
| BC_EXIT_LOOPER | 通知驱动线程已经退出 | void |
| BC_REQUEST_DEATH_NOTIFICATION | 请求接收死亡通知 | binder_handle_cookie |
| BC_CLEAR_DEATH_NOTIFICATION | 去除接收死亡通知 | binder_handle_cookie |
| BC_DEAD_BINDER_DONE | 已经处理完死亡通知 | binder_uintptr_t |
| BC_ATTEMPT_ACQUIRE | 暂不支持 | - |
| BC_ACQUIRE_RESULT | 暂不支持 | - |

其中,最重要且最频繁的操作为BC_TRANSACTION/BC_REPLY,我们就只分析一下这两个请求码做了什么事

binder_transaction

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
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
...
while (...) {
...
switch (cmd) {
...
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;

if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr,
cmd == BC_REPLY, 0);
break;
}
...
}
...
}

对于这两个请求码,首先从用户空间中复制了一份binder_transaction_data到内核空间,接着就调用binder_transaction函数继续处理

binder_transaction_data结构体

在分析binder_transaction函数前,我们需要先了解一些结构体

binder_transaction_data结构体就是BC_TRANSACTION/BC_REPLY所对应的参数类型,它被定义在include/uapi/linux/android/binder.h

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
struct binder_transaction_data {
union {
//当BINDER_WRITE_READ命令的目标对象非本地binder实体时,用handle表示对目标binder的引用
__u32 handle;
//当BINDER_WRITE_READ命令的目标对象是本地binder实体时,用此域成员变量表示这个对象在本进程中的内存地址
binder_uintptr_t ptr;
} target;
//目标binder实体所带的附加数据
binder_uintptr_t cookie;
//表示要对目标对象请求的命令代码
__u32 code;

//事务标志,详见enum transaction_flags
__u32 flags;
//发起请求的进程pid
pid_t sender_pid;
//发起请求的进程uid
uid_t sender_euid;
//真正要传输的数据的大小
binder_size_t data_size;
//偏移数组大小,这个偏移数组是用来描述数据区中,每一个binder对象的位置的
binder_size_t offsets_size;

union {
struct {
//数据区的首地址
binder_uintptr_t buffer;
//偏移数组的首地址,这个偏移数组是用来描述数据区中,每一个binder对象的位置的
//数组的每一项为一个binder_size_t,这个值对应着每一个binder对象相对buffer首地址的偏移
binder_uintptr_t offsets;
} ptr;
//数据较小的时候可以直接装在这个数组里
__u8 buf[8];
} data;
};

可以看到,真正需要拷贝的数据的地址是保存在data域中的,可能文字描述的data结构不是特别清晰,可以结合下图理解:

data结构

这里我用一个例子来解释一下binder_transaction_data传输的数据是什么样子的

小伙伴们应该都了解Parcel吧,它是一个存放读取数据的容器,我们binder_transaction_data中实际传输的数据就是通过它组合而成的,它可以传输基本数据类型,Parcelable类型和binder类型

其中基本数据类型就不用说了,每种基本类型所占用的大小是固定的,Parcelable类型实际上也是传输基本数据类型,它是通过实现Parcelable接口将一个复杂对象中的成员序列化成了一个个基本数据类型传输,而binder类型的传输有点特别,它会将这个binder对象 “压扁” 成一个flat_binder_object结构体传输

假设我们有一个客户端client,一个服务端serverclient想要向binder驱动发起一个事物,调用server的某个方法,我们该怎么构建binder_transaction_data的数据区呢?

一般来说,我们需要先写一个token,这个token是为了进行校验的,两端需要保持一致。接着我们需要按顺序依次写入参数,假设我们想要调用servercallMe(int, Parcelable, IBinder)函数,那我们就需要先写入一个int,再写入一个Parcelable,最后再将IBinder “压扁” 成一个flat_binder_object写入。

此时数据布局如下图所示:

data结构

从图中我们可以看出来,offsets指示出了buffer中传输的binder对象的位置,有几个binder对象,就会有几个offset与之对应

transaction_flags

我们再看一下有哪些事务标志,他们分别代表什么意思

1
2
3
4
5
6
7
8
9
10
enum transaction_flags {
//单向调用,异步操作,无返回
TF_ONE_WAY = 0x01,
//reply内容是一个组件的根对象,对应类型为本地binder
TF_ROOT_OBJECT = 0x04,
//reply内容是一个32位的状态码,对应类型为远程binder引用的句柄
TF_STATUS_CODE = 0x08,
//可以接收一个文件描述符,对应的类型为文件(BINDER_TYPE_FD),即handle中存储的为文件描述符
TF_ACCEPT_FDS = 0x10,
};

binder_transaction结构体

binder_transaction结构体用来描述进程间通信过程(事务),它被定义在drivers/android/binder.c

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
struct binder_transaction {
int debug_id;
//用来描述需要处理的工作事项
struct binder_work work;
//发起事务的线程
struct binder_thread *from;
//事务所依赖的另一个事务
struct binder_transaction *from_parent;
//处理该事务的进程
struct binder_proc *to_proc;
//处理该事务的线程
struct binder_thread *to_thread;
//目标线程下一个需要处理的事务
struct binder_transaction *to_parent;
//1: 表示同步事务,需要等待对方回复
//0: 表示异步事务
unsigned need_reply:1;

//指向为该事务分配的内核缓冲区
struct binder_buffer *buffer;
unsigned int code;
unsigned int flags;
//发起事务线程的优先级
struct binder_priority priority;
//线程在处理事务时,驱动会修改它的优先级以满足源线程和目标Service组建的要求
//在修改之前,会将它原来的线程优先级保存在该成员中,以便线程处理完该事务后可以恢复原来的优先级
struct binder_priority saved_priority;
bool set_priority_called;
kuid_t sender_euid;
binder_uintptr_t security_ctx;

spinlock_t lock;
};

binder_work结构体

binder_work结构体用来描述需要处理的工作事项,它被定义在drivers/android/binder.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct binder_work {
//双向链表中的一个节点,这个链表储存了所有的binder_work
struct list_head entry;

//工作项类型
enum binder_work_type {
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,
BINDER_WORK_RETURN_ERROR,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
} type;
};

简单看完了一些必要的结构体后,我们把目光转回binder_transaction函数上

binder_transaction函数的代码很长,我们精简一下,然后再分段来看,从整体上,我们可以将它分为几个部分:

  1. 获得目标进程/线程信息
  2. 将数据拷贝到目标进程所映射的内存中(此时会建立实际的映射关系)
  3. 将待处理的任务加入todo队列,唤醒目标线程

第一部分:获得目标进程/线程信息

这里根据是否为reply,分成了两种情况

BC_TRANSACTION

我们先看BC_TRANSACTION的情况

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size)
{
struct binder_proc *target_proc = NULL;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
uint32_t return_error = 0;
struct binder_context *context = proc->context;

if (reply) {
...
} else {
if (tr->target.handle) {
struct binder_ref *ref;

binder_proc_lock(proc);
//查找binder引用
ref = binder_get_ref_olocked(proc, tr->target.handle, true);
//通过目标binder实体获取目标进程信息
target_node = binder_get_node_refs_for_txn(
ref->node, &target_proc,
&return_error);
binder_proc_unlock(proc);
} else { //handle为0代表目标target是ServiceManager
mutex_lock(&context->context_mgr_node_lock);
//ServiceManager为binder驱动的context,所以可以直接从context中获取binder实体
target_node = context->binder_context_mgr_node;
if (target_node)
//通过目标binder实体获取目标进程信息
target_node = binder_get_node_refs_for_txn(
target_node, &target_proc,
&return_error);
else
return_error = BR_DEAD_REPLY;
mutex_unlock(&context->context_mgr_node_lock);
if (target_node && target_proc == proc) {
... //error
}
}
...
//使用LSM进行安全检查
if (security_binder_transaction(proc->tsk,
target_proc->tsk) < 0) {
... //error
}
binder_inner_proc_lock(proc);
//flags不带TF_ONE_WAY(即需要reply)并且当前线程存在binder事务栈
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
struct binder_transaction *tmp;

tmp = thread->transaction_stack;
if (tmp->to_thread != thread) {
... //error
}
//寻找一个合适的目标binder线程
while (tmp) {
struct binder_thread *from;

spin_lock(&tmp->lock);
from = tmp->from;
if (from && from->proc == target_proc) {
atomic_inc(&from->tmp_ref);
target_thread = from;
spin_unlock(&tmp->lock);
break;
}
spin_unlock(&tmp->lock);
tmp = tmp->from_parent;
}
}
binder_inner_proc_unlock(proc);
}
...
}

可以看到,虽然整个函数很长很复杂,但经过我们的拆分精简,逻辑就清晰很多了

binder_transaction_data.target.handle用一个int值表示目标binder引用,当它不为0时,调用binder_get_ref_olocked函数查找binder_ref

binder_get_ref_olocked
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static struct binder_ref *binder_get_ref_olocked(struct binder_proc *proc,
u32 desc, bool need_strong_ref)
{
struct rb_node *n = proc->refs_by_desc.rb_node;
struct binder_ref *ref;

while (n) {
ref = rb_entry(n, struct binder_ref, rb_node_desc);

if (desc < ref->data.desc) {
n = n->rb_left;
} else if (desc > ref->data.desc) {
n = n->rb_right;
} else if (need_strong_ref && !ref->data.strong) {
binder_user_error("tried to use weak ref as strong ref\n");
return NULL;
} else {
return ref;
}
}
return NULL;
}

可以看到,这个函数就是从binder_proc.refs_by_desc这个红黑树中,通过desc句柄查找到对应的binder引用binder_ref,这样就可以通过binder_ref.node获得到binder实体binder_node

接着再调用binder_get_node_refs_for_txn函数通过目标binder实体获取目标进程信息

binder_get_node_refs_for_txn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static struct binder_node *binder_get_node_refs_for_txn(
struct binder_node *node,
struct binder_proc **procp,
uint32_t *error)
{
struct binder_node *target_node = NULL;

binder_node_inner_lock(node);
if (node->proc) {
target_node = node;
//binder_node强引用计数加1
binder_inc_node_nilocked(node, 1, 0, NULL);
//binder_node临时引用计数加1
binder_inc_node_tmpref_ilocked(node);
//binder_proc临时引用计数加1
atomic_inc(&node->proc->tmp_ref);
//使外部传入的proc指针指向binder_proc地址
*procp = node->proc;
} else
*error = BR_DEAD_REPLY;
binder_node_inner_unlock(node);

return target_node;
}

这个函数第二个参数接受一个binder_proc **类型,即指向指针的指针,调用方对proc取地址,即指向proc指针分配在栈上的地址,这样函数中对procp解引用就得到了proc指针本身的地址,即可使proc指针指向binder_proc的地址

binder_transaction_data.target.handle为0时,表示目标是ServiceManager,而ServiceManagerbinder驱动的context,所以可以直接从context中获取binder实体,关于ServiceManager是怎么成为binder驱动的context的,我们会在后面的章节进行分析

接下来做一下安全检查,当flags不带TF_ONE_WAY(即需要reply)并且当前线程存在binder事务栈时,寻找一个合适的目标binder工作线程用来处理此事务(线程复用)

这里client端可能是第一次请求服务,此时binder_thread里是不存在binder事务栈,所以是没法找到目标binder线程的

BC_REPLY

接着,我们再看BC_REPLY的情况

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
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size)
{
struct binder_proc *target_proc = NULL;
struct binder_thread *target_thread = NULL;
struct binder_transaction *in_reply_to = NULL;

if (reply) {
binder_inner_proc_lock(proc);
//这个事务是发起事务,也就是说我们需要对这个事务做应答
in_reply_to = thread->transaction_stack;
if (in_reply_to == NULL) {
... //error
}
if (in_reply_to->to_thread != thread) {
... //error
}
//改指向下一个需要处理的事务,即将这个事务移出链表
thread->transaction_stack = in_reply_to->to_parent;
binder_inner_proc_unlock(proc);
//目标线程即为需要回应的事务的发起线程
target_thread = binder_get_txn_from_and_acq_inner(in_reply_to);
if (target_thread->transaction_stack != in_reply_to) {
... //error
}
//通过binder_thread获得binder_proc
target_proc = target_thread->proc;
atomic_inc(&target_proc->tmp_ref);
binder_inner_proc_unlock(target_thread->proc);
} else {
...
}
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static struct binder_thread *binder_get_txn_from_and_acq_inner(
struct binder_transaction *t)
{
struct binder_thread *from;

//相当于 from = t->from; 内部加了锁和引用计数操作
from = binder_get_txn_from(t);
if (!from)
return NULL;
binder_inner_proc_lock(from->proc);
if (t->from) {
BUG_ON(from != t->from);
return from;
}
binder_inner_proc_unlock(from->proc);
binder_thread_dec_tmpref(from);
return NULL;
}

BC_REPLY获取目标进程/线程信息就更简单了,BC_TRANSACTION中我们还需要根据binder句柄来获取各种信息,BC_REPLY我们只需要找到需要回应的那个事务,那个事务所在的线程和进程即为reply事务的目标线程和目标进程

第二部分:数据拷贝,建立映射

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size)
{
int ret;
struct binder_transaction *t;
struct binder_work *tcomplete;
binder_size_t *offp, *off_end, *off_start;
binder_size_t off_min;
u8 *sg_bufp, *sg_buf_end;
struct binder_proc *target_proc = NULL;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL
u32 secctx_sz = 0;

...

//为目标进程binder事务分配空间(后续会加到目标进程/线程的todo队列中,由目标进程/线程处理这个事务)
t = kzalloc(sizeof(*t), GFP_KERNEL);
spin_lock_init(&t->lock);

tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);

t->debug_id = t_debug_id;
//设置事务发起线程
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
else
t->from = NULL;
t->sender_euid = task_euid(proc->tsk);
//设置事务处理进程
t->to_proc = target_proc;
//设置事务处理线程
t->to_thread = target_thread;
t->code = tr->code;
t->flags = tr->flags;
//设置优先级
if (!(t->flags & TF_ONE_WAY) &&
binder_supported_policy(current->policy)) {
/* Inherit supported policies for synchronous transactions */
t->priority.sched_policy = current->policy;
t->priority.prio = current->normal_prio;
} else {
/* Otherwise, fall back to the default priority */
t->priority = target_proc->default_priority;
}

//安全相关
if (target_node && target_node->txn_security_ctx) {
...
}

//分配缓存,建立映射
t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
tr->offsets_size, extra_buffers_size,
!reply && (t->flags & TF_ONE_WAY));
t->buffer->debug_id = t->debug_id;
t->buffer->transaction = t;
t->buffer->target_node = target_node;

off_start = (binder_size_t *)(t->buffer->data +
ALIGN(tr->data_size, sizeof(void *)));
offp = off_start;

//这里就是真正的一次复制
copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size);
copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size);

//检查数据对齐
if (!IS_ALIGNED(tr->offsets_size, sizeof(binder_size_t))) {
... //error
}
if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) {
... //error
}
off_end = (void *)off_start + tr->offsets_size;
sg_bufp = (u8 *)(PTR_ALIGN(off_end, sizeof(void *)));
sg_buf_end = sg_bufp + extra_buffers_size -
ALIGN(secctx_sz, sizeof(u64));
off_min = 0;
//循环遍历每一个binder对象
for (; offp < off_end; offp++) {
struct binder_object_header *hdr;
size_t object_size = binder_validate_object(t->buffer, *offp);

if (object_size == 0 || *offp < off_min) {
... //error
}

hdr = (struct binder_object_header *)(t->buffer->data + *offp);
off_min = *offp + object_size;
switch (hdr->type) {
//需要对binder类型进行转换
//因为在A进程中为本地binder对象,对于B进程则为远程binder对象,反之亦然
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct flat_binder_object *fp;

fp = to_flat_binder_object(hdr);
ret = binder_translate_binder(fp, t, thread);
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
struct flat_binder_object *fp;

fp = to_flat_binder_object(hdr);
ret = binder_translate_handle(fp, t, thread);
} break;

case BINDER_TYPE_FD: {
...
} break;
case BINDER_TYPE_FDA: {
...
} break;
case BINDER_TYPE_PTR: {
...
} break;
default:
... //error
}
}
//设置工作类型
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
//设置目标进程的事务类型
t->work.type = BINDER_WORK_TRANSACTION;
...
}

我们可以将这一部分再细分成几个部分:

  1. 分配缓存,建立映射
  2. 数据拷贝
  3. binder类型转换
分配缓存,建立映射

我们首先看分配缓存,建立映射是怎么做的,它调用了binder_alloc_new_buf函数,这个函数定义在drivers/android/binder_alloc.c中,内部加了锁后调用了binder_alloc_new_buf_locked函数

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
static struct binder_buffer *binder_alloc_new_buf_locked(
struct binder_alloc *alloc,
size_t data_size,
size_t offsets_size,
size_t extra_buffers_size,
int is_async)
{
struct rb_node *n = alloc->free_buffers.rb_node;
struct binder_buffer *buffer;
size_t buffer_size;
struct rb_node *best_fit = NULL;
void *has_page_addr;
void *end_page_addr;
size_t size, data_offsets_size;
int ret;

if (alloc->vma == NULL) {
... //error
}

//计算需要的缓冲区大小
//这里需要将size对齐void *(32位下占用4字节,64位下占用8字节)
data_offsets_size = ALIGN(data_size, sizeof(void *)) +
ALIGN(offsets_size, sizeof(void *));
size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *));
size = max(size, sizeof(void *));

//从binder_alloc的空闲缓冲红黑树中找到一个大小最合适的binder_buffer
while (n) {
//当找到一个需求大小和缓存区大小刚好相同的空闲缓存区时
//此时buffer就正好指向这个空闲缓存区
buffer = rb_entry(n, struct binder_buffer, rb_node);
BUG_ON(!buffer->free);
buffer_size = binder_alloc_buffer_size(alloc, buffer);

//当只找到一个比需求大小稍大一点的空闲缓存区时
//此时buffer指向的是这个空闲缓存区所在节点的父节点
//然后n指向NULL
if (size < buffer_size) {
best_fit = n;
n = n->rb_left;
} else if (size > buffer_size)
n = n->rb_right;
else {
best_fit = n;
break;
}
}
if (best_fit == NULL) {
... //error
}
//此时buffer指向的是所需求的空闲缓存区所在红黑树节点的父节点
//需要让其指向真正需求的那个空闲缓存区
if (n == NULL) {
buffer = rb_entry(best_fit, struct binder_buffer, rb_node);
buffer_size = binder_alloc_buffer_size(alloc, buffer);
}

//计算出buffer的终点,向下对齐(不能超过可用的buffer_size)
has_page_addr =
(void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK);
WARN_ON(n && buffer_size != size);
//计算出实际上我们接收数据需要的空间的终点,向上映射
end_page_addr =
(void *)PAGE_ALIGN((uintptr_t)buffer->data + size);
//如果超出了可用的buffer_size,恢复到正常可用的结束地址
if (end_page_addr > has_page_addr)
end_page_addr = has_page_addr;
//分配物理页,建立映射
ret = binder_update_page_range(alloc, 1,
(void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr);
if (ret)
return ERR_PTR(ret);

//有空余空间的话,分隔这个buffer,剩余的buffer加入到空闲缓存区红黑树中(合理利用空间)
if (buffer_size != size) {
struct binder_buffer *new_buffer;

new_buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
new_buffer->data = (u8 *)buffer->data + size;
list_add(&new_buffer->entry, &buffer->entry);
new_buffer->free = 1;
binder_insert_free_buffer(alloc, new_buffer);
}

//我们已经使用了这个buffer,要将其从空闲缓存区红黑树中移除
rb_erase(best_fit, &alloc->free_buffers);
//标记为非空闲
buffer->free = 0;
buffer->allow_user_free = 0;
//插入到已分配缓存区红黑树中
binder_insert_allocated_buffer_locked(alloc, buffer);
buffer->data_size = data_size;
buffer->offsets_size = offsets_size;
buffer->async_transaction = is_async;
buffer->extra_buffers_size = extra_buffers_size;
//如果是异步事件, 那么更新binder_alloc的异步事件空闲buffer
if (is_async) {
alloc->free_async_space -= size + sizeof(struct binder_buffer);
}
return buffer;
...
}

这个函数的整体逻辑分为三个部分:

  1. 找到可用的空闲内核缓存区,计算我们需要分配的大小
  2. 分配物理页,建立映射
  3. 初始化新分配的buffer

其中1、3部分已经用注释标出来了,应该还是比较好理解的,我们终点看一下第2部分:怎么分配物理页,建立映射

我们在上一章Android源码分析 - Binder驱动(上)中说到,binder_mmap并没有立即将内核虚拟内存和进程虚拟内存与物理内存做映射,实际上这个映射操作是在binder_update_page_range这里做的

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
void *start, void *end)
{
void *page_addr;
unsigned long user_page_addr;
struct binder_lru_page *page;
struct vm_area_struct *vma = NULL;
struct mm_struct *mm = NULL;
bool need_mm = false;

if (end <= start)
return 0;

if (allocate == 0)
goto free_range;

//检查是否有页框需要分配
for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
if (!page->page_ptr) {
need_mm = true;
break;
}
}

//指向目标用户进程的内存空间描述体
if (need_mm && atomic_inc_not_zero(&alloc->vma_vm_mm->mm_users))
mm = alloc->vma_vm_mm;

if (mm) {
//获取mm_struct的读信号量
down_read(&mm->mmap_sem);
//检查mm是否有效
if (!mmget_still_valid(mm)) {
//释放
if (allocate == 0)
goto free_range;
//错误
goto err_no_vma;
}
vma = alloc->vma;
}

if (!vma && need_mm) {
... //error
}

for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
int ret;
bool on_lru;
size_t index;

//指向对应页框地址,为后面赋值做准备
index = (page_addr - alloc->buffer) / PAGE_SIZE;
page = &alloc->pages[index];

//page->page_ptr不为NULL说明之前已经分配并映射过了
if (page->page_ptr) {
on_lru = list_lru_del(&binder_alloc_lru, &page->lru);
continue;
}

//分配一个页的物理内存
page->page_ptr = alloc_page(GFP_KERNEL |
__GFP_HIGHMEM |
__GFP_ZERO);
//未分配成功
if (!page->page_ptr) {
... //error
}
page->alloc = alloc;
INIT_LIST_HEAD(&page->lru);

//将物理内存空间映射到内核虚拟内存空间
ret = map_kernel_range_noflush((unsigned long)page_addr,
PAGE_SIZE, PAGE_KERNEL,
&page->page_ptr);
flush_cache_vmap((unsigned long)page_addr,
(unsigned long)page_addr + PAGE_SIZE);

//根据之前计算的user_buffer_offset可以直接得到目标用户空间进程虚拟内存地址
user_page_addr =
(uintptr_t)page_addr + alloc->user_buffer_offset;
//将物理内存空间映射到目标用户进程虚拟内存空间
ret = vm_insert_page(vma, user_page_addr, page[0].page_ptr);

if (index + 1 > alloc->pages_high)
alloc->pages_high = index + 1;
}
if (mm) {
//释放mm_struct的读信号量
up_read(&mm->mmap_sem);
mmput(mm);
}
return 0;
... //错误处理
}

代码中的注释写的应该比较清楚了,总之就是先分配物理内存,再将这块物理内存分别映射到内核虚拟空间用户进程虚拟空间,这样内核虚拟空间用户进程虚拟空间相当于也间接的建立了映射关系

关于物理内存的分配以及映射,就是Linux内核层的事情了,感兴趣的同学可以再深入往里看看,这里就不再多说了

数据拷贝

关于数据拷贝这部分就不用多说了,物理内存已经分配好了,映射也建立了,接下来直接调用copy_from_user将数据从用户空间拷贝至映射的那块内存就可以了

binder类型转换

最后循环遍历每一个binder对象,对其中每一个binder对象类型做转换,因为在一个进程中为本地binder对象,对于另一个进程则为远程binder对象,反之亦然

flat_binder_object结构体

这里就是我们之前提到的,binder对象在传输过程中会被 “压扁” 的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct flat_binder_object {
//描述了binder对象的类型
struct binder_object_header hdr;
//和binder_transaction_data中flags含义相同
__u32 flags;

/* 8 bytes of data. */
union {
//当hdr.type == BINDER_TYPE_BINDER时,表示是一个binder实体对象,指向binder实体在用户空间的地址
binder_uintptr_t binder; /* local object */
//当hdr.type == BINDER_TYPE_HANDLE,表示是一个binder引用句柄
__u32 handle; /* remote object */
};

////当hdr.type == BINDER_TYPE_BINDER时才有值,表示携带的额外数据
/* extra data associated with local object */
binder_uintptr_t cookie;
};
binder_translate_binder

BINDER_TYPE_BINDER表示是一个binder实体对象,需要将它转换成binder引用句柄

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
46
47
48
49
50
51
static int binder_translate_binder(struct flat_binder_object *fp,
struct binder_transaction *t,
struct binder_thread *thread)
{
struct binder_node *node;
struct binder_proc *proc = thread->proc;
struct binder_proc *target_proc = t->to_proc;
struct binder_ref_data rdata;
int ret = 0;

//通过proc->nodes.rb_node红黑树查找binder_node
node = binder_get_node(proc, fp->binder);
//如果没有找到,新建一个binder_node并将其插入红黑树
if (!node) {
node = binder_new_node(proc, fp);
if (!node)
return -ENOMEM;
}

if (fp->cookie != node->cookie) {
... //error
}

//安全检查
if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
ret = -EPERM;
goto done;
}

//查找binder_ref并将其引用计数加1,如果没有查找到则创建一个,并将其插入红黑树
ret = binder_inc_ref_for_node(target_proc, node,
fp->hdr.type == BINDER_TYPE_BINDER,
&thread->todo, &rdata);
if (ret)
goto done;

//转换binder类型
if (fp->hdr.type == BINDER_TYPE_BINDER)
fp->hdr.type = BINDER_TYPE_HANDLE;
else
fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
fp->binder = 0;
//binder引用句柄赋值
fp->handle = rdata.desc;
fp->cookie = 0;

done:
//binder_node临时引用计数减1
binder_put_node(node);
return ret;
}
binder_translate_handle

BINDER_TYPE_HANDLE表示是一个binder引用句柄,,需要将它转换成binder实体对象

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
static int binder_translate_handle(struct flat_binder_object *fp,
struct binder_transaction *t,
struct binder_thread *thread)
{
struct binder_proc *proc = thread->proc;
struct binder_proc *target_proc = t->to_proc;
struct binder_node *node;
struct binder_ref_data src_rdata;
int ret = 0;

//从proc->refs_by_desc.rb_node红黑树中查找binder_node,并将其临时引用计数加1
node = binder_get_node_from_ref(proc, fp->handle,
fp->hdr.type == BINDER_TYPE_HANDLE, &src_rdata);

//安全检查
if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
ret = -EPERM;
goto done;
}

binder_node_lock(node);
//如果binder实体所在的进程为事务处理进程
if (node->proc == target_proc) {
//binder类型转换
if (fp->hdr.type == BINDER_TYPE_HANDLE)
fp->hdr.type = BINDER_TYPE_BINDER;
else
fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
fp->binder = node->ptr;
fp->cookie = node->cookie;
if (node->proc)
binder_inner_proc_lock(node->proc);
//binder强引用计数加1
binder_inc_node_nilocked(node,
fp->hdr.type == BINDER_TYPE_BINDER,
0, NULL);
if (node->proc)
binder_inner_proc_unlock(node->proc);
binder_node_unlock(node);
} else {
//重新查找binder_ref
struct binder_ref_data dest_rdata;

binder_node_unlock(node);
ret = binder_inc_ref_for_node(target_proc, node,
fp->hdr.type == BINDER_TYPE_HANDLE,
NULL, &dest_rdata);
if (ret)
goto done;

fp->binder = 0;
fp->handle = dest_rdata.desc;
fp->cookie = 0;
}
done:
//binder_node临时引用计数减1
binder_put_node(node);
return ret;
}

第三部分:加入todo队列,唤醒目标线程

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size)
{
struct binder_transaction *t;
struct binder_work *tcomplete;
struct binder_thread *target_thread = NULL;
struct binder_transaction *in_reply_to = NULL;

...

if (reply) { //如果请求码为BC_REPLY
//将tcomplete插入到事务发起binder线程的todo队列中
binder_enqueue_thread_work(thread, tcomplete);
binder_inner_proc_lock(target_proc);
if (target_thread->is_dead) {
binder_inner_proc_unlock(target_proc);
goto err_dead_proc_or_thread;
}
//将发起事务从目标binder线程的事务链表中移除
binder_pop_transaction_ilocked(target_thread, in_reply_to);
//将t->work插入到目标binder线程的todo队列中
binder_enqueue_thread_work_ilocked(target_thread, &t->work);
binder_inner_proc_unlock(target_proc);
//唤醒目标binder线程的等待队列
wake_up_interruptible_sync(&target_thread->wait);
//恢复发起事务的优先级
binder_restore_priority(current, in_reply_to->saved_priority);
//释放发起事务
binder_free_transaction(in_reply_to);
} else if (!(t->flags & TF_ONE_WAY)) { //如果请求码为BC_TRANSACTION并且不为异步操作,需要返回
binder_inner_proc_lock(proc);
//将tcomplete插入到事务发起binder线程的todo队列中(这里会延迟执行BINDER_WORK_TRANSACTION_COMPLETE)
binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete);
//设置为需要回应
t->need_reply = 1;
//插入事务链表中
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t;
binder_inner_proc_unlock(proc);
//将t->work插入目标线程的todo队列中并唤醒目标进程
if (!binder_proc_transaction(t, target_proc, target_thread)) {
binder_inner_proc_lock(proc);
//出错后,移除该事务
binder_pop_transaction_ilocked(thread, t);
binder_inner_proc_unlock(proc);
goto err_dead_proc_or_thread;
}
} else { //如果请求码为BC_TRANSACTION并且为异步操作,不需要返回
//将tcomplete插入到事务发起binder线程的todo队列中
binder_enqueue_thread_work(thread, tcomplete);
//将t->work插入目标进程的某个线程(或目标进程)的todo队列中并唤醒目标进程
if (!binder_proc_transaction(t, target_proc, NULL))
goto err_dead_proc_or_thread;
}

//减临时引用计数
if (target_thread)
binder_thread_dec_tmpref(target_thread);
binder_proc_dec_tmpref(target_proc);
if (target_node)
binder_dec_node_tmpref(target_node);

return;
... //错误处理
}

这一块的代码基本上格式都是一样的,都是将tcomplete插入到事务发起binder线程的todo队列中,t->work插入到目标binder线程的todo队列中,最后唤醒目标进程

这里需要注意的是,在BC_TRANSACTION的情况下,需要区分事务的flags中是否包含TF_ONE_WAY,这意味着这个事务是否需要回应

在没有TF_ONE_WAY的情况下,会使用binder_enqueue_deferred_thread_work_ilocked函数将tcomplete插入到事务发起binder线程的todo队列中,这个函数区别于binder_enqueue_thread_work_ilocked函数,它没有将thread->process_todo设为true,这个标记在之前介绍binder_thread结构体的时候提到了,当其为false的情况下会在binder_thread_read中休眠,延迟执行BINDER_WORK_TRANSACTION_COMPLETE,具体是怎么操作的,我们会在后续的binder_thread_read函数中进行分析

TF_ONE_WAY的情况下,我们是没有去寻找合适的目标处理binder线程的,关于这一点,我们需要看一下binder_proc_transaction函数是怎么处理没有传入binder_thread的情况的

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
static bool binder_proc_transaction(struct binder_transaction *t,
struct binder_proc *proc,
struct binder_thread *thread)
{
struct binder_node *node = t->buffer->target_node;
struct binder_priority node_prio;
bool oneway = !!(t->flags & TF_ONE_WAY);
bool pending_async = false;

binder_node_lock(node);
node_prio.prio = node->min_priority;
node_prio.sched_policy = node->sched_policy;

//如果设置了TF_ONE_WAY标志
if (oneway) {
if (node->has_async_transaction) {
//如果binder实体对象正在处理一个异步事务,做一个标记
pending_async = true;
} else {
//如果binder实体对象没有正在处理一个异步事务,将has_async_transaction置为true,表示接下来要处理一个异步任务
node->has_async_transaction = true;
}
}

binder_inner_proc_lock(proc);

//如果目标进程死亡或者目标线程不为NULL且死亡
if (proc->is_dead || (thread && thread->is_dead)) {
binder_inner_proc_unlock(proc);
binder_node_unlock(node);
return false;
}

//如果没有传入目标线程,且目标binder实体对象没有正在处理一个异步事务
if (!thread && !pending_async)
//从proc->waiting_threads链表中取出第一个节点元素(没有的话则为NULL)
thread = binder_select_thread_ilocked(proc);

if (thread) { //当找到了合适的binder线程
//设置事务优先级
binder_transaction_priority(thread->task, t, node_prio,
node->inherit_rt);
//将t->work插入到目标binder线程的todo队列中
binder_enqueue_thread_work_ilocked(thread, &t->work);
} else if (!pending_async) { //没有找到合适的binder线程,且目标binder实体对象没有正在处理一个异步事务
//将t->work加入到目标binder进程的todo队列中
binder_enqueue_work_ilocked(&t->work, &proc->todo);
} else { //没有找到合适的binder线程,且目标binder实体对象正在处理一个异步事务
//将t->work加入到目标binder实体的async_todo队列中
binder_enqueue_work_ilocked(&t->work, &node->async_todo);
}

//目标binder实体对象没有正在处理一个异步事务
if (!pending_async)
//唤醒目标binder线程的等待队列
binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);

binder_inner_proc_unlock(proc);
binder_node_unlock(node);

return true;
}

当没有传入目标binder线程时,从目标进程的等待线程链表中取出第一个binder_thread作为处理线程处理该事务,如果没找到合适的空闲线程,分为两种情况:

  1. 目标binder实体对象正在处理一个异步事务:将相应的binder_work插入到目标binder实体的async_todo队列中
  2. 目标binder实体对象没有正在处理一个异步事务:将相应的binder_work插入到目标binder进程的todo队列中

关于binder驱动是怎么从这些todo队列取出binder_work并处理的,我们马上在后面binder_thread_read里分析,这里我们最后再看一下如何唤醒目标binder线程的等待队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void binder_wakeup_thread_ilocked(struct binder_proc *proc,
struct binder_thread *thread,
bool sync)
{
assert_spin_locked(&proc->inner_lock);

if (thread) {
if (sync)
wake_up_interruptible_sync(&thread->wait);
else
wake_up_interruptible(&thread->wait);
return;
}

//没有找到一个可用的等待线程,可能在两种情况下发生:
//1. 所有线程都忙于处理事务
//在这种情况下,这些线程中的一个应该很快回调到内核驱动程序并执行这项工作
//2. 线程正在使用epoll轮询,在这种情况下,它们可能在没有被添加到waiting_threads的情况下被阻塞在等待队列上
//对于这种情况,我们只循环获取所有不处理事务工作的线程,并将它们全部唤醒
binder_wakeup_poll_threads_ilocked(proc, sync);
}

这个函数也有可能binder_thread参数传入NULL,在这种情况下,我们需要循环获取目标进程下的所有binder线程,对所有不处理事务工作的线程全部执行唤醒操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void binder_wakeup_poll_threads_ilocked(struct binder_proc *proc,
bool sync)
{
struct rb_node *n;
struct binder_thread *thread;

for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) {
thread = rb_entry(n, struct binder_thread, rb_node);
if (thread->looper & BINDER_LOOPER_STATE_POLL &&
binder_available_for_proc_work_ilocked(thread)) {
if (sync)
wake_up_interruptible_sync(&thread->wait);
else
wake_up_interruptible(&thread->wait);
}
}
}

总结

到这里,我们已经分析了binder_ioctl函数的一半binder_thread_write,了解了一些相关的数据结构,并且补充了binder_mmap篇未完成的内存映射的分析,大家应该对binder请求的发起与调度有了一个初步的认识了

本来这一篇是打算把整个binder_ioctl分析完的,但没想到写到后面内容这么多,只好再分一篇,下一篇我们将分析binder_thread_read,将binder驱动篇完结


Android源码分析 - Binder驱动(上)

开篇

本篇以aosp分支android-11.0.0_r25,kernel分支android-msm-wahoo-4.4-android11作为基础解析

上一篇文章Android源码分析 - Binder概述我们大概了解了一下Android选用Binder的原因,以及Binder的基本结构和通信过程。今天,我们便开始从Binder驱动层代码开始分析Binder的机制

提示

Binder驱动部分代码不在AOSP项目中,所以我们需要单独clone一份驱动代码

由于我的开发设备是pixel2,查了Linux内核版本号为4.4.223,对应的分支为android-msm-wahoo-4.4-android11,所以今天的分析我们也是基于此分支

我是从清华大学镜像站clone的代码,高通的设备,所以地址为:https://aosp.tuna.tsinghua.edu.cn/android/kernel/msm.git

初始化

binder驱动的源码位于drivers/android目录下,我们从binder.c文件看起

Linux initcall机制

binder.c的最底下,我们可以看到这一行代码

1
device_initcall(binder_init);

Linux内核中,驱动程序通常是用xxx_initcall(fn)启动的,这实际上是一个宏定义,被定义在平台对应的init.h文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define early_initcall(fn) __define_initcall(fn, early)
#define pure_initcall(fn) __define_initcall(fn, 0)
#define core_initcall(fn) __define_initcall(fn, 1)
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
#define postcore_initcall(fn) __define_initcall(fn, 2)
#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
#define arch_initcall(fn) __define_initcall(fn, 3)
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
#define subsys_initcall(fn) __define_initcall(fn, 4)
#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
#define fs_initcall(fn) __define_initcall(fn, 5)
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
#define device_initcall(fn) __define_initcall(fn, 6)
#define device_initcall_sync(fn) __define_initcall(fn, 6s)
#define late_initcall(fn) __define_initcall(fn, 7)
#define late_initcall_sync(fn) __define_initcall(fn, 7s)

可以看到,实际上调用的是__define_initcall()函数,这个函数的第二个参数表示优先级,数字越小,优先级越高,带s的优先级低于不带s的优先级

在Linux内核启动过程中,需要调用各种函数,在底层实现是通过在内核镜像文件中,自定义一个段,这个段里面专门用来存放这些初始化函数的地址,内核启动时,只需要在这个段地址处取出函数指针,一个个执行即可,而__define_initcall()函数,就是将自定义的init函数添加到上述段中

binder_init

了解了以上函数定义后,我们再回头看device_initcall(binder_init)就可以知道,在Linux内核启动时,会调用binder_init这么一个函数

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
static int __init binder_init(void)
{
int ret;
char *device_name, *device_names, *device_tmp;
struct binder_device *device;
struct hlist_node *tmp;

//初始化binder内存回收
ret = binder_alloc_shrinker_init();
if (ret)
return ret;

...
//创建一个单线程工作队列,用于处理异步任务
binder_deferred_workqueue = create_singlethread_workqueue("binder");
if (!binder_deferred_workqueue)
return -ENOMEM;

//创建binder/proc目录
binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
if (binder_debugfs_dir_entry_root)
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
binder_debugfs_dir_entry_root);
//在binder目录下创建5个文件
if (binder_debugfs_dir_entry_root) {
debugfs_create_file("state",
0444,
binder_debugfs_dir_entry_root,
NULL,
&binder_state_fops);
debugfs_create_file("stats",
0444,
binder_debugfs_dir_entry_root,
NULL,
&binder_stats_fops);
debugfs_create_file("transactions",
0444,
binder_debugfs_dir_entry_root,
NULL,
&binder_transactions_fops);
debugfs_create_file("transaction_log",
0444,
binder_debugfs_dir_entry_root,
&binder_transaction_log,
&binder_transaction_log_fops);
debugfs_create_file("failed_transaction_log",
0444,
binder_debugfs_dir_entry_root,
&binder_transaction_log_failed,
&binder_transaction_log_fops);
}

//"binder,hwbinder,vndbinder"
device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL);
if (!device_names) {
ret = -ENOMEM;
goto err_alloc_device_names_failed;
}
strcpy(device_names, binder_devices_param);

device_tmp = device_names;
//用binder,hwbinder,vndbinder分别调用init_binder_device函数
while ((device_name = strsep(&device_tmp, ","))) {
ret = init_binder_device(device_name);
if (ret)
goto err_init_binder_device_failed;
}

return ret;

err_init_binder_device_failed:
...

err_alloc_device_names_failed:
...
}

我们将重点放在init_binder_device函数上

init_binder_device

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
static int __init init_binder_device(const char *name)
{
int ret;
struct binder_device *binder_device;

binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);
if (!binder_device)
return -ENOMEM;

//binder注册虚拟字符设备所对应的file_operations
binder_device->miscdev.fops = &binder_fops;
//动态分配次设备号
binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
binder_device->miscdev.name = name;

binder_device->context.binder_context_mgr_uid = INVALID_UID;
binder_device->context.name = name;
//初始化互斥锁
mutex_init(&binder_device->context.context_mgr_node_lock);
//注册misc设备
ret = misc_register(&binder_device->miscdev);
if (ret < 0) {
kfree(binder_device);
return ret;
}
//将binder设备加入链表(头插法)
hlist_add_head(&binder_device->hlist, &binder_devices);

return ret;
}

先构造了一个结构体用来存放binder参数,然后通过misc_register函数,以misc设备进行注册binder,作为虚拟字符设备

注册misc设备

我们先学习一下在Linux中如何注册一个misc设备

在Linux驱动中把无法归类的五花八门的设备定义为misc设备,Linux内核所提供的misc设备有很强的包容性,各种无法归结为标准字符设备的类型都可以定义为misc设备,譬如NVRAM,看门狗,实时时钟,字符LCD等

Linux内核里把所有的misc设备组织在一起,构成了一个子系统(subsys),统一进行管理。在这个子系统里的所有miscdevice类型的设备共享一个主设备号MISC_MAJOR(10),但次设备号不同

在内核中用miscdevice结构体表示misc设备,具体的定义在include/linux/miscdevice.h

1
2
3
4
5
6
7
8
9
10
11
struct miscdevice  {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
const char *nodename;
umode_t mode;
};

我们自己注册misc设备时只需要填入前3项即可:

  • minor:次设备号,如果填充MISC_DYNAMIC_MINOR,则由内核动态分配次设备号
  • name:设备名
  • fopsfile_operations结构体,用于定义自己misc设备的文件操作函数,如果不填此项则会使用默认的misc_fops

file_operations结构体被定义在include/linux/fs.h

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
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
};

file_operation是把系统调用和驱动程序关联起来的关键结构,这个结构的每一个成员都对应着一个系统调用,Linux系统调用通过读取file_operation中相应的函数指针,接着把控制权转交给函数,从而完成Linux设备驱动程序的工作

最后调用misc_register函数注册misc设备,函数原型如下:

1
2
3
4
//注册misc设备
extern int misc_register(struct miscdevice *misc);
//卸载misc设备
extern void misc_deregister(struct miscdevice *misc);

注册binder设备

了解了misc设备的注册,我们就可以看一下binder的注册过程了,代码中先构建了一个binder_device结构体,我们先观察一下这个结构体长什么样子

1
2
3
4
5
struct binder_device {
struct hlist_node hlist;
struct miscdevice miscdev;
struct binder_context context;
};

其中的hlist_node是链表中的一个节点,miscdevice就是上文所描述的注册misc所必要的结构体参数,binder_context用于保存binder上下文管理者的信息

回到代码中,首先给miscdevice赋了值,指定了file_operation,设置了minor动态分配次设备号,binder_context则是简单初始化了一下,然后便调用misc_register函数注册misc设备,最后将这个binder设备使用头插法加入到一个全局链表中

我们看一下它指定的file_operation

1
2
3
4
5
6
7
8
9
10
static const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl = binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};

可以看到,binder驱动支持以上7种系统调用,接下来,我们就逐一分析这些系统调用

binder_proc

在分析这些系统调用前,我们有必要先了解一下在binder中非常重要的结构体binder_proc,它是用来描述进程上下文信息以及管理IPC的一个结构体,被定义在drivers/android/binder.c中,是一个私有的结构体

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
struct binder_proc {
//hash链表中的一个节点
struct hlist_node proc_node;
//处理用户请求的线程组成的红黑树
struct rb_root threads;
//binder实体组成的红黑树
struct rb_root nodes;
//binder引用组成的红黑树,以句柄来排序
struct rb_root refs_by_desc;
//binder引用组成的红黑树,以它对应的binder实体的地址来排序
struct rb_root refs_by_node;
struct list_head waiting_threads;
//进程id
int pid;
//进程描述符
struct task_struct *tsk;
//进程打开的所有文件数据
struct files_struct *files;
struct mutex files_lock;
struct hlist_node deferred_work_node;
int deferred_work;
bool is_dead;
//待处理事件队列
struct list_head todo;
struct binder_stats stats;
struct list_head delivered_death;
int max_threads;
int requested_threads;
int requested_threads_started;
atomic_t tmp_ref;
struct binder_priority default_priority;
struct dentry *debugfs_entry;
//用来记录mmap分配的用户虚拟地址空间和内核虚拟地址空间等信息
struct binder_alloc alloc;
struct binder_context *context;
spinlock_t inner_lock;
spinlock_t outer_lock;
};

binder_open

我们先从打开binder驱动设备开始

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
static int binder_open(struct inode *nodp, struct file *filp)
{
//管理IPC和保存进程信息的结构体
struct binder_proc *proc;
struct binder_device *binder_dev;
...
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;

//初始化内核同步自旋锁
spin_lock_init(&proc->inner_lock);
spin_lock_init(&proc->outer_lock);
//原子操作赋值
atomic_set(&proc->tmp_ref, 0);
//使执行当前系统调用进程的task_struct.usage加1
get_task_struct(current->group_leader);
//使binder_proc中的tsk指向执行当前系统调用的进程
proc->tsk = current->group_leader;
//初始化文件锁
mutex_init(&proc->files_lock);
//初始化todo列表
INIT_LIST_HEAD(&proc->todo);
//设置优先级
if (binder_supported_policy(current->policy)) {
proc->default_priority.sched_policy = current->policy;
proc->default_priority.prio = current->normal_prio;
} else {
proc->default_priority.sched_policy = SCHED_NORMAL;
proc->default_priority.prio = NICE_TO_PRIO(0);
}
//找到binder_device结构体的首地址
binder_dev = container_of(filp->private_data, struct binder_device,
miscdev);
//使binder_proc的上下文指向binder_device的上下文
proc->context = &binder_dev->context;
//初始化binder缓冲区
binder_alloc_init(&proc->alloc);
//全局binder_stats结构体中,BINDER_STAT_PROC类型的对象创建数量加1
binder_stats_created(BINDER_STAT_PROC);
//设置当前进程id
proc->pid = current->group_leader->pid;
//初始化已分发的死亡通知列表
INIT_LIST_HEAD(&proc->delivered_death);
//初始化等待线程列表
INIT_LIST_HEAD(&proc->waiting_threads);
//保存binder_proc数据
filp->private_data = proc;

//因为binder支持多线程,所以需要加锁
mutex_lock(&binder_procs_lock);
//将binder_proc添加到binder_procs全局链表中
hlist_add_head(&proc->proc_node, &binder_procs);
//释放锁
mutex_unlock(&binder_procs_lock);

//在binder/proc目录下创建文件,以执行当前系统调用的进程id为名
if (binder_debugfs_dir_entry_proc) {
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
proc->debugfs_entry = debugfs_create_file(strbuf, 0444,
binder_debugfs_dir_entry_proc,
(void *)(unsigned long)proc->pid,
&binder_proc_fops);
}

return 0;
}

binder_open函数创建了binder_proc结构体,并把初始化并将当前进程等信息保存到binder_proc结构体中,然后将binder_proc结构体保存到文件指针filpprivate_data中,再将binder_proc加入到全局链表binder_procs

这里面有一些关于Linux的知识需要解释一下

spinlock

spinlock是内核中提供的一种自旋锁机制。在Linux内核实现中,常常会碰到共享数据被中断上下文和进程上下文访问的场景,如果只有进程上下文的话,我们可以使用互斥锁或者信号量解决,将未获得锁的进程置为睡眠状态等待,但由于中断上下文不是一个进程,它不存在task_struct,所以不可被调度,当然也就不可睡眠,这时候就可以通过spinlock自旋锁的忙等待机制来达成睡眠同样的效果

current

Linux内核中,定义了一个叫current的宏,它被定义在asm/current.h

1
2
3
4
5
6
static inline struct task_struct *get_current(void)
{
return(current_thread_info()->task);
}

#define current get_current()

它返回一个task_struct指针,指向执行当前这段内核代码的进程

container_of

container_of也是Linux中定义的一个宏,它的作用是根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针

1
2
3
4
5
#define offsetof(TYPE, MEMBER)	((size_t)&((TYPE *)0)->MEMBER)

#define container_of(ptr, type, member) ({              \         
const typeof( ((type *)0)->member ) *__mptr = (ptr);    \         
(type *)( (char *)__mptr - offsetof(type,member) );})

fd&filp

filp->private_data保存了binder_proc结构体,当进程调用open系统函数时,内核会返回一个文件描述符fd,这个fd指向文件指针filp,在后续调用mmapioctl等函数与binder驱动交互时,会传入这个fd,内核就会以这个fd指向文件指针filp作为参数调用binder_mmapbinder_ioctl等函数,这样这些函数就可以通过filp->private_data取出binder_proc结构体

binder_mmap

vm_area_struct

在分析mmap前,我们需要先了解一下vm_area_struct这个结构体,它被定义在include/linux/mm_types.h

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
46
47
48
49
struct vm_area_struct {
//当前vma的首地址
unsigned long vm_start;
//当前vma的末地址后第一个字节的地址
unsigned long vm_end;

//链表
struct vm_area_struct *vm_next, *vm_prev;
//红黑树中对应节点
struct rb_node vm_rb;

//当前vma前面还有多少空闲空间
unsigned long rb_subtree_gap;

//当前vma所属的内存地址空间
struct mm_struct *vm_mm;
//访问权限
pgprot_t vm_page_prot;
//vma标识集,定义在 include/linux/mm.h 中
unsigned long vm_flags;

union {
struct {
struct rb_node rb;
unsigned long rb_subtree_last;
} shared;
const char __user *anon_name;
};

struct list_head anon_vma_chain;
struct anon_vma *anon_vma;

//当前vma操作函数集指针
const struct vm_operations_struct *vm_ops;

//当前vma起始地址在vm_file中的文件偏移,单位为物理页面PAGE_SIZE
unsigned long vm_pgoff;
//被映射的文件(如果使用文件映射)
struct file * vm_file;
void * vm_private_data;

#ifndef CONFIG_MMU
struct vm_region *vm_region; /* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMA
struct mempolicy *vm_policy; /* NUMA policy for the VMA */
#endif
struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
};

vm_area_struct结构体描述了一段虚拟内存空间,通常,进程所使用到的虚拟内存空间不连续,且各部分虚存空间的访问属性也可能不同,所以一个进程的虚拟内存空间需要多个vm_area_struct结构来描述(后面简称vma

每个进程都有一个对应的task_struct结构描述,这个task_struct结构中有一个mm_struct结构用于描述进程的内存空间,mm_struct结构中有两个域成员变量分别指向了vma链表头和红黑树根

vma所描述的虚拟内存空间范围由vm_startvm_end表示,vm_start代表当前vma的首地址,vm_end代表当前vma的末地址后第一个字节的地址,即虚拟内存空间范围为[vm_start, vm_end)

vm_operations_struct和上文中的file_operations类似,用来定义虚拟内存的操作函数


介绍完vma,接下来我们便看一下binder_mmap函数

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
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
struct binder_proc *proc = filp->private_data;
const char *failure_string;

//校验进程信息
if (proc->tsk != current->group_leader)
return -EINVAL;

//将虚拟内存地址大小限制在4M
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;
...
//检查用户空间是否可写(FORBIDDEN_MMAP_FLAGS == VM_WRITE)
if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
ret = -EPERM;
failure_string = "bad vm_flags";
goto err_bad_arg;
}
//VM_DONTCOPY表示此vma不可被fork所复制
vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP;
//用户空间不可设置该vma的VM_WRITE标志
vma->vm_flags &= ~VM_MAYWRITE;
//设置此vma操作函数集
vma->vm_ops = &binder_vm_ops;
//指向binder_proc
vma->vm_private_data = proc;

//处理进程虚拟内存空间与内核虚拟地址空间的映射关系
ret = binder_alloc_mmap_handler(&proc->alloc, vma);
if (ret)
return ret;
mutex_lock(&proc->files_lock);
//获取进程的打开文件信息结构体files_struct,并将引用计数加1
proc->files = get_files_struct(current);
mutex_unlock(&proc->files_lock);
return 0;

err_bad_arg:
pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}
  1. 首先从filp中获取对应的binder_proc信息
  2. 将它的进程task_struct和执行当前这段内核代码的进程task_struct对比校验
  3. 限制了用户空间虚拟内存的大小在4M以内
  4. 检查用户空间是否可写(binder驱动为进程分配的缓冲区在用户空间中只可以读,不可以写)
  5. 设置vm_flags,令vma不可写,不可复制
  6. 设置vma的操作函数集
  7. vm_area_struct中的成员变量vm_private_data指向binder_proc,使得vma设置的操作函数中可以拿到binder_proc
  8. 处理进程虚拟内存空间与内核虚拟地址空间的映射关系
  9. 获取进程的打开文件信息结构体files_struct,令binder_procfiles指向它,并将引用计数加1

binder_alloc_mmap_handler

binder_alloc_mmap_handler将进程虚拟内存空间与内核虚拟地址空间做映射,它被实现在drivers/android/binder_alloc.c

这里先介绍一下vm_struct,之前我们已经了解了vm_area_struct表示用户进程中的虚拟地址空间,而相对应的,vm_struct则表示内核中的虚拟地址空间

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
struct vm_area_struct *vma)
{
int ret;
struct vm_struct *area;
const char *failure_string;
struct binder_buffer *buffer;

mutex_lock(&binder_alloc_mmap_lock);
//检查是否已经分配过内核缓冲区
if (alloc->buffer) {
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
//获得一个内核虚拟空间
area = get_vm_area(vma->vm_end - vma->vm_start, VM_ALLOC);
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
//alloc->buffer指向内核虚拟内存空间地址
alloc->buffer = area->addr;
//计算出用户虚拟空间线性地址到内核虚拟空间线性地址的偏移量
alloc->user_buffer_offset =
vma->vm_start - (uintptr_t)alloc->buffer;
mutex_unlock(&binder_alloc_mmap_lock);
...
//申请内存
alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
((vma->vm_end - vma->vm_start) / PAGE_SIZE),
GFP_KERNEL);
if (alloc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
//buffer大小等于vma大小
alloc->buffer_size = vma->vm_end - vma->vm_start;

buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!buffer) {
ret = -ENOMEM;
failure_string = "alloc buffer struct";
goto err_alloc_buf_struct_failed;
}
//指向内核虚拟空间地址
buffer->data = alloc->buffer;
//将buffer添加到链表中
list_add(&buffer->entry, &alloc->buffers);
buffer->free = 1;
//将此内核缓冲区加入到binder_alloc的空闲缓冲红黑树中
binder_insert_free_buffer(alloc, buffer);
//设置进程最大可用异步事务缓冲区大小(防止异步事务消耗过多内核缓冲区,影响同步事务)
alloc->free_async_space = alloc->buffer_size / 2;
//内存屏障,保证指令顺序执行
barrier();
//设置binder_alloc
alloc->vma = vma;
alloc->vma_vm_mm = vma->vm_mm;
//引用计数+1
atomic_inc(&alloc->vma_vm_mm->mm_count);

return 0;
... //错误处理
}
  1. 检查是否已经分配过内核缓冲区
  2. 从内核中寻找一块可用的虚拟内存地址
  3. 将此内核虚拟内存空间地址保存至binder_alloc
  4. 计算出用户虚拟空间线性地址到内核虚拟空间线性地址的偏移量(这样就可以非常方便的在用户虚拟内存空间与内核虚拟内存空间间切换)
  5. alloc->pages数组申请内存,申请的大小等于vma能分配多少个页框
  6. 设置buffer大小等于vma大小
  7. binder_buffer申请内存,填充参数,使其指向内核虚拟空间地址,并将其添加到链表和红黑树中
  8. 设置binder_alloc其他参数

这里要注意,虽然我们计算出了用户虚拟空间线性地址到内核虚拟空间线性地址的偏移量,但并没有建立映射关系。在旧版内核中,这里会调用binder_update_page_range函数分别将内核虚拟内存和进程虚拟内存与物理内存做映射,这样内核虚拟内存和进程虚拟内存也相当于间接建立了映射关系,而在4.4.223中,这件事将会延迟到binder_ioctl

当完成物理内存的映射后,以32位系统,缓冲区大小4M为例,效果应该如下图所示:

binder_mmap

总结

到这里,我们已经了解了binder驱动设备是如何注册的,并且分析了binder_openbinder_mmap操作函数,了解了一些重要的结构体,明白了mmap是如何映射用户空间和内核空间的,由于篇幅原因,下一章我们会分析binder驱动中最重要的部分binder_ioctl

参考文献


Android源码分析 - Binder概述

开篇

本篇无源码分析,只对Binder做通信过程和基础架构的介绍

BinderAndroid中最重要的一种进程间通信机制,基于开源的OpenBinder

George Hoffman当时任Be公司的工程师,他启动了一个名为OpenBinder的项目,在Be公司被ParmSource公司收购后,OpenBinderDinnie Hackborn继续开发,后来成为管理ParmOS6 Cobalt OS的进程的基础。在Hackborn加入谷歌后,他在OpenBinder的基础上开发出了Android Binder(以下简称Binder),用来完成Android的进程通信。

为什么需要学习Binder

作为一名Android开发,我们每天都在和Binder打交道,虽然可能有的时候不会注意到,譬如:

  • startActivity的时候,会获取AMS服务,调用AMS服务的startActivity方法
  • startActivity传递的对象为什么需要序列化
  • bindService为什么回调的是一个Ibinder对象
  • 多进程应用,各个进程之间如何通信
  • AIDL的使用

它们都和Binder有着莫切关系,当碰到上面的场景,或者一些疑难问题的时候,理解Binder机制是非常有必要的

为什么Android选择Binder

这就要从进程间通信开始说起了,我们先看看比较常见的几种进程间通信方式

常见进程间通信

共享内存

共享内存是进程间通信中最简单的方式之一,共享内存允许两个或更多进程访问同一块内存,当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改,它的原理如下图所示:

共享内存

因为共享内存是访问同一块内存,所以数据不需要进行任何复制,是IPC几种方式中最快,性能最好的方式。但相对应的,共享内存未提供同步机制,需要我们手动控制内存间的互斥操作,较容易发生问题。同时共享内存由于能任意的访问和修改内存中的数据,如果有恶意程序去针对某个程序设计代码,很可能导致隐私泄漏或者程序崩溃,所以安全性较差。

管道

管道分为命名管道和无名管道,它是以一种特殊的文件作为中间介质,我们称为管道文件,它具有固定的读端和写端,写进程通过写段向管道文件里写入数据,读进程通过读段从读进程中读出数据,构成一条数据传递的流水线,它的原理如下图所示:

管道

管道一次通信需要经历2次数据复制(进程A -> 管道文件,管道文件 -> 进程B)。管道的读写分阻塞和非阻塞,管道创建会分配一个缓冲区,而这个缓冲区是有限的,如果传输的数据大小超过缓冲区上限,或者在阻塞模式下没有安排好数据的读写,会出现阻塞的情况。管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式。

消息队列

消息队列是存放在内核中的消息链表,每个消息队列由消息队列标识符表示。消息队列允许多个进程同时读写消息,发送方与接收方要约定好,消息体的数据类型与大小。消息队列克服了信号承载信息量少、管道只能承载无格式字节流等缺点,消息队列一次通信同样需要经历2次数据复制(进程A -> 消息队列,消息队列 -> 进程B),它的原理如下图所示:

消息队列

Socket

Socket原本是为了网络设计的,但也可以通过本地回环地址 (127.0.0.1) 进行进程间通信,后来在Socket的框架上更是发展出一种IPC机制,名叫UNIX Domain SocketSocket是一种典型的C/S架构,一个Socket会拥有两个缓冲区,一读一写,由于发送/接收消息需要将一个Socket缓冲区中的内容拷贝至另一个Socket缓冲区,所以Socket一次通信也是需要经历2次数据复制,它的原理如下图所示:

Socket

Binder

了解了常见进程间通信的方式,我们再来看一下Binder的原理

Binder是基于内存映射mmap设计实现的,我们需要先了解一下mmap的概念

mmap

mmap是一种内存映射的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。

Linux内核不会主动将mmap修改后的内容同步到磁盘文件中,有4个时机会触发mmap映射同步到磁盘:

  • 调用 msync 函数主动进行数据同步(主动)
  • 调用 munmap 函数对文件进行解除映射关系时(主动)
  • 进程退出时(被动)
  • 系统关机时(被动)

通过这种方式,直接操作映射的这一部分内存,可以避免一些数据复制,从而获得更好的性能

原理

一次Binder IPC通信的过程分为以下几个步骤:

  1. 首先,Binder驱动在内核空间中开辟出一个数据接收缓冲区
  2. 接着,在内核空间中开辟出一个内核缓冲区
  3. 内核缓冲区数据接收缓冲区建立映射关系
  4. 数据接收缓冲区接收进程的用户空间地址建立映射关系
  5. 发送方进程通过copy_from_user将数据从用户空间复制到内核缓冲区
  6. 由于内核缓冲区数据接收缓冲区有映射关系,同时数据接收缓冲区接收进程的用户空间地址有映射关系,所以在接收进程中可以直接获取到这段数据

这样便完成了一次Binder IPC通信,它的原理如下图所示:

Binder

可以看到,通过mmapBinder通信时,只需要经历一次数据复制,性能要优于管道/消息队列/socket等方式,在安全性,易用性方面又优于共享内存。鉴于上述原因,Android选择了这种折中的IPC方式,来满足系统对稳定性、传输性能和安全性方面的要求

Binder架构

Binder也是一种C/S架构,分为BpBinder(客户端)和BBinder(服务端),他们都派生自IBinder。其中BpBinder中的p表示proxy,即代理。BpBinder通过transact来发送事务请求,BBinder通过onTransact来接收相应的事务

Ibinder

Binder一次通信的时序图如下:

Binder通信

Binder采用分层架构设计

Binder分层架构

总结

至此,我们大概了解了一下Android选用Binder的原因,以及Binder的基本结构和通信过程,为之后深入源码层分析Binder做了准备

参考文献


Android源码分析 - SystemServer(下)

开篇

本篇以android-11.0.0_r25作为基础解析

上一篇文章Android源码分析 - SystemServer(上)我们分析了SystemServer进程是怎么被启动起来的,今天这篇,我们来分析SystemServer进程启动后做了什么

main

我们上一章中讲到,Zygote进程fork出子进程后,最终调用了SystemServer.main方法,SystemServer源代码在frameworks/base/services/java/com/android/server/SystemServer.java中,我们来看看做了什么

1
2
3
public static void main(String[] args) {
new SystemServer().run();
}

构造方法

非常简单,就是先new了一个SystemServer对象,然后调用它的run方法,我们先看一下构造方法

1
2
3
4
5
6
7
8
9
public SystemServer() {
//工厂模式
mFactoryTestMode = FactoryTest.getMode();

... //记录启动信息

//记录是否经历过重启
mRuntimeRestart = "1".equals(SystemProperties.get("sys.boot_completed"));
}

工厂模式

首先,先从系统属性中获取工厂模式级别,有三种属性:

  • FACTORY_TEST_OFF:正常模式
  • FACTORY_TEST_LOW_LEVEL:低级别工厂模式,在此模式下,很多Service不会启动
  • FACTORY_TEST_HIGH_LEVEL:高级别工厂模式,此模式与正常模式基本相同,略有区别

它们被定义在frameworks/base/core/java/android/os/FactoryTest.java

run

紧接着便开始执行run方法

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
private void run() {
... //记录启动信息
//如果没有设置时区,将时区设置为GMT
String timezoneProperty = SystemProperties.get("persist.sys.timezone");
if (timezoneProperty == null || timezoneProperty.isEmpty()) {
Slog.w(TAG, "Timezone not set; setting to GMT.");
SystemProperties.set("persist.sys.timezone", "GMT");
}

//设置区域与语言
if (!SystemProperties.get("persist.sys.language").isEmpty()) {
final String languageTag = Locale.getDefault().toLanguageTag();

SystemProperties.set("persist.sys.locale", languageTag);
SystemProperties.set("persist.sys.language", "");
SystemProperties.set("persist.sys.country", "");
SystemProperties.set("persist.sys.localevar", "");
}

//Binder事务发生阻塞时发出警告
Binder.setWarnOnBlocking(true);
//PackageManager相关
PackageItemInfo.forceSafeLabels();
...
//设置虚拟机库文件libart.so
SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());
//清除虚拟机内存增长上限,以获得更多内存
VMRuntime.getRuntime().clearGrowthLimit();
// Some devices rely on runtime fingerprint generation, so make sure
// we've defined it before booting further.
Build.ensureFingerprintProperty();
//设置在访问环境变量前,需要明确指定用户
Environment.setUserRequired(true);
//设置标记,当发生BadParcelableException异常时保守处理,不要抛出异常
BaseBundle.setShouldDefuse(true);
//设置异常跟踪
Parcel.setStackTraceParceling(true);
//确保Binder调用优先级总为前台优先级
BinderInternal.disableBackgroundScheduling(true);
//设置Binder线程池最大数量
BinderInternal.setMaxThreads(sMaxBinderThreads);
//设置进程优先级为前台进程
// Prepare the main looper thread (this thread).
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
android.os.Process.setCanSelfBackground(false);
//以当前线程作为MainLooper准备
Looper.prepareMainLooper();
Looper.getMainLooper().setSlowLogThresholdMs(
SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);

SystemServiceRegistry.sEnableServiceNotFoundWtf = true;

//加载android_servers.so库
System.loadLibrary("android_servers");
//标记该进程的堆可分析
initZygoteChildHeapProfiling();
//Debug选项 - 开启一个线程用来监测FD泄漏
if (Build.IS_DEBUGGABLE) {
spawnFdLeakCheckThread();
}
//检查上次关机过程中是否失败
performPendingShutdown();
//初始化System Context
createSystemContext();
//创建并设置一些每个进程启动时都需要的一些模块 (TelephonyServiceManager, StatsServiceManager)
ActivityThread.initializeMainlineModules();

//创建SystemServiceManager(管理所有的系统Service)
mSystemServiceManager = new SystemServiceManager(mSystemContext);
mSystemServiceManager.setStartInfo(mRuntimeRestart,
mRuntimeStartElapsedTime, mRuntimeStartUptime);
//将SystemServiceManager作为本地进程Service使用
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
//为初始化任务准备线程池
SystemServerInitThreadPool.start();
...
//设置默认异常处理程序
RuntimeInit.setDefaultApplicationWtfHandler(SystemServer::handleEarlySystemWtf);

...
//启动引导服务
startBootstrapServices(t);
//启动核心服务
startCoreServices(t);
//启动其他服务
startOtherServices(t);
...

//严格模式初始化虚拟机策略
StrictMode.initVmDefaults(null);
...
//进入Looper死循环,等待Handler事件
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看到,run方法主要做了以下工作

  1. 检查并设置各种参数handler
  2. 创建SystemContext
  3. 创建SystemServiceManager
  4. 启动服务
  5. Looper循环

其中,创建SystemContext这一步是由ContextImpl完成的,等后面分析到的时候在详细去看,Looper也是,我们将重点放在启动服务上

启动服务

启动服务分为三步,首先是启动引导服务,其次是启动核心服务,最后是启动其他服务,我们先从引导服务开始

由于启动的服务太多了,我们只介绍一些我们比较熟悉的服务

startBootstrapServices

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
...
//看门狗
final Watchdog watchdog = Watchdog.getInstance();
watchdog.start();
...
final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig";
//读取系统配置
SystemServerInitThreadPool.submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
...
//Installer服务(实际上是与installd跨进程通信)
Installer installer = mSystemServiceManager.startService(Installer.class);
...
//创建 ATMS & AMS
ActivityTaskManagerService atm = mSystemServiceManager.startService(
ActivityTaskManagerService.Lifecycle.class).getService();
mActivityManagerService = ActivityManagerService.Lifecycle.startService(
mSystemServiceManager, atm);
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
mActivityManagerService.setInstaller(installer);
mWindowManagerGlobalLock = atm.getGlobalLock();
...
//电源管理服务,后面有其他服务依赖它,所以需要较早启动
mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
...
mActivityManagerService.initPowerManagement();
...
//灯光服务
mSystemServiceManager.startService(LightsService.class);
...
//显示管理服务
mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
...
//阶段100
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);

...
//创建PMS
try {
Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
} finally {
Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
}

//捕获dex load行为
SystemServerDexLoadReporter.configureSystemServerDexReporter(mPackageManagerService);
//是否首次启动
mFirstBoot = mPackageManagerService.isFirstBoot();
//获取PMS
mPackageManager = mSystemContext.getPackageManager();
...
//用户管理服务
mSystemServiceManager.startService(UserManagerService.LifeCycle.class);
...
//初始化属性缓存
AttributeCache.init(mSystemContext);
...
//注册各种系统服务
mActivityManagerService.setSystemProcess();
...
//使用AMS完成看门狗的设置,并监听重新启动
watchdog.init(mSystemContext, mActivityManagerService);
...
//设置调度策略
mDisplayManagerService.setupSchedulerPolicies();
...
//在单独线程中启动传感器服务
mSensorServiceStart = SystemServerInitThreadPool.submit(() -> {
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
traceLog.traceBegin(START_SENSOR_SERVICE);
startSensorService();
traceLog.traceEnd();
}, START_SENSOR_SERVICE);
...
}

startCoreServices

1
2
3
4
5
6
7
8
9
10
11
private void startCoreServices(@NonNull TimingsTraceAndSlog t) {
...
//电池电量服务,依赖LightsService
mSystemServiceManager.startService(BatteryService.class);
...
//应用统计服务
mSystemServiceManager.startService(UsageStatsService.class);
mActivityManagerService.setUsageStatsManager(
LocalServices.getService(UsageStatsManagerInternal.class));
...
}

startOtherServices

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
//AccountManagerService - 账户管理
mSystemServiceManager.startService(ACCOUNT_SERVICE_CLASS);
...
//ContentService - 内容服务
mSystemServiceManager.startService(CONTENT_SERVICE_CLASS);
...
//加载SettingProvider
mActivityManagerService.installSystemProviders();
...
//DropBox日志服务
mSystemServiceManager.startService(DropBoxManagerService.class);
...
//震动服务
vibrator = new VibratorService(context);
ServiceManager.addService("vibrator", vibrator);
...
//时钟/闹钟服务
mSystemServiceManager.startService(new AlarmManagerService(context));
//输入服务
inputManager = new InputManagerService(context);
...
//等待传感器服务准备完毕
ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
mSensorServiceStart = null;
//启动WindowManagerService
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
...
mActivityManagerService.setWindowManager(wm);
...
wm.onInitReady();
...
//HIDL services
SystemServerInitThreadPool.submit(() -> {
startHidlServices();
}, START_HIDL_SERVICES);
...
//关联WMS,启动输入服务
inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
inputManager.start();
...
mDisplayManagerService.windowManagerAndInputReady();
...
//有蓝牙功能且非低级工厂模式,启动蓝牙服务
if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
...
} else if (!context.getPackageManager().hasSystemFeature
(PackageManager.FEATURE_BLUETOOTH)) {
...
} else {
mSystemServiceManager.startService(BluetoothService.class);
}
...
//输入法/无障碍服务
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) {
mSystemServiceManager.startService(
MultiClientInputMethodManagerService.Lifecycle.class);
} else {
mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
}
mSystemServiceManager.startService(ACCESSIBILITY_MANAGER_SERVICE_CLASS);
}

wm.displayReady();

//存储相关服务
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
mSystemServiceManager.startService(STORAGE_MANAGER_SERVICE_CLASS);
storageManager = IStorageManager.Stub.asInterface(
ServiceManager.getService("mount"));
mSystemServiceManager.startService(STORAGE_STATS_SERVICE_CLASS);
}
}

//UIMode服务(夜间模式,驾驶模式等)
mSystemServiceManager.startService(UiModeManagerService.class);
...
//执行磁盘清理工作,释放磁盘空间
mPackageManagerService.performFstrimIfNeeded();

if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
...
final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("");
...
if (hasPdb || OemLockService.isHalPresent()) {
//OEM锁服务
mSystemServiceManager.startService(OemLockService.class);
}
...
if (!isWatch) {
//状态栏管理服务
statusBar = new StatusBarManagerService(context);
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
}

//网络相关服务
ConnectivityModuleConnector.getInstance().init(context);
NetworkStackClient.getInstance().init();
networkManagement = NetworkManagementService.create(context);
ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
ipSecService = IpSecService.create(context, networkManagement);
ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);

//文本服务
mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
mSystemServiceManager
.startService(TextClassificationManagerService.Lifecycle.class);

//网络相关服务
mSystemServiceManager.startService(NetworkScoreService.Lifecycle.class);
networkStats = NetworkStatsService.create(context, networkManagement);
ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
networkPolicy = new NetworkPolicyManagerService(context, mActivityManagerService,
networkManagement);
ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI)) {
mSystemServiceManager.startServiceFromJar(
WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
mSystemServiceManager.startServiceFromJar(
WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
}
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_RTT)) {
mSystemServiceManager.startServiceFromJar(
WIFI_RTT_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
}
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_AWARE)) {
mSystemServiceManager.startServiceFromJar(
WIFI_AWARE_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
}
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_DIRECT)) {
mSystemServiceManager.startServiceFromJar(
WIFI_P2P_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
}
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LOWPAN)) {
mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS);
}
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
}
connectivity = new ConnectivityService(
context, networkManagement, networkStats, networkPolicy);
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity,
/* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
networkPolicy.bindConnectivityManager(connectivity);
...
//系统更新服务
ServiceManager.addService(Context.SYSTEM_UPDATE_SERVICE,
new SystemUpdateManagerService(context));
ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
new UpdateLockService(context));
//通知服务
mSystemServiceManager.startService(NotificationManagerService.class);
SystemNotificationChannels.removeDeprecated(context);
SystemNotificationChannels.createAll(context);
notification = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
...
//位置服务
mSystemServiceManager.startService(LocationManagerService.Lifecycle.class);
...
//墙纸服务
if (context.getResources().getBoolean(R.bool.config_enableWallpaperService)) {
mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS);
} else {
...
}
//音频服务
if (!isArc) {
mSystemServiceManager.startService(AudioService.Lifecycle.class);
} else {
String className = context.getResources()
.getString(R.string.config_deviceSpecificAudioService);
mSystemServiceManager.startService(className + "$Lifecycle");
}
...
//ADB服务
mSystemServiceManager.startService(ADB_SERVICE_CLASS);
//USB服务
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
|| mPackageManager.hasSystemFeature(
PackageManager.FEATURE_USB_ACCESSORY)
|| isEmulator) {
mSystemServiceManager.startService(USB_SERVICE_CLASS);
}
//微件(小组件)服务
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)
|| context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) {
mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
}
...
//Android10新增,用于报告来自运行时模块的信息
ServiceManager.addService("runtime", new RuntimeService(context));
...
//App后台Dex优化
BackgroundDexOptService.schedule(context);
...
}
...
//相机服务
if (!disableCameraService) {
mSystemServiceManager.startService(CameraServiceProxy.class);
}
//进入安全模式
if (safeMode) {
mActivityManagerService.enterSafeMode();
}

//短信服务
mmsService = mSystemServiceManager.startService(MmsServiceBroker.class);
...
//剪贴板服务
mSystemServiceManager.startService(ClipboardService.class);
...

//调用各大服务的systemReady方法

vibrator.systemReady();
lockSettings.systemReady();

//阶段480
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_LOCK_SETTINGS_READY);
//阶段500
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_SYSTEM_SERVICES_READY);

wm.systemReady();
...
//手动更新Context Configuration
final Configuration config = wm.computeNewConfiguration(DEFAULT_DISPLAY);
DisplayMetrics metrics = new DisplayMetrics();
context.getDisplay().getMetrics(metrics);
context.getResources().updateConfiguration(config, metrics);

final Theme systemTheme = context.getTheme();
if (systemTheme.getChangingConfigurations() != 0) {
systemTheme.rebase();
}

mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
...
mPackageManagerService.systemReady();
mDisplayManagerService.systemReady(safeMode, mOnlyCore);

mSystemServiceManager.setSafeMode(safeMode);

//阶段520
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY);
...
//最后运行AMS.systemReady
mActivityManagerService.systemReady(() -> {
//阶段550
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_ACTIVITY_MANAGER_READY);
...
//阶段600
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
...
}, t);
}

服务的启动是分阶段完成的,从0-100-480-500-520-550-600-1000,最后的阶段1000,是在AMS调用finishBooting方法后进入

可以看到,启动的服务非常之多,不可能全看得完,其中最重要的几个:ActivityManagerServiceWindowManagerServicePackageManagerServiceInputManagerService,后面我们会慢慢看过去,在此之前,我们还是先看看服务启动的方式

SystemServiceManager

绝大部分的服务是通过SystemServiceManager启动的,它的源码路径为frameworks/base/services/core/java/com/android/server/SystemServiceManager.java

startService

我们来看看这个类里的启动服务方法

这个类中有三个方法用于启动Serivce,分别是:

  • public SystemService startService(String className)
  • public SystemService startServiceFromJar(String className, String path)
  • public <T extends SystemService> T startService(Class<T> serviceClass)
  • public void startService(@NonNull final SystemService service)

实际上最后都是调用了最后一个方法

先看参数为StringstartService方法

1
2
3
4
5
public SystemService startService(String className) {
final Class<SystemService> serviceClass = loadClassFromLoader(className,
this.getClass().getClassLoader());
return startService(serviceClass);
}
1
2
3
4
5
6
7
8
private static Class<SystemService> loadClassFromLoader(String className,
ClassLoader classLoader) {
try {
return (Class<SystemService>) Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
...
}
}

实际上就是通过反射拿到类名对应的Class,再调用Class为参的startService方法

startServiceFromJar实际上也是一样,只不过是先通过PathClassLoader加载了jar而已

1
2
3
4
5
6
7
8
9
10
11
public SystemService startServiceFromJar(String className, String path) {
PathClassLoader pathClassLoader = mLoadedPaths.get(path);
if (pathClassLoader == null) {
// NB: the parent class loader should always be the system server class loader.
// Changing it has implications that require discussion with the mainline team.
pathClassLoader = new PathClassLoader(path, this.getClass().getClassLoader());
mLoadedPaths.put(path, pathClassLoader);
}
final Class<SystemService> serviceClass = loadClassFromLoader(className, pathClassLoader);
return startService(serviceClass);
}

接着我们看看Class为参数的startService方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public <T extends SystemService> T startService(Class<T> serviceClass) {
final String name = serviceClass.getName();

// Create the service.
if (!SystemService.class.isAssignableFrom(serviceClass)) {
throw new RuntimeException("Failed to create " + name
+ ": service must extend " + SystemService.class.getName());
}
final T service;
try {
Constructor<T> constructor = serviceClass.getConstructor(Context.class);
service = constructor.newInstance(mContext);
} catch (...) {
...
}

startService(service);
return service;
}

看函数泛型我们就可以知道,这个方法只接受SystemService的子类,并且在方法的开头,还使用了isAssignableFrom方法做了类型校验,避免通过String反射获取的ClassSystemService的子类

之后的逻辑也很简单,反射实例化对象,然后调用另一个以SystemService对象为参数的重载方法

1
2
3
4
5
6
7
8
9
public void startService(@NonNull final SystemService service) {
mServices.add(service);
try {
service.onStart();
} catch (RuntimeException ex) {
throw new RuntimeException("Failed to start service " + service.getClass().getName()
+ ": onStart threw an exception", ex);
}
}

这个方法会将SystemService对象加入一个List中,然后调用它的onStart方法,通知SystemService自行处理启动

startBootPhase

因为各种服务之间是存在依赖关系的,所以Android将服务的启动划分了8个阶段:0-100-480-500-520-550-600-1000,而startBootPhase方法便是用来通知各个服务进行到哪一阶段了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void startBootPhase(@NonNull TimingsTraceAndSlog t, int phase) {
if (phase <= mCurrentPhase) {
throw new IllegalArgumentException("Next phase must be larger than previous");
}
mCurrentPhase = phase;

final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
service.onBootPhase(mCurrentPhase);
}

if (phase == SystemService.PHASE_BOOT_COMPLETED) {
SystemServerInitThreadPool.shutdown();
}
}

每进入到一个阶段,便会调用Service List中所有SystemServiceonBootPhase方法,通知SystemService阶段变换,而当阶段达到1000 (PHASE_BOOT_COMPLETED) 时,就代表着所有的服务都已准备完毕,关闭SystemServerInitThreadPool线程池

ServiceManager

当服务被创建出来后,会调用ServiceManager.addService方法添加服务,以供其他地方使用这些服务

addService有三个重载,最终调用的为:

1
2
3
4
5
6
7
8
public static void addService(String name, IBinder service, boolean allowIsolated,
int dumpPriority) {
try {
getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
1
2
3
4
5
6
7
8
9
10
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}

// Find the service manager
sServiceManager = ServiceManagerNative
.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}
1
2
3
4
5
6
7
8
public static IServiceManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}

// ServiceManager is never local
return new ServiceManagerProxy(obj);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ServiceManagerProxy implements IServiceManager {
public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
mServiceManager = IServiceManager.Stub.asInterface(remote);
}

public IBinder asBinder() {
return mRemote;
}
...
public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
throws RemoteException {
mServiceManager.addService(name, service, allowIsolated, dumpPriority);
}
...
private IBinder mRemote;

private IServiceManager mServiceManager;
}

从这里就能看出来ServiceManager实际上是一个单独的进程,名为servicemanager,它负责管理所有服务,使用了Binder IPC机制,我们调用addService方法实际上是调用了Binder Proxy的方法,他向/dev/binder中写入消息,在servicemanager进程中接收到了这个消息并处理这个请求

关于Binder机制,我们随后便会分析它

最终调用了frameworks/native/cmds/servicemanager/ServiceManager.cpp中的addService函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
auto ctx = mAccess->getCallingContext();
...
//添加服务
auto entry = mNameToService.emplace(name, Service {
.binder = binder,
.allowIsolated = allowIsolated,
.dumpPriority = dumpPriority,
.debugPid = ctx.debugPid,
});

auto it = mNameToRegistrationCallback.find(name);
if (it != mNameToRegistrationCallback.end()) {
for (const sp<IServiceCallback>& cb : it->second) {
entry.first->second.guaranteeClient = true;
// permission checked in registerForNotifications
cb->onRegistration(name, binder);
}
}

return Status::ok();
}

可以看到,最终通过service name和传过来的binder对象构造出一个Service结构体,并将其保存至mNameToService这个Map中,以供后面使用

关于进程

SystemServer启动的服务大多都运行在systemserver进程中,但也有一些例外

譬如Installer服务,便是从init进程单独fork出了一个installd进程

下面是它的rc文件,frameworks/native/cmds/installd/installd.rc

1
2
3
service installd /system/bin/installd
class main
...

而在SystemServer进程中start的Installer,便是通过binder连接到installd进程提供服务

源码路径frameworks/base/services/core/java/com/android/server/pm/Installer.java

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
@Override
public void onStart() {
if (mIsolated) {
mInstalld = null;
} else {
connect();
}
}

private void connect() {
IBinder binder = ServiceManager.getService("installd");
if (binder != null) {
try {
binder.linkToDeath(new DeathRecipient() {
@Override
public void binderDied() {
Slog.w(TAG, "installd died; reconnecting");
connect();
}
}, 0);
} catch (RemoteException e) {
binder = null;
}
}

if (binder != null) {
mInstalld = IInstalld.Stub.asInterface(binder);
try {
invalidateMounts();
} catch (InstallerException ignored) {
}
} else {
Slog.w(TAG, "installd not found; trying again");
BackgroundThread.getHandler().postDelayed(() -> {
connect();
}, DateUtils.SECOND_IN_MILLIS);
}
}

结束

SystemServer启动了非常多的服务,并将这些服务添加到了ServiceManager中,我们又从中引申出了Binder机制,我们下一章便开始分析Binder


Android源码分析 - SystemServer(上)

开篇

本篇以android-11.0.0_r25作为基础解析

上一篇文章Android源码分析 - Zygote进程,我们分析了Android Zygote进程的启动和之后是如何接收消息创建App进程的

在上一章中,我们说了,Zygote的一大作用就是启动SystemServer,那么SystemServer是怎么启动的呢?启动后又做了些什么呢?我们分上下两篇来分析,本篇介绍SystemServer是如何启动的

介绍

SystemServer主要是用来创建系统服务的,譬如我们熟知的ActivityManagerServicePackageManagerService都是由它创建的

启动SystemServer

我们从上一篇文章的ZygoteInit开始,ZygoteInit类的源码路径为frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String argv[]) {
...
boolean startSystemServer = false;
...
for (int i = 1; i < argv.length; i++) {
//参数中有start-system-server
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
}
...
}
...
//启动SystemServer
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);

//子进程中才会满足r != null
if (r != null) {
//此时执行这个Runnable
r.run();
return;
}
}
}

之前在c++代码中JNI调用Java函数的时候,带了参数start-system-server,在这里就会通过这个参数判断是否启动SystemServer,接下来调用forkSystemServer方法

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
//设置Linux capabilities
long capabilities = posixCapabilitiesAsBits(
OsConstants.CAP_IPC_LOCK,
OsConstants.CAP_KILL,
OsConstants.CAP_NET_ADMIN,
OsConstants.CAP_NET_BIND_SERVICE,
OsConstants.CAP_NET_BROADCAST,
OsConstants.CAP_NET_RAW,
OsConstants.CAP_SYS_MODULE,
OsConstants.CAP_SYS_NICE,
OsConstants.CAP_SYS_PTRACE,
OsConstants.CAP_SYS_TIME,
OsConstants.CAP_SYS_TTY_CONFIG,
OsConstants.CAP_WAKE_ALARM,
OsConstants.CAP_BLOCK_SUSPEND
);
//移除一些当前线程都不可用的特权
StructCapUserHeader header = new StructCapUserHeader(
OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
StructCapUserData[] data;
try {
data = Os.capget(header);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to capget()", ex);
}
//data[0].effective为当前线程所可用的特权,data[1].effective貌似为0
capabilities &= ((long) data[0].effective) | (((long) data[1].effective) << 32);

//设置fork参数
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
+ "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};
ZygoteArguments parsedArgs = null;

int pid;

try {
//解析设置的参数
parsedArgs = new ZygoteArguments(args);
... //进一步设置参数
pid = Zygote.forkSystemServer(
parsedArgs.mUid, parsedArgs.mGid,
parsedArgs.mGids,
parsedArgs.mRuntimeFlags,
null,
parsedArgs.mPermittedCapabilities,
parsedArgs.mEffectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}

//SystemServer子进程
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}

//关闭zygote server socket
zygoteServer.closeServerSocket();
return handleSystemServerProcess(parsedArgs);
}

return null;
}

Capabilities

这里需要先了解一下Linux Capabilities机制:Linux Capabilities机制

这里先定义了SystemServer进程的PermittedEffective能力集合

1
2
3
4
5
6
7
8
9
10
11
12
private static long posixCapabilitiesAsBits(int... capabilities) {
long result = 0;
for (int capability : capabilities) {
//非法capability,直接抛出异常
if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
throw new IllegalArgumentException(String.valueOf(capability));
}
//为或操作,构建capabilities集合
result |= (1L << capability);
}
return result;
}

检查一下有无非法capability,然后做位或运算,构建出一个capabilities集合

然后通过Os.capget方法获取当前线程的capabilities集合,上一篇文章中我们已经分析过了Os的作用,最终通过Linux_capgetJNI函数调用Linuxcapget函数,通过返回回来的值,剔除一些当前线程不支持的特权

Fork

接着设置一些fork参数,通过ZygoteArguments去解析它

然后调用Zygote.forkSystemServer方法,这个和上一章里说的fork App的过程差不多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
//停止其他线程
ZygoteHooks.preFork();

int pid = nativeForkSystemServer(
uid, gid, gids, runtimeFlags, rlimits,
permittedCapabilities, effectiveCapabilities);

//设置默认线程优先级
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
//恢复其他线程
ZygoteHooks.postForkCommon();
return pid;
}

先把子线程都停止掉,fork完后再恢复,调用native函数nativeForkSystemServer,路径为frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

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
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
jlong effective_capabilities) {
...
pid_t pid = ForkCommon(env, true,
fds_to_close,
fds_to_ignore,
true);
if (pid == 0) {
// System server prcoess does not need data isolation so no need to
// know pkg_data_info_list.
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
permitted_capabilities, effective_capabilities,
MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
false, nullptr, nullptr, /* is_top_app= */ false,
/* pkg_data_info_list */ nullptr,
/* whitelisted_data_info_list */ nullptr, false, false);
} else if (pid > 0) {
...
gSystemServerPid = pid;
//检查SystemServer进程状态
int status;
if (waitpid(pid, &status, WNOHANG) == pid) {
//如果SystemServer进程死亡,重启整个Zygote
ALOGE("System server process %d has died. Restarting Zygote!", pid);
RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
}

//如果是低内存设备,限制SystemServer进程使用内存大小
if (UsePerAppMemcg()) {
if (!SetTaskProfiles(pid, std::vector<std::string>{"SystemMemoryProcess"})) {
ALOGE("couldn't add process %d into system memcg group", pid);
}
}
}
return pid;
}

ForkCommon

我们先看ForkCommon函数

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
const std::vector<int>& fds_to_close,
const std::vector<int>& fds_to_ignore,
bool is_priority_fork) {
//设置子进程信号处理器
SetSignalHandlers();

//C++中的一种可调用对象,ZygoteFailure函数接收4个参数,前三个参数都已提供,最后一个参数占位等待调用方填入
auto fail_fn = std::bind(ZygoteFailure, env, is_system_server ? "system_server" : "zygote",
nullptr, _1);

//在fork期间阻塞住SIGCHLD信号,避免在SIGCHLD信号处理函数中打印log,导致后面关闭的日志fd重新被打开
BlockSignal(SIGCHLD, fail_fn);

//关闭所有日志相关fd
__android_log_close();
AStatsSocket_close();

//SystemServer是Zygote进程起来后第一个fork的出来进程,创建打开的文件描述符表
if (gOpenFdTable == nullptr) {
gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, fail_fn);
} else {
gOpenFdTable->Restat(fds_to_ignore, fail_fn);
}

android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level();

//立即清除任何未使用的内存
mallopt(M_PURGE, 0);

pid_t pid = fork();

if (pid == 0) {
//fork SystemServer时,此参数为true
if (is_priority_fork) {
//设置最高进程优先级
setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
} else {
setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MIN);
}

// The child process.
PreApplicationInit();

//清除所有需要立即关闭的fd
DetachDescriptors(env, fds_to_close, fail_fn);

//USAP机制我们现在不关注
ClearUsapTable();

//重新打开剩余打开的文件描述符,避免文件描述符通过fork在SystemServer和Zygote之间共享
gOpenFdTable->ReopenOrDetach(fail_fn);

//Sanitizer机制,用来检测程序异常
android_fdsan_set_error_level(fdsan_error_level);

// Reset the fd to the unsolicited zygote socket
gSystemServerSocketFd = -1;
} else {
ALOGD("Forked child process %d", pid);
}

//取消之前阻塞的SIGCHLD信号
UnblockSignal(SIGCHLD, fail_fn);

return pid;
}

处理子进程信号

先设置子进程信号处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
static void SetSignalHandlers() {
struct sigaction sig_chld = {.sa_flags = SA_SIGINFO, .sa_sigaction = SigChldHandler};

if (sigaction(SIGCHLD, &sig_chld, nullptr) < 0) {
ALOGW("Error setting SIGCHLD handler: %s", strerror(errno));
}

struct sigaction sig_hup = {};
sig_hup.sa_handler = SIG_IGN;
if (sigaction(SIGHUP, &sig_hup, nullptr) < 0) {
ALOGW("Error setting SIGHUP handler: %s", strerror(errno));
}
}

关于信号的处理,我们在Android源码分析 - init进程中已经了解过一次,SA_SIGINFO这个flag代表调用信号处理函数sa_sigaction的时候,会将信号的信息通过参数siginfo_t传入

SIGHUP表示终端断开信号,SIG_IGN表示忽略信号,即忽略终端断开信号

我们看一下Zygote是怎么处理其子进程信号的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static void SigChldHandler(int /*signal_number*/, siginfo_t* info, void* /*ucontext*/) {
pid_t pid;
int status;
...
int saved_errno = errno;

while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
//通知SystemServer,Zygote收到了一个SIGCHLD信号
sendSigChildStatus(pid, info->si_uid, status);
... //打印子进程状态日志
//如果崩溃的进程是SystemServer,整个Zygote都会退出,再通过init进程重启
if (pid == gSystemServerPid) {
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
"Exit zygote because system server (pid %d) has terminated", pid);
kill(getpid(), SIGKILL);
}
}
...
errno = saved_errno;
}

如果检测到有子进程退出,通知SystemServer,如果这个进程是SystemServer进程,杀掉Zygote进程重启

ZygoteFailure

这里先需要理解一下C++11 中的std::function和std::bind

简单来说,std::bind返回了一个std::function对象,它是一个可调用对象,实际调用的就是传入的第一个参数:ZygoteFailure函数,这个函数接受4个参数,前三个参数都在std::bind时提供好了,第四个参数以_1占位符替代(std::placeholders::_1

实际上调用fail_fn(msg)就相当于调用函数ZygoteFailure(env, "system_server", nullptr, msg)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void ZygoteFailure(JNIEnv* env,
const char* process_name,
jstring managed_process_name,
const std::string& msg) {
std::unique_ptr<ScopedUtfChars> scoped_managed_process_name_ptr = nullptr;
if (managed_process_name != nullptr) {
scoped_managed_process_name_ptr.reset(new ScopedUtfChars(env, managed_process_name));
if (scoped_managed_process_name_ptr->c_str() != nullptr) {
process_name = scoped_managed_process_name_ptr->c_str();
}
}

const std::string& error_msg =
(process_name == nullptr) ? msg : StringPrintf("(%s) %s", process_name, msg.c_str());
//抛出异常
env->FatalError(error_msg.c_str());
__builtin_unreachable();
}

当发生错误后,最终向Java层抛出了一个异常

BlockSignal & UnblockSignal

fork期间需要阻塞住SIGCHLD信号,避免在SIGCHLD信号处理函数中打印log,导致后面关闭的日志fd重新被打开

1
2
3
4
5
6
7
8
9
static void BlockSignal(int signum, fail_fn_t fail_fn) {
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, signum);

if (sigprocmask(SIG_BLOCK, &sigs, nullptr) == -1) {
fail_fn(CREATE_ERROR("Failed to block signal %s: %s", strsignal(signum), strerror(errno)));
}
}

fork结束,取消阻塞SIGCHLD信号

1
2
3
4
5
6
7
8
9
static void UnblockSignal(int signum, fail_fn_t fail_fn) {
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, signum);

if (sigprocmask(SIG_UNBLOCK, &sigs, nullptr) == -1) {
fail_fn(CREATE_ERROR("Failed to un-block signal %s: %s", strsignal(signum), strerror(errno)));
}
}

信号集函数我们之前已经在Android源码分析 - init进程中介绍过了,很简单,就是将SIGCHLD信号添加到屏蔽集中,fork完后再将这个信号从屏蔽集中移除

SpecializeCommon

至此,fork操作结束,我们看一下在SystemServer进程中执行的SpecializeCommon函数

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jlong permitted_capabilities, jlong effective_capabilities,
jint mount_external, jstring managed_se_info,
jstring managed_nice_name, bool is_system_server,
bool is_child_zygote, jstring managed_instruction_set,
jstring managed_app_data_dir, bool is_top_app,
jobjectArray pkg_data_info_list,
jobjectArray whitelisted_data_info_list,
bool mount_data_dirs, bool mount_storage_dirs) {
//process_name = "system_server"
const char* process_name = is_system_server ? "system_server" : "zygote";
auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);

//均为nullptr
auto se_info = extract_fn(managed_se_info);
auto nice_name = extract_fn(managed_nice_name);
auto instruction_set = extract_fn(managed_instruction_set);
auto app_data_dir = extract_fn(managed_app_data_dir);

//当UID发生改变时(root->非root)保留capabilities
if (uid != 0) {
EnableKeepCapabilities(fail_fn);
}
//设置Inheritable集合
SetInheritable(permitted_capabilities, fail_fn);
//从Bounding集合中移除调用线程相关能力
DropCapabilitiesBoundingSet(fail_fn);
...
//创建私有挂载命名空间,挂载虚拟存储
MountEmulatedStorage(uid, mount_external, need_pre_initialize_native_bridge, fail_fn);

...
//设置GroupId
SetGids(env, gids, is_child_zygote, fail_fn);
//设置资源Limit
SetRLimits(env, rlimits, fail_fn);
...
//设置gid及访问权限
if (setresgid(gid, gid, gid) == -1) {
fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
}
//capabilities集合中仍然存在CAP_SYS_ADMIN,需要过滤系统调用
SetUpSeccompFilter(uid, is_child_zygote);
//设置调度策略
SetSchedulerPolicy(fail_fn, is_top_app);
//设置uid及访问权限
if (setresuid(uid, uid, uid) == -1) {
fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
}
...
//设置Capabilities
SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities, fail_fn);
//关闭所有日志相关fd
__android_log_close();
AStatsSocket_close();
...
//设置线程名
if (nice_name.has_value()) {
SetThreadName(nice_name.value());
} else if (is_system_server) { //nice_name为nullptr, 进入此分支
SetThreadName("system_server");
}

//取消掉之前设置的SIGCHID信号处理函数
UnsetChldSignalHandler();

if (is_system_server) {
//调用ZygoteHooks.postForkSystemServer(runtime_flags);
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkSystemServerHooks, runtime_flags);
if (env->ExceptionCheck()) {
fail_fn("Error calling post fork system server hooks.");
}
...
}
...
//调用ZygoteHooks.postForkChild(runtime_flags, true, false, null);
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
is_system_server, is_child_zygote, managed_instruction_set);

//设置默认进程优先级
setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT);

if (env->ExceptionCheck()) {
fail_fn("Error calling post fork hooks.");
}
}

这里做了很多工作,有Capabilities相关,selinux相关,权限相关等等,有点太多了,我标了注释,就不再一一分析了

接下来回到nativeForkSystemServer中,在Zygote进程中继续执行

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
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
jlong effective_capabilities) {
...
if (pid == 0) {
...
} else if (pid > 0) {
...
gSystemServerPid = pid;
//检查SystemServer进程状态
int status;
if (waitpid(pid, &status, WNOHANG) == pid) {
//如果SystemServer进程死亡,重启整个Zygote
ALOGE("System server process %d has died. Restarting Zygote!", pid);
RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
}

//如果是低内存设备,限制SystemServer进程使用内存大小
if (UsePerAppMemcg()) {
if (!SetTaskProfiles(pid, std::vector<std::string>{"SystemMemoryProcess"})) {
ALOGE("couldn't add process %d into system memcg group", pid);
}
}
}
return pid;
}

通过Linux函数waitpid检查SystemServer进程状态,这个函数和之前在Android源码分析 - init进程中提过的waitid函数类似,WNOHANG表示非阻塞等待

如果SystemServer进程死亡,重启整个Zygote

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
...
//SystemServer子进程
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}

//关闭zygote server socket
zygoteServer.closeServerSocket();
return handleSystemServerProcess(parsedArgs);
}

return null;
}

cgroups

如果是小内存设备,使用Linux的cgroups机制,限制SystemServer进程使用内存大小

1
2
3
4
bool UsePerAppMemcg() {
bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
return GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
}

关于Linux的cgroups机制,可以查看这篇文档:cgroups(7) — Linux manual page

关于Android的Cgroups机制,可以看这篇官方文档:Cgroup 抽象层

运行

初始化

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
private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
//将umask设置为0077,这样新的文件和目录将默认为仅属于所有者的权限
Os.umask(S_IRWXG | S_IRWXO);
//设置进程名
if (parsedArgs.mNiceName != null) {
Process.setArgV0(parsedArgs.mNiceName);
}

//对classpath中的apk,分别进行dex优化操作,由installd真正执行
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
if (systemServerClasspath != null) {
performSystemServerDexOpt(systemServerClasspath);
...
}

if (parsedArgs.mInvokeWith != null) {
...
} else {
//SystemServer进入这个分支
ClassLoader cl = null;
if (systemServerClasspath != null) {
cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion);

Thread.currentThread().setContextClassLoader(cl);
}

return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, cl);
}
}

处理一些初始化操作,然后调用ZygoteInit.zygoteInit方法

1
2
3
4
5
6
7
8
9
10
public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
...
//通用初始化
RuntimeInit.commonInit();
//开启binder线程池
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);
}

RuntimeInit的路径为frameworks/base/core/java/com/android/internal/os/RuntimeInit.java,先执行通用初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected static final void commonInit() {
//设置默认线程异常处理器
LoggingHandler loggingHandler = new LoggingHandler();
RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));

//设置时区
RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone"));

//重置Log配置
LogManager.getLogManager().reset();
new AndroidConfig();

//设置网络UA信息
String userAgent = getDefaultUserAgent();
System.setProperty("http.agent", userAgent);

//初始化网络流量统计
NetworkManagementSocketTagger.install();
...
initialized = true;
}

接着执行RuntimeInit.applicationInit

1
2
3
4
5
6
7
8
9
10
11
12
13
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
//如果应用程序调用System.exit(),则立即终止该进程,不运行任何hook函数
nativeSetExitWithoutCleanup(true);
//设置虚拟机参数
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
//解析参数
final Arguments args = new Arguments(argv);
...
//查找startClass中的main方法
return findStaticMain(args.startClass, args.startArgs, classLoader);
}

参数解析

我们看一下它是怎么解析参数的

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
Arguments(String args[]) throws IllegalArgumentException {
parseArgs(args);
}

private void parseArgs(String args[])
throws IllegalArgumentException {
int curArg = 0;
for (; curArg < args.length; curArg++) {
String arg = args[curArg];

if (arg.equals("--")) {
curArg++;
break;
} else if (!arg.startsWith("--")) {
break;
}
}

if (curArg == args.length) {
throw new IllegalArgumentException("Missing classname argument to RuntimeInit!");
}

startClass = args[curArg++];
startArgs = new String[args.length - curArg];
System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
}

循环读参数直到有一项参数为”–”或者不以”–”开头,然后以下一个参数作为startClass,用再下一个参数到args数组结尾生成一个新的数组作为startArgs,我们观察一下forkSystemServer方法中设置的args

1
2
3
4
5
6
7
8
9
10
11
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
+ "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};

可以看出,startClass应该为com.android.server.SystemServerstartArgs数组为空

反射执行

接着调用findStaticMain方法

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
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;

try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}

Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}

int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}

/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
return new MethodAndArgsCaller(m, argv);
}

这里使用了Java中的反射,找到了SystemServer中对应的main方法,并用其创建了一个Runnable对象MethodAndArgsCaller

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
static class MethodAndArgsCaller implements Runnable {
private final Method mMethod;
private final String[] mArgs;

public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}

public void run() {
try {
//执行SystemServer.main方法
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}

我们最后再回到ZygoteInitmain方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String argv[]) {
...
//启动SystemServer
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);

//子进程中才会满足r != null
if (r != null) {
//此时执行这个Runnable
r.run();
return;
}
}
}

执行这个在子进程中返回出去的RunnableMethodAndArgsCaller,反射调用SystemServer.main方法

结束

至此,SystemServer的启动我们就分析完了,下一篇我们将分析SystemServer启动后做了什么


Linux Capabilities机制

介绍

传统的Linux权限控制粒度太粗,以passwd命令为例,修改用户密码是需要root权限的,但普通用户应该是能够修改自己密码的才对,这时候Linux就使用了SUIDEUID机制,使passwd进程以它的所有者root权限运行这样就可以以root权限修改密码了

SUID机制是有安全隐患的,passwd进程只需要修改密码的就可以了,却在整个运行周期内获得了root权限,一旦出现漏洞,很有可能会被利用

所以,Linux内核在2.2后引入了Capabilities机制,细粒度化了权限控制,可以做到按需授权

这里是文档:https://man7.org/linux/man-pages/man7/capabilities.7.html

如何使用

首先,Capabilities是有一个集合的概念的,即一个进程或可执行文件,它可以拥有哪些特权的集合

可执行文件

可执行文件有三种Capabilities集合:

Permitted

当文件执行时,这个集合的内容会被添加到进程的Permitted集合中

Inheritable

当文件执行后,这个集合会与进程的Inheritable集合做位与操作(&),以确定进程在执行execve函数后哪些capabilites可以被继承

Effective

这不是一个集合,而是一个位(bit),如果此bit设为1,则Permitted集合中新增的capabilites会在执行execve函数后添加到进程的Effective集合中

命令

  • 设置capabilites
1
2
3
4
5
setcap [capability,capability,...]+[ep] [文件]

# or

setcap [capability+ep capability+ep ...] [文件]

capability就是某个特权值,+ep代表加入EffectivePermitted集合中

  • 获取capabilites
1
getcap [文件]

线程(进程)

线程(进程)有五种Capabilities集合:

Permitted

这个集合定义了线程所能够拥有的特权的上限,是InheritableEffective集合的的超集

Inheritable

包含了当执行execve 函数时,能够被新的可执行文件继承的capabilities(执行execve 函数后会被添加到Permitted集合中)

Effective

内核检查特权操作时,实际检查的集合(可以通过执行操作前增/删Effective中的capabilities,以达到临时开/关权限的功能)

Bounding (内核2.6.25以后)

这个集合是 Inheritable 集合的超集,如果某个capability不在Bounding集合中,即使它在Permitted集合中,该线程也不能将该capability添加到它的Inheritable集合中,该集合在execve后不可再添加capabilities

Ambient (内核4.3以后)

这个集合是PermittedInheritable的子集,当PermittedInheritable删除某个capability时,也会自动删除该集合中对应的capability,子进程会自动继承这个集合中的capabilities,子进程的PermittedEffectiveAmbient都会拥有这些capabilities

函数

capset

原型:

1
int capset(cap_user_header_t hdrp, const cap_user_data_t datap);

文档:https://linux.die.net/man/2/capset

capget

原型:

1
int capget(cap_user_header_t hdrp , cap_user_data_t datap);

文档:https://linux.die.net/man/2/capget

结构体

1
2
3
4
5
6
7
8
9
10
typedef struct __user_cap_header_struct {
__u32 version;
int pid;
} *cap_user_header_t;

typedef struct __user_cap_data_struct {
__u32 effective;
__u32 permitted;
__u32 inheritable;
} *cap_user_data_t;

计算公式

我们用 P 代表执行 execve() 前线程的 capabilities,P' 代表执行 execve() 后线程的 capabilitiesF 代表可执行文件的 capabilities,那么:

P’(ambient) = (file is privileged) ? 0 : P(ambient)

P’(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & P(bounding))) | P’(ambient)

P’(effective) = F(effective) ? P’(permitted) : P’(ambient)

P’(inheritable) = P(inheritable) [i.e., unchanged]

P’(bounding) = P(bounding) [i.e., unchanged]

我们一条一条来解释:

  • 如果用户是 root 用户,那么执行 execve() 后线程的 Ambient 集合是空集;如果是普通用户,那么执行 execve() 后线程的 Ambient 集合将会继承执行 execve() 前线程的 Ambient 集合。

  • 执行 execve() 前线程的 Inheritable 集合与可执行文件的 Inheritable 集合取交集,会被添加到执行 execve() 后线程的 Permitted 集合;可执行文件的 capability bounding 集合与可执行文件的 Permitted 集合取交集,也会被添加到执行 execve() 后线程的 Permitted 集合;同时执行 execve() 后线程的 Ambient 集合中的 capabilities 会被自动添加到该线程的 Permitted 集合中。

  • 如果可执行文件开启了 Effective 标志位,那么在执行完 execve() 后,线程 Permitted 集合中的 capabilities 会自动添加到它的 Effective 集合中。

  • 执行 execve() 前线程的 Inheritable 集合会继承给执行 execve() 后线程的 Inheritable 集合。

这里有几点需要着重强调:

  1. 上面的公式是针对系统调用 execve() 的,如果是 fork(),那么子线程的 capabilities 信息完全复制父进程的 capabilities 信息。

  2. 可执行文件的 Inheritable 集合与线程的 Inheritable 集合并没有什么关系,可执行文件 Inheritable 集合中的 capabilities 不会被添加到执行 execve() 后线程的 Inheritable 集合中。如果想让新线程的 Inheritable 集合包含某个 capability,只能通过 capset() 将该 capability 添加到当前线程的 Inheritable 集合中(因为 P’(inheritable) = P(inheritable))。

  3. 如果想让当前线程 Inheritable 集合中的 capabilities 传递给新的可执行文件,该文件的 Inheritable 集合中也必须包含这些 capabilities(因为 P’(permitted) = (P(inheritable) & F(inheritable))|…)。

  4. 将当前线程的 capabilities 传递给新的可执行文件时,仅仅只是传递给新线程的 Permitted 集合。如果想让其生效,新线程必须通过 capset() 将 capabilities 添加到 Effective 集合中。或者开启新的可执行文件的 Effective 标志位(因为 P’(effective) = F(effective) ? P’(permitted) : P’(ambient))。

  5. 在没有 Ambient 集合之前,如果某个脚本不能调用 capset(),但想让脚本中的线程都能获得该脚本的 Permitted 集合中的 capabilities,只能将 Permitted 集合中的 capabilities 添加到 Inheritable 集合中(P’(permitted) = P(inheritable) & F(inheritable)|…),同时开启 Effective 标志位(P’(effective) = F(effective) ? P’(permitted) : P’(ambient))。有 有 Ambient 集合之后,事情就变得简单多了,后续的文章会详细解释。

  6. 如果某个 UID 非零(普通用户)的线程执行了 execve(),那么 PermittedEffective 集合中的 capabilities 都会被清空。

  7. 从 root 用户切换到普通用户,那么 PermittedEffective 集合中的 capabilities 都会被清空,除非设置了 SECBIT_KEEP_CAPS 或者更宽泛的 SECBIT_NO_SETUID_FIXUP。

附录

Capabilities表

Capability 描述
CAP_AUDIT_CONTROL 启用和禁用内核审计;改变审计过滤规则;检索审计状态和过滤规则
CAP_AUDIT_READ 允许通过 multicast netlink 套接字读取审计日志
CAP_AUDIT_WRITE 将记录写入内核审计日志
CAP_BLOCK_SUSPEND 使用可以阻止系统挂起的特性
CAP_BPF (5.8) 从CAP_SYS_ADMIN分离一部分BFP功能,控制了一些BPF特定的操作,包括创建BPF maps、使用一些高级的BPF程序功能、访问BPF type format(BTF)数据等
CAP_CHECKPOINT_RESTORE (5.9) 允许更新/proc/sys/kernel/ns_last_pid,使用set_tid特性,读其他进程的/proc/[pid]/map_files
CAP_CHOWN 修改文件所有者的权限
CAP_DAC_OVERRIDE 忽略文件的 DAC 访问限制
CAP_DAC_READ_SEARCH 忽略文件读及目录搜索的 DAC 访问限制
CAP_FOWNER 忽略文件属主 ID 必须和进程用户 ID 相匹配的限制
CAP_FSETID 允许设置文件的 setuid 位
CAP_IPC_LOCK 允许锁定共享内存片段
CAP_IPC_OWNER 忽略 IPC 所有权检查
CAP_KILL 允许对不属于自己的进程发送信号
CAP_LEASE 允许修改文件锁的 FL_LEASE 标志
CAP_LINUX_IMMUTABLE 允许修改文件的 IMMUTABLE 和 APPEND 属性标志
CAP_MAC_ADMIN 允许 MAC 配置或状态更改
CAP_MAC_OVERRIDE 忽略文件的 DAC 访问限制
CAP_MKNOD 允许使用 mknod() 系统调用
CAP_NET_ADMIN 允许执行网络管理任务
CAP_NET_BIND_SERVICE 允许绑定到小于 1024 的端口
CAP_NET_BROADCAST 允许网络广播和多播访问
CAP_NET_RAW 允许使用原始套接字
CAP_PERFMON (5.8) 管理性能监控task
CAP_SETGID 允许改变进程的 GID
CAP_SETFCAP 允许为文件设置任意的 capabilities
CAP_SETPCAP 允许设置其他进程的 capabilities
CAP_SETUID 允许改变进程的 UID
CAP_SYS_ADMIN 允许执行系统管理任务,如加载或卸载文件系统、设置磁盘配额等
CAP_SYS_BOOT 允许重新启动系统
CAP_SYS_CHROOT 允许使用 chroot() 系统调用
CAP_SYS_MODULE 允许插入和删除内核模块
CAP_SYS_NICE 允许提升优先级及设置其他进程的优先级
CAP_SYS_PACCT 允许执行进程的 BSD 式审计
CAP_SYS_PTRACE 允许跟踪任何进程
CAP_SYS_RAWIO 允许直接访问 /devport、/dev/mem、/dev/kmem 及原始块设备
CAP_SYS_RESOURCE 忽略资源限制
CAP_SYS_TIME 允许改变系统时钟
CAP_SYS_TTY_CONFIG 允许配置 TTY 设备
CAP_SYSLOG 允许使用 syslog() 系统调用
CAP_WAKE_ALARM 允许触发一些能唤醒系统的东西(比如 CLOCK_BOOTTIME_ALARM 计时器)

参考文献

Linux Capabilities 简介

Linux的capabilities机制

Linux Capabilities 入门教程:概念篇


Android源码分析 - Zygote进程

开篇

本篇以android-11.0.0_r25作为基础解析

上一篇文章Android源码分析 - init进程,我们分析了Android第一个用户进程init进程的启动过程和之后的守护服务

init进程启动了很多服务,例如Zygote,ServiceManager,MediaServer,SurfaceFlinger等,我们平常写Android应用都是使用Java语言,这次我们就先从Java世界的半边天:Zygote进程 开始分析

介绍

Zygote意为受精卵,它有两大作用,一是启动SystemServer,二是孵化启动App

启动服务

我们已经知道了init进程会从init.rc文件中解析并启动服务,那zygote是在哪定义的呢,init.rc的头几行就有一个import:import /system/etc/init/hw/init.${ro.zygote}.rc

我们在init.rc同目录下就能找到几个对应的文件:init.zygote32_64.rc init.zygote32.rc init.zygote64_32.rc init.zygote64.rc,具体import哪个文件与具体设备硬件有关,现在64位手机这么普及了,我们就以init.zygote64.rc为目标分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks

下面的子项我们暂时不用关心,先记住app_process64的启动参数-Xzygote /system/bin --zygote --start-system-server即可

Zygote启动的源文件为frameworks/base/cmds/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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
int main(int argc, char* const argv[])
{
...
//创建了一个AppRuntime,继承自AndroidRuntime,重写了一些回调方法
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments
// ignore argv[0]
//在启动服务时,传进来的参数是包含文件路径的
//我们不需要这个参数,就减一下个数,移一下指针
argc--;
argv++;

...

//处理参数,这里只添加了一个-Xzygote参数
int i;
for (i = 0; i < argc; i++) {
...
if (argv[i][0] != '-') {
break;
}
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i; // Skip --.
break;
}

runtime.addOption(strdup(argv[i]));
...
}

// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;

//跳过参数/system/bin,这个参数目前没有被使用
++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
//有--zygote参数
zygote = true;
//ZYGOTE_NICE_NAME在64位下为zygote64,32位下为zygote
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
//有-start-system-server参数
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
className.setTo(arg);
break;
} else {
--i;
break;
}
}

Vector<String8> args;
if (!className.isEmpty()) {
//这个分支不会进入Zygote模式
...
} else {
// We're in zygote mode.
//新建Dalvik缓存目录
maybeCreateDalvikCache();

//添加启动参数
if (startSystemServer) {
args.add(String8("start-system-server"));
}

char prop[PROP_VALUE_MAX];
if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
ABI_LIST_PROPERTY);
return 11;
}

String8 abiFlag("--abi-list=");
abiFlag.append(prop);
args.add(abiFlag);

// In zygote mode, pass all remaining arguments to the zygote
// main() method.
//Zygote模式下没有其他参数了
for (; i < argc; ++i) {
args.add(String8(argv[i]));
}
}

if (!niceName.isEmpty()) {
//设置程序名以及进程名
runtime.setArgv0(niceName.string(), true /* setProcName */);
}

if (zygote) {
//执行AndroidRuntime::start方法
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
}

整体结构还是比较简单的,就是处理一下参数,进入zygote对应的分支,执行AndroidRuntime::start方法,第一个参数传的是ZygoteInit在Java中的类名,第二个参数传了一些选项(start-system-server和abi-list),第三个参数传了true,代表启动虚拟机的时候需要额外添加一些JVM参数

AndroidRuntime::start

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
...
static const String8 startSystemServer("start-system-server");
// Whether this is the primary zygote, meaning the zygote which will fork system server.
//64_32位兼容设备上会启动两个Zygote,一个叫zygote,一个叫zygote_secondary
bool primary_zygote = false;

//有start-system-server选项则代表是主Zygote
for (size_t i = 0; i < options.size(); ++i) {
if (options[i] == startSystemServer) {
primary_zygote = true;
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
}
}

//检查和配置一些环境变量
...

/* start the virtual machine */
//加载libart.so
JniInvocation jni_invocation;
jni_invocation.Init(NULL);

JNIEnv* env;
//启动JVM
if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
return;
}
//回调AppRuntime中重写的方法
onVmCreated(env);

/*
* Register android functions.
*/
//注册Android JNI函数
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}

//创建一个Java层的String数组用来装参数
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;

stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
//第一个参数是类名com.android.internal.os.ZygoteInit
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);

//剩下来参数分别是start-system-server和abi-list
for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}

/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
//将Java类名中的"."替换成"/",这是JNI中的类名规则
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
//获取ZygoteInit中的main方法,参数为String类型,返回值为void
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
//执行ZygoteInit的main方法
env->CallStaticVoidMethod(startClass, startMeth, strArray);

//后面的代码除非JVM挂了,否则不会执行
...
}
}
...
}

首先判断选项中是否携带参数start-system-server,如有,则将它视为主Zygote,接着就开始启动JVM了

启动JVM

JniInvocation

使用JniInvocation初始化Android ART虚拟机环境,它的路径是libnativehelper/include_platform/nativehelper/JniInvocation.h,我们来看一下它是怎么做的

我们首先看一下它的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
/* libnativehelper/include_platform/nativehelper/JniInvocation.h */
class JniInvocation final {
public:
JniInvocation() {
impl_ = JniInvocationCreate();
}

~JniInvocation() {
JniInvocationDestroy(impl_);
}
...
}

调用JniInvocationCreate方法创建了一个JniInvocationImpl实例对象

1
2
3
JniInvocationImpl* JniInvocationCreate() {
return new JniInvocationImpl();
}

接着调用JniInvocation::Init方法

1
2
3
4
5
6
7
bool Init(const char* library) {
return JniInvocationInit(impl_, library) != 0;
}

int JniInvocationInit(JniInvocationImpl* instance, const char* library) {
return instance->Init(library) ? 1 : 0;
}

可以看到,JniInvocation实际上是个代理类,内部实现是交给JniInvocationImpl的,路径为libnativehelper/JniInvocation.cpp

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
bool JniInvocationImpl::Init(const char* library) {
...
//非debug一律为libart.so
library = GetLibrary(library, buffer);
//加载libart.so库
handle_ = OpenLibrary(library);
if (handle_ == NULL) {
//如果是加载libart.so库失败,直接返回false
if (strcmp(library, kLibraryFallback) == 0) {
// Nothing else to try.
ALOGE("Failed to dlopen %s: %s", library, GetError().c_str());
return false;
}
...
//如果是加载其他库失败,尝试回退加载libart.so库
library = kLibraryFallback;
handle_ = OpenLibrary(library);
if (handle_ == NULL) {
ALOGE("Failed to dlopen %s: %s", library, GetError().c_str());
return false;
}
}
//从libart.so库获得三个JVM相关的函数地址
if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_GetDefaultJavaVMInitArgs_),
"JNI_GetDefaultJavaVMInitArgs")) {
return false;
}
if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_CreateJavaVM_),
"JNI_CreateJavaVM")) {
return false;
}
if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_GetCreatedJavaVMs_),
"JNI_GetCreatedJavaVMs")) {
return false;
}
return true;
}

加载libart.so库

我们先看GetLibrary方法

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
static const char* kLibraryFallback = "libart.so";

const char* JniInvocationImpl::GetLibrary(const char* library,
char* buffer,
bool (*is_debuggable)(),
int (*get_library_system_property)(char* buffer)) {
#ifdef __ANDROID__
const char* default_library;

if (!is_debuggable()) {
library = kLibraryFallback;
default_library = kLibraryFallback;
} else {
...
}
#else
...
const char* default_library = kLibraryFallback;
#endif
if (library == NULL) {
library = default_library;
}

return library;
}

可以看到,在debug模式或library参数为NULL的情况下都是直接返回的libart.so

OpenLibrary方法是使用了dlopen函数,加载libart.so

1
2
3
4
5
6
void* OpenLibrary(const char* filename) {
...
const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
return dlopen(filename, kDlopenFlags);
...
}

dlopen

原型:void *dlopen(const char *filename, int flags);

文档:https://man7.org/linux/man-pages/man3/dlopen.3.html

这是一个Linux函数,用来加载一个动态链接库,当加载成功时,会返回一个句柄

上面的这两个参数,RTLD_NOW代表立即计算库的依赖性,RTLD_NODELETE代表不要再dlclose期间卸载库,这样当再次加载库的时候不会重新初始化对象的静态全局变量,使用这个flag是为了确保libart.so在关闭时不会被取消映射。因为即使在 JNI_DeleteJavaVM 调用之后,某些线程仍可能尚未完成退出,如果卸载该库,则可能导致段错误

从libart.so库中寻找函数地址

接着调用FindSymbol函数查找函数地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define FUNC_POINTER void*

bool JniInvocationImpl::FindSymbol(FUNC_POINTER* pointer, const char* symbol) {
//获得函数地址
*pointer = GetSymbol(handle_, symbol);
//获取失败,卸载libart.so库
if (*pointer == NULL) {
ALOGE("Failed to find symbol %s: %s\n", symbol, GetError().c_str());
CloseLibrary(handle_);
handle_ = NULL;
return false;
}
return true;
}

FUNC_POINTER GetSymbol(void* handle, const char* symbol) {
...
return dlsym(handle, symbol);
...
}

dlsym

原型:void *dlsym(void *restrict handle , const char *restrict symbol);

文档:https://man7.org/linux/man-pages/man3/dlsym.3.html

也是一个Linux函数,用来从已加载的动态链接库中获取一个函数的地址

传入的第一个参数为之前加载库时返回的句柄,第二个参数为函数名

总结

回顾一下全局,JniInvocationImpl::Init的作用是,加载libart.so库,并从中获取三个函数指针:

  • JNI_GetDefaultJavaVMInitArgs:获取虚拟机的默认初始化参数
  • JNI_CreateJavaVM:创建虚拟机实例
  • JNI_GetCreatedJavaVMs:获取创建的虚拟机实例

这几个函数被定义在jni.h中,后面我们创建JVM的时候会用到这些函数

AndroidRuntime::startVm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote)
{
JavaVMInitArgs initArgs;
...
//配置了一大堆JVM选项

initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray();
initArgs.nOptions = mOptions.size();
initArgs.ignoreUnrecognized = JNI_FALSE;

//创建并初始化JVM
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed\n");
return -1;
}

return 0;
}

JNI_CreateJavaVM方法就是我们之前从libart.so里获得的方法,被编译进libart.so前,源码的路径为art/runtime/jni/java_vm_ext.cc,接下来属于ART虚拟机的工作,我们就不再往下深究了

后面有一个onVmCreated回调,但在zygote模式下没做任何事

注册JNI函数

接着调用startReg函数注册Android JNI函数

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
/*
* Register android native functions with the VM.
*/
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
...
//设置Native创建线程的函数,通过javaCreateThreadEtc这个函数创建的线程
//会把创建的线程attach到JVM中,使其既能执行c/c++代码,也能执行Java代码
androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

ALOGV("--- registering native functions ---\n");

//创建局部引用栈帧
env->PushLocalFrame(200);

//注册jni函数
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
//将当前栈帧出栈,释放其中所有局部引用
env->PopLocalFrame(NULL);

return 0;
}

首先hook了Native创建线程的函数,之后创建线程便会调用我们设置的javaCreateThreadEtc函数,
会把创建的线程attach到JVM中,使其既能执行c/c++代码,也能执行Java代码。这个等之后看到Android线程创建的时候再分析

PushLocalFramePopLocalFrame是一对函数,它们是用来管理JNI的局部引用的

首先,PushLocalFrame会创建出一个局部引用栈帧,之后JNI创建出来的局部引用都会放在这个栈帧里,等使用结束后调用PopLocalFrame函数,会将当前栈帧出栈,并且释放其中所有的局部引用

接下来我们看register_jni_procs函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct RegJNIRec {
int (*mProc)(JNIEnv*);
};

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
for (size_t i = 0; i < count; i++) {
if (array[i].mProc(env) < 0) {
...
return -1;
}
}
return 0;
}

很简单,就是循环执行gRegJNI数组中所有的函数

1
2
3
4
5
6
7
8
9
10
#define REG_JNI(name)      { name }

static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
...
};

gRegJNI数组中存放着很多Java类注册JNI函数的函数,后面大家如果阅读源码看到了Android中的native方法可以来这边找它所对应的c++实现

这边注册的类非常多,我们就取第一个register_com_android_internal_os_RuntimeInit为例分析一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;

int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
const JNINativeMethod methods[] = {
{"nativeFinishInit", "()V",
(void*)com_android_internal_os_RuntimeInit_nativeFinishInit},
{"nativeSetExitWithoutCleanup", "(Z)V",
(void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},
};
return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
methods, NELEM(methods));
}

创建了一个JNINativeMethod结构体,第一个成员是Java中的方法名,第二个成员是Java中对应方法的签名,第三个成员是Java方法对应native函数的函数指针,然后调用jniRegisterNativeMethods函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);

ALOGV("Registering %s's %d native methods...", className, numMethods);

scoped_local_ref<jclass> c(env, findClass(env, className));
ALOG_ALWAYS_FATAL_IF(c.get() == NULL,
"Native registration unable to find class '%s'; aborting...",
className);

int result = e->RegisterNatives(c.get(), gMethods, numMethods);
ALOG_ALWAYS_FATAL_IF(result < 0, "RegisterNatives failed for '%s'; aborting...",
className);

return 0;
}

这个函数先通过Java类名获得一个jclass对象,接着调用JNIEnv::RegisterNatives函数,这个函数定义在jni.h中,实现在libart.so库中,我们在平时开发jni的时候,动态注册native方法的时候就会使用到它,这里就不再往下分析了

进入JAVA世界

JVM启动好了,JNI函数也注册完毕了,接下来就该进入到JAVA世界了

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
46
47
48
49
50
51
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
...
//创建一个Java层的String数组用来装参数
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;

stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
//第一个参数是类名com.android.internal.os.ZygoteInit
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);

//剩下来参数分别是start-system-server和abi-list
for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}

/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
//将Java类名中的"."替换成"/",这是JNI中的类名规则
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
//获取ZygoteInit中的main方法,参数为String类型,返回值为void
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
//执行ZygoteInit的main方法
env->CallStaticVoidMethod(startClass, startMeth, strArray);

//后面的代码除非JVM挂了,否则不会执行
...
}
}
...
}

这里看不懂的自己先补一下JNI知识,总之就是调用了com.android.internal.os.ZygoteInit类的静态方法main,以com.android.internal.os.ZygoteInitstart-system-serverabi-list作为参数

ZygoteInit

ZygoteInit类的源码路径为frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

我们这就开始分析它的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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
public static void main(String argv[]) {
ZygoteServer zygoteServer = null;

// Mark zygote start. This ensures that thread creation will throw
// an error.
//标记着zygote开始启动,不允许创建线程(Zygote必须保证单线程)
ZygoteHooks.startZygoteNoThreadCreation();

// Zygote goes into its own process group.
//设置进程组id
try {
Os.setpgid(0, 0);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}

Runnable caller;
try {
...
//配置参数
boolean startSystemServer = false;
String zygoteSocketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if ("--enable-lazy-preload".equals(argv[i])) {
enableLazyPreload = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}

//public static final String PRIMARY_SOCKET_NAME = "zygote";
final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
...
if (!enableLazyPreload) {
...
//预加载
preload(bootTimingsTraceLog);
...
}
...

//调用Java层的垃圾回收
gcAndFinalize();
...
//回调AppRRuntime中的onZygoteInit函数
Zygote.initNativeState(isPrimaryZygote);

//解除创建线程限制(马上就要执行fork了,子进程要有能力创建线程)
ZygoteHooks.stopZygoteNoThreadCreation();

//创建socket
zygoteServer = new ZygoteServer(isPrimaryZygote);

//启动SystemServer
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);

// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
}

Log.i(TAG, "Accepting command socket connections");

// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
//执行死循环监听socket,负责接收事件,启动App
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
if (zygoteServer != null) {
zygoteServer.closeServerSocket();
}
}

// We're in the child process and have exited the select loop. Proceed to execute the
// command.
//接收到AMS的启动App请求后,fork出子进程,处理App启动
if (caller != null) {
caller.run();
}
}

先调用ZygoteHooks.startZygoteNoThreadCreation()禁止创建线程,Zygote必须保证单线程,这和fork机制有关,fork函数只会将当前线程复制到子进程,同时,fork会将锁也复制到子进程中,如果在fork之前,有一个线程持有了锁,但是fork的时候没把这个线程复制到子进程中,这把锁就被永久持有了,会造成死锁

android.system.Os

我们看一下Os是什么,根据import我们知道它的全限定类名为android.system.Os,它的源码路径为libcore/luni/src/main/java/android/system/Os.java

1
2
3
4
5
6
7
8
9
...
import libcore.io.Libcore;

public final class Os {
private Os() {}
...
public static void setpgid(int pid, int pgid) throws ErrnoException { Libcore.os.setpgid(pid, pgid); }
...
}

它里面全是这种形式的静态代理方法,实际调用Libcore.os执行,我们就以setpgid方法去追踪一下

Libcore位于libcore/luni/src/main/java/libcore/io/Libcore.javaos是其中的一个静态变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class Libcore {
private Libcore() { }

/**
* Direct access to syscalls. Code should strongly prefer using {@link #os}
* unless it has a strong reason to bypass the helpful checks/guards that it
* provides.
*/
public static final Os rawOs = new Linux();

/**
* Access to syscalls with helpful checks/guards.
* For read access only; the only supported way to update this field is via
* {@link #compareAndSetOs}.
*/
@UnsupportedAppUsage
public static volatile Os os = new BlockGuardOs(rawOs);

public static Os getOs() {
return os;
}
...
}

os的类型为BlockGuardOs,以Linux类型的常量rawOs作为构造方法参数实例化,它继承自ForwardingOs

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ForwardingOs implements Os {
@UnsupportedAppUsage
private final Os os;

@UnsupportedAppUsage
@libcore.api.CorePlatformApi
protected ForwardingOs(Os os) {
this.os = Objects.requireNonNull(os);
}
...
public void setpgid(int pid, int pgid) throws ErrnoException { os.setpgid(pid, pgid); }
...
}

可以看到,这其实又是一个代理类,实际上是直接调用了Linux类的方法,至于BlockGuardOs,它在部分方法上做了一些回调监听,除此之外也是直接调用了Linux类的方法

1
2
3
4
5
6
7
public final class Linux implements Os {
Linux() { }

...
public native int getpgid(int pid);
...
}

里面基本上都是JNI调用native方法,对应的c++源码路径为libcore/luni/src/main/native/libcore_io_Linux.cpp,下面是注册JNI函数的函数

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
#define NATIVE_METHOD(className, functionName, signature)                \
MAKE_JNI_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName)

#define MAKE_JNI_NATIVE_METHOD(name, signature, function) \
_NATIVEHELPER_JNI_MAKE_METHOD(kNormalNative, name, signature, function)

#define _NATIVEHELPER_JNI_MAKE_METHOD(kind, name, sig, fn) \
MAKE_CHECKED_JNI_NATIVE_METHOD(kind, name, sig, fn)

#define MAKE_CHECKED_JNI_NATIVE_METHOD(native_kind, name_, signature_, fn) \
([]() { \
using namespace nativehelper::detail; /* NOLINT(google-build-using-namespace) */ \
static_assert( \
MatchJniDescriptorWithFunctionType<native_kind, \
decltype(fn), \
fn, \
sizeof(signature_)>(signature_),\
"JNI signature doesn't match C++ function type."); \
/* Suppress implicit cast warnings by explicitly casting. */ \
return JNINativeMethod { \
const_cast<decltype(JNINativeMethod::name)>(name_), \
const_cast<decltype(JNINativeMethod::signature)>(signature_), \
reinterpret_cast<void*>(&(fn))}; \
})()

static JNINativeMethod gMethods[] = {
...
NATIVE_METHOD(Linux, setpgid, "(II)V"),
...
};
void register_libcore_io_Linux(JNIEnv* env) {
...
jniRegisterNativeMethods(env, "libcore/io/Linux", gMethods, NELEM(gMethods));
}

可以看到,Java层方法对应native方法的格式为Linux_方法名,我们通过这种规则找到setpgid方法对应的函数

1
2
3
static void Linux_setpgid(JNIEnv* env, jobject, jint pid, int pgid) {
throwIfMinusOne(env, "setpgid", TEMP_FAILURE_RETRY(setpgid(pid, pgid)));
}

可以看到是直接调用了Linux系统层函数

总结

综上所述,android.system.Os类存在的意义是可以使Java层能够方便的调用Linux系统方法

预加载

接下来就是一些参数配置工作,然后调用preload预加载

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
static void preload(TimingsTraceLog bootTimingsTraceLog) {
...
//预加载Java类
preloadClasses();
...
//加载三个jar文件
/* /system/framework/android.hidl.base-V1.0-java.jar */
/* /system/framework/android.hidl.manager-V1.0-java.jar */
/* /system/framework/android.test.base.jar */
cacheNonBootClasspathClassLoaders();
...
//预加载系统资源
preloadResources();
...
//预加载硬件抽象层?
nativePreloadAppProcessHALs();
...
//预加载opengl
maybePreloadGraphicsDriver();
...
//预加载动态库
preloadSharedLibraries();
//TextView预加载Font
preloadTextResources();
//预加载webviewchromium
WebViewFactory.prepareWebViewInZygote();
...
}

preloadClasses

我们看看Java类的预加载

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
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";

private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();

InputStream is;
try {
is = new FileInputStream(PRELOADED_CLASSES);
} catch (FileNotFoundException e) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
return;
}
...
try {
BufferedReader br =
new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE);

int count = 0;
String line;
while ((line = br.readLine()) != null) {
// Skip comments and blank lines.
line = line.trim();
if (line.startsWith("#") || line.equals("")) {
continue;
}

...
//Java类加载器加载类
Class.forName(line, true, null);
count++;
...
}

Log.i(TAG, "...preloaded " + count + " classes in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
} catch (IOException e) {
Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
} finally {
...
}
}

主要的代码就是从/system/etc/preloaded-classes这个文件中读取出需要预加载的类,再通过Class.forName使用类加载器加载一遍,编译前的路径为frameworks/base/config/preloaded-classes

为什么需要预加载

Zygote进程的一大作用就是孵化App,那是怎么孵化的呢?这过程中肯定要使用到fork,我们知道,fork后,父子进程是可以共享资源的,既然我们每启动一个App,都需要使用虚拟机、加载一些View等必要的类等等,那为何不在父进程中加载好这些,fork后子进程不就可以直接使用它们了吗?这就是Zygote进程预加载的原因

启动binder线程池

预加载结束后,会先清理一下Java层的垃圾,然后调用Zygote.initNativeState(isPrimaryZygote)方法,这个方法调用了native方法nativeInitNativeState,这个方法是在AndroidRuntime中注册的,同时也实现在AndroidRuntime中,

1
2
3
4
static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
gCurRuntime->onZygoteInit();
}

我们之前分析过,执行的是AndroidRuntime的子类AppRuntimeonZygoteInit函数

1
2
3
4
5
6
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}

通过这个函数启动Binder线程池,至于Binder的细节,我们留到以后再分析

启动SystemServer

Zygote进程孵化的第一个进程便是SystemServer,具体怎么孵化的,孵化后SystemServer又做了什么,留在下一节我们再分析

ZygoteServer

构造方法

我们知道,我们App都是从Zygote孵化而来的,App启动是从ActivityManagerServicestartActivity方法开始的,那么AMS是怎么和Zygote通信的呢,答案是通过socket

我们先从ZygoteServer的构造方法开始看起

1
2
3
4
5
6
7
8
9
10
ZygoteServer(boolean isPrimaryZygote) {
...
if (isPrimaryZygote) {
mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
...
} else {
...
}
...
}

其中有一些东西是和USAP机制有关的,但在AOSP中默认是关闭的,关于USAP机制我们以后再分析,现在只需要关注mZygoteSocket就可以了,它是通过调用Zygote.createManagedSocketFromInitSocket赋值的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
int fileDesc;
//ANDROID_SOCKET_zygote
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;

try {
//获得文件描述符
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException("Socket unset or invalid: " + fullSocketName, ex);
}

try {
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
return new LocalServerSocket(fd);
} catch (IOException ex) {
throw new RuntimeException(
"Error building socket from file descriptor: " + fileDesc, ex);
}
}

很简单,就是从系统属性中获取一个fd,然后实例化LocalServerSocket,路径为frameworks/base/core/java/android/net/LocalServerSocket.java

1
2
3
4
5
6
public LocalServerSocket(FileDescriptor fd) throws IOException
{
impl = new LocalSocketImpl(fd);
impl.listen(LISTEN_BACKLOG);
localAddress = impl.getSockAddress();
}

在内部创建了一个LocalSocketImpl,然后调用了listen方法声明开始监听这个fd,内部调用了Linux的listen函数

runSelectLoop

然后我们来看在ZygoteInit中调用的runSelectLoop方法

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
ArrayList<ZygoteConnection> peers = new ArrayList<>();

//将server socket fd加到列表头(后面需要判断是否为server socket)
socketFDs.add(mZygoteSocket.getFileDescriptor());
peers.add(null);
...
while (true) {
...
StructPollfd[] pollFDs;
...
pollFDs = new StructPollfd[socketFDs.size()];

int pollIndex = 0;
for (FileDescriptor socketFD : socketFDs) {
pollFDs[pollIndex] = new StructPollfd();
pollFDs[pollIndex].fd = socketFD;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
}
... //上面一大段都与USAP机制有关,这里先不关注

int pollReturnValue;
try {
//等待文件描述符上的事件
pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}

if (pollReturnValue == 0) { //没有接收到事件(超时),从循环开头重新开始等待事件
...
} else {
...
while (--pollIndex >= 0) {
//没有要读取的数据,跳过
if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
continue;
}

if (pollIndex == 0) {
//pollIndex == 0说明这个fd是ZygoteServer socket的fd

//接受并建立一个socket连接
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
//将client socket fd加入列表
socketFDs.add(newPeer.getFileDescriptor());

} else if (pollIndex < usapPoolEventFDIndex) {
//不使用USAP机制的话,pollIndex < usapPoolEventFDIndex条件一定成立
//进入这边说明是client socket

try {
//内部执行fork,返回一个待执行Runnable用于处理子进程后续任务
ZygoteConnection connection = peers.get(pollIndex);
final Runnable command = connection.processOneCommand(this);

//fork后,在子进程中会将这个变量设为true
if (mIsForkChild) { //子进程中
if (command == null) {
throw new IllegalStateException("command == null");
}
//return出去,由ZygoteInit执行这个Runnable
return command;
} else { //父进程中
if (command != null) {
throw new IllegalStateException("command != null");
}

//读取完了,关闭这个socket,清理列表
if (connection.isClosedByPeer()) {
connection.closeSocket();
peers.remove(pollIndex);
socketFDs.remove(pollIndex);
}
}
} catch (Exception e) {
...
} finally {
mIsForkChild = false;
}
} else {
... //不开启USAP机制不会走到这个分支
}
}
...
}
...
}
}

创建两个列表,socketFDspeers的下标是一一对应的,首先将server socket fd添加到列表头,方便后续判断事件是来自client或是server socker,peers列表也要添加一个null作为socketFDs的对应

接着就开始执行死循环,Zygote进程永远不会退出这个循环,只有fork出子进程后,子进程会主动return

poll

为了理解后面的内容,我们先要学习一下poll函数

poll是Linux中的字符设备驱动中的一个函数,它的作用是等待文件描述符上的某个事件

原型:int poll(struct pollfd * fds , nfds_t nfds , int timeout );

文档:https://man7.org/linux/man-pages/man2/poll.2.html

第一个参数是一个pollfd结构体指针

1
2
3
4
5
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
  • fd不用多说,就是文件描述符
  • event代表关注哪些事件,POLLIN代表可读,POLLOUT代表可写等等
  • revents是由内核通知的,函数返回的时候,会设置对应的fd实际发生的事件,比如fd有可读的事件,设置POLLIN

第二个参数nfds表示fd的个数,即pollfd数组的size

第三个参数表示超时时间

返回值:

  • 大于0:表示有fd事件产生,值为有产生事件的fd的个数
  • 等于0:表示超时
  • 小于0:表示有错误产生

StructPollfd & pollfd

弄懂poll是干嘛的后,我们再来接着看runSelectLoop方法

死循环中首先创建了一个StructPollfd数组,它根据socketFDs依次创建出一个个StructPollfd对象,并将他们的事件都设为POLLIN可读

StructPollfd和c中的结构体pollfd是对应的,目的是为了方便Java层调用Linux的poll函数

StructPollfd的路径为libcore/luni/src/main/java/android/system/StructPollfd.java

1
2
3
4
5
6
7
public final class StructPollfd {
public FileDescriptor fd;
public short events;
public short revents;
public Object userData;
...
}

调用

然后调用Os.poll方法

关于Os我们上面刚分析过,知道他调用了JNI函数,native函数命名格式为Linux_函数名,我们去libcore/luni/src/main/native/libcore_io_Linux.cpp中找一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static jint Linux_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint timeoutMs) {
... //把Java对象StructPollfd数组转换成c中的struct pollfd数组
int rc;
while (true) {
...
rc = poll(fds.get(), count, timeoutMs);
if (rc >= 0 || errno != EINTR) {
break;
}
...
}
if (rc == -1) {
throwErrnoException(env, "poll");
return -1;
}
... //设置Java对象StructPollfd的revents值
return rc;
}

简单看一下,就是把传进去的StructPollfd数组转换成了struct pollfd数组,然后调用Linux poll函数,再把revents写进StructPollfd对象中,最后返回

再看回runSelectLoop方法,如果poll执行返回值为-1,会直接引发一个Java异常,其他情况先判断一下poll的返回值,如果为0,则没有事件产生,否则会从后向前依次判断pollFDsrevents,如果为POLLIN可读,则处理,不可读则跳过

建立连接

我们先看第一次poll到事件的情况,这时候,pollFDs中只有一个zygote socket fd,收到可读事件,说明有客户端socket向zygote socket请求发起连接,这时候我们调用acceptCommandPeer方法建立新连接

1
2
3
4
5
6
7
8
private ZygoteConnection acceptCommandPeer(String abiList) {
try {
return createNewConnection(mZygoteSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}

调用了mZygoteSocket.accept方法

1
2
3
4
5
6
7
8
public LocalSocket accept() throws IOException
{
LocalSocketImpl acceptedImpl = new LocalSocketImpl();

impl.accept(acceptedImpl);

return LocalSocket.createLocalSocketForAccept(acceptedImpl);
}

新建了一个LocalSocketImpl(client socket) 实例,然后调用LocalSocketImpl(zygote socket) 的accept方法

1
2
3
4
5
6
7
8
9
10
11
12
protected void accept(LocalSocketImpl s) throws IOException {
if (fd == null) {
throw new IOException("socket not created");
}

try {
s.fd = Os.accept(fd, null /* address */);
s.mFdCreatedInternally = true;
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
}

调用了Linux的accept函数,接受建立连接,并返回了一个新的client socket fd,将LocalSocketImpl中的fd变量设置为这个fd,接着调用LocalSocket.createLocalSocketForAcceptLocalSocketImpl包装成LocalSocket

1
2
3
4
5
6
7
8
9
10
11
static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl) {
return createConnectedLocalSocket(impl, SOCKET_UNKNOWN);
}

private static LocalSocket createConnectedLocalSocket(LocalSocketImpl impl, int sockType) {
LocalSocket socket = new LocalSocket(impl, sockType);
socket.isConnected = true;
socket.isBound = true;
socket.implCreated = true;
return socket;
}

然后使用这个LocalSocket创建了一个ZygoteConnection包装socket连接

1
2
3
4
protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
throws IOException {
return new ZygoteConnection(socket, abiList);
}

我们看一下构造方法做了什么

1
2
3
4
5
6
7
8
9
10
11
ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
mSocket = socket;
this.abiList = abiList;

mSocketOutStream = new DataOutputStream(socket.getOutputStream());
mSocketReader =
new BufferedReader(
new InputStreamReader(socket.getInputStream()), Zygote.SOCKET_BUFFER_SIZE);
...
isEof = false;
}

打开了client socket的输入输出流,准备读写数据了

然后将这个连接和fd分别添加进peerssocketFDs

执行client socket命令

在第二次循环中pollFDs数组中便包括了新建立连接的client socket了,这时调用Os.poll,可以获得到这个client socket的可读事件,此时调用connection.processOneCommand方法

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
46
47
48
Runnable processOneCommand(ZygoteServer zygoteServer) {
String[] args;

try {
//读取从client socket传来的参数
args = Zygote.readArgumentList(mSocketReader);
} catch (IOException ex) {
throw new IllegalStateException("IOException on command socket", ex);
}
...
int pid;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;

//解析参数
ZygoteArguments parsedArgs = new ZygoteArguments(args);

... //一系列参数校验工作

//创建子进程
pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);

try {
if (pid == 0) { //子进程中
//设置mIsForkChild = true
zygoteServer.setForkChild();
//子进程中关闭fork复制来的zygote socket
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;

return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
} else { //父进程中
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, serverPipeFd);
return null;
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}

启动APP进程

首先读取从client socket传来的参数,然后校验这些参数,完毕后调用Zygote.forkAndSpecialize方法fork出子进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList,
boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
//停止其他线程
ZygoteHooks.preFork();

//fork进程
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs,
bindMountAppStorageDirs);
...
//恢复其他线程
ZygoteHooks.postForkCommon();
return pid;
}

Zygote进程启动了4个线程:

  • HeapTaskDaemon
  • ReferenceQueueDaemon
  • FinalizerDaemon
  • FinalizerWatchdogDaemon

之前上面也分析过了多线程对fork会产生影响,所以这里先把其他线程停了,等fork完了再重新启动

然后执行native函数nativeForkAndSpecialize,路径为frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring nice_name,
jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
jboolean mount_data_dirs, jboolean mount_storage_dirs) {
...
pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore, true);

if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
is_top_app == JNI_TRUE, pkg_data_info_list,
whitelisted_data_info_list,
mount_data_dirs == JNI_TRUE,
mount_storage_dirs == JNI_TRUE);
}
return pid;
}

先调用ForkCommon,再在子进程调用SpecializeCommon

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
const std::vector<int>& fds_to_close,
const std::vector<int>& fds_to_ignore,
bool is_priority_fork) {
//设置子进程信号处理函数
SetSignalHandlers();
...
//fork前先阻塞SIGCHLD信号
BlockSignal(SIGCHLD, fail_fn);
...
//执行fork
pid_t pid = fork();
...
//恢复SIGCHLD信号
UnblockSignal(SIGCHLD, fail_fn);

return pid;
}
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
46
47
48
49
50
51
52
53
54
55
static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jlong permitted_capabilities, jlong effective_capabilities,
jint mount_external, jstring managed_se_info,
jstring managed_nice_name, bool is_system_server,
bool is_child_zygote, jstring managed_instruction_set,
jstring managed_app_data_dir, bool is_top_app,
jobjectArray pkg_data_info_list,
jobjectArray whitelisted_data_info_list,
bool mount_data_dirs, bool mount_storage_dirs) {
const char* process_name = is_system_server ? "system_server" : "zygote";
...
//创建进程组
if (!is_system_server && getuid() == 0) {
const int rc = createProcessGroup(uid, getpid());
if (rc == -EROFS) {
ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
} else if (rc != 0) {
ALOGE("createProcessGroup(%d, %d) failed: %s", uid, /* pid= */ 0, strerror(-rc));
}
}
//设置GroupId
SetGids(env, gids, is_child_zygote, fail_fn);
//设置资源Limit
SetRLimits(env, rlimits, fail_fn);
...
//设置调度策略
SetSchedulerPolicy(fail_fn, is_top_app);
...
//设置线程名
if (nice_name.has_value()) {
SetThreadName(nice_name.value());
} else if (is_system_server) {
SetThreadName("system_server");
}

//子进程中不再处理SIGCHLD信号
UnsetChldSignalHandler();
...

if (is_child_zygote) {
initUnsolSocketToSystemServer();
}

//调用Zygote.callPostForkChildHooks方法
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
is_system_server, is_child_zygote, managed_instruction_set);

//设置默认进程优先级
setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT);

if (env->ExceptionCheck()) {
fail_fn("Error calling post fork hooks.");
}
}

子进程创建完成后,ZygoteConnection在子进程中会返回handleChildProc,在父进程中会返回null

ZygoteServer中做了判断,如果为子进程且command不为null,返回common到ZygoteInit,如果是父进程,继续socket poll循环

ZygoteInit.runSelectLoop后,如果返回值caller(对应ZygoteServer中的command)不为null,则执行这个Runnable

我们看一下handleChildProc做了什么

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
private Runnable handleChildProc(ZygoteArguments parsedArgs,
FileDescriptor pipeFd, boolean isZygote) {
//在子进程中关闭发起启动App请求的client socket
closeSocket();
//设置进程名
Zygote.setAppProcessName(parsedArgs, TAG);

...
if (parsedArgs.mInvokeWith != null) {
//和进程内存泄露或溢出有关?
WrapperInit.execApplication(parsedArgs.mInvokeWith,
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.mRemainingArgs);

// Should not get here.
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
if (!isZygote) {
//根据参数,执行这个方法
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, null /* classLoader */);
} else {
return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mRemainingArgs, null /* classLoader */);
}
}
}

执行ZygoteInit.zygoteInit

1
2
3
4
5
6
7
8
9
10
11
12
public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
...
//重定向Log
RuntimeInit.redirectLogStreams();
//通用初始化
RuntimeInit.commonInit();
//之前有提到,开启binder线程池
ZygoteInit.nativeZygoteInit();
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);
}

RuntimeInit的路径为frameworks/base/core/java/com/android/internal/os/RuntimeInit.java,先执行通用初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected static final void commonInit() {
//设置默认线程异常处理器
LoggingHandler loggingHandler = new LoggingHandler();
RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));

//设置时区
RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone"));

//重置Log配置
LogManager.getLogManager().reset();
new AndroidConfig();

//设置网络UA信息
String userAgent = getDefaultUserAgent();
System.setProperty("http.agent", userAgent);

//初始化网络流量统计
NetworkManagementSocketTagger.install();
...
initialized = true;
}

然后执行RuntimeInit.applicationInit

1
2
3
4
5
6
7
8
9
10
11
12
13
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
//如果应用程序调用System.exit(),则立即终止该进程,不运行任何hook函数
nativeSetExitWithoutCleanup(true);
//设置虚拟机参数
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
//解析参数
final Arguments args = new Arguments(argv);
...
//查找startClass中的main方法
return findStaticMain(args.startClass, args.startArgs, classLoader);
}

这里的startClass为android.app.ActivityThread

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
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;

try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}

Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}

int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}

/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
return new MethodAndArgsCaller(m, argv);
}

这里使用了Java中的反射,找到了ActivityThread中对应的main方法,并用其创建了一个Runnable对象MethodAndArgsCaller

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
static class MethodAndArgsCaller implements Runnable {
private final Method mMethod;
private final String[] mArgs;

public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}

public void run() {
try {
//执行ActivityThread.main方法
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}

之前说了,在ZygoteInit.runSelectLoop后,如果返回值caller不为null,则执行这个Runnable,即执行MethodAndArgsCallerrun方法,反射调用ActivityThread.main方法

结束

至此,Zygote进程部分的分析就到此为止了,后面fork出App进程那段讲的很粗糙,后面写到App启动那块的时候,我会重新梳理一遍这里的逻辑,补充上去