Android Binder 机制之 ServiceManager 模块
ServiceManager 启动源码分析
以 Android 9.0 代码为例介绍
Init 拉起 ServiceManager 进程
init 进程通过 init.rc 脚本拉起 Native 层的 ServiceManager 进程
- init.rc
// system/core/rootdir/init.rc
on late-init
...
trigger post-fs # late_init 事件触发 post_fs 事件
...
on post-fs
load_system_props
start logd
start servicemanager # post-fs 事件中启动三个服务管理进程
start hwservicemanager
start vndservicemanager
- init.cpp
// system/core/init/init.cpp:740
int main(int argc, char** argv) {
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init"); // 当前的 bootmode 不是 charger 时,将触发 late-init 事件
}
...
am.ExecuteOneCommand(); // 执行该事件里设定好的命令,即拉起 servicemanager 进程
}
ServiceManager 启动
ServiceManager 启动后完成两件事
- 打开 binder 驱动
- 将自己注册到 binder 驱动中,设置自己为守护进程,等待服务端和客户端的调用
然后 ServiceManager 进程陷入循环,等待请求
// frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char** argv){
...
driver = "/dev/binder";
bs = binder_open(driver, 128*1024); // 打开 binder 驱动
...
if (binder_become_context_manager(bs)) { // 将当前进程设置为守护进程
...
}
...
binder_loop(bs, svcmgr_handler); // 在 svcmgr_handler() 函数中循环等待 client 发过来的请求
...
}
1. ServiceManager 打开 binder 驱动
在 binder_open(),完成两件事
- 获取 binder 驱动的句柄文件
- 和 binder 内核完成共享内存步骤
binder_open() 打开驱动
system_manager 会在 main.cpp 中,调用 open 系统调用
// frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(const char* driver, size_t mapsize){
struct binder_state *bs; // 记录 binder 的结构体
...
bs->fd = open(driver, O_RDWR | O_CLOEXEC); // 打开文件节点的文件描述符
...
// 写 BINDER_VERSION 到 Binder 内核
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
fprintf(stderr,
"binder: kernel driver version (%d) differs from user space version (%d)\n",
vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
goto fail_open;
}
...
bs->mapsize = mapsize; // 分配的内存映射的空间大小
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); // 分配的内存的空间地址
}
binder_open() 内核处理
上层通过系统调用接口 open()
调用到 binder 内核中的 binder_open()
函数,调用过程如下:
- 应用进程调用
open()
系统调用,请求并打开指定文件 - 系统调用转为内核中的
do_sys_open()
函数调用 - 该函数在
VFS(Virtual File System)
层查找并打开指定的文件设备,并创建一个新的 file 结构体实例 - VFS 设置 file 结构体的各个字段,包含与设备文件相关联的操作函数(read/write/ioctl)
do_sys_open()
函数返回一个文件描述符,该文件描述符与新创建的 file 结构体关联- 如果请求的 Binder 设备文件,VFS 会调用
binder_open
函数,并将 file 结构体作为参数传递过去。
Binder 内核在 binder_open()
函数中做了 4 件事,分别是
- 创建
binder_proc
结构体,存储于 Binder 相关的进程信息,每个使用 Binder 的进程都有一份独立的binder_proc
实例 - 初始化数据结构,包括初始化 todo 队列和等待队列,这些队列用于管理 Binder 事务和进程间通信
- 记录
binder_proc,将创建的
binder_proc
添加到内核的binder_procs
列表中,这样系统就可以在需要时访问和管理所有的 Binder 进程 - 分配文件描述符,
binder_open()
会为应用进程分配一个文件描述符,以便应用进程可以通过该描述符与 Binder 驱动进行通信
// device/renesas/kernel/drivers/android/binder.c
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
...
proc = kzalloc(sizeof(*proc), GFP_KERNEL); // 初始化 binder_proc
...
INIT_LIST_HEAD(&proc->todo); // 初始化 todo 队列
...
INIT_LIST_HEAD(&proc->waiting_threads); // 初始化 等待队列
...
filp->private_data = proc; // 将 binder_proc 记录到 file 实例中
....
hlist_add_head(&proc->proc_node, &binder_procs); // 将 binder_proc 记录到 binder_procs 列表中
...
}
binder_mmap() 内核的内存映射处理
mmap 系统调用最终会到 binder_mmap 函数中
binder_mmap 完成以下几个工作:
- 内存映射:binder_mmap 会在内核虚拟地址空间中申请一块与用户虚拟地址内存相同大小的内存
- 物理分配内存:它会申请一块页面大小的物理内存
- 映射同步:将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,实现用户空间的 Buffer 和内核空间的 Buffer 同步操作
// device/renesas/kernel/drivers/android/binder.c
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
...
// 获取对应进程的信息
struct binder_proc *proc = filp->private_data;
...
// 设置了一块内存的操作接口,打开关闭和缺页等
// 并且将该块内存和指定进程绑定
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
...
ret = binder_alloc_mmap_handler(&proc->alloc, vma);
...
}
// device/renesas/kernel/drivers/android/binder_alloc.c
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
struct vm_area_struct *vma)
{
...
// 在内核虚拟地址空间中申请一块与用户虚拟内存大小相同的内存
// 将该内存与当前进程绑定
alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
((vma->vm_end - vma->vm_start) / PAGE_SIZE),
GFP_KERNEL);
alloc->buffer_size = vma->vm_end - vma->vm_start; // 记录虚拟内存空间大小
// 申请一块页面大小的物理内存
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
buffer->user_data = alloc->buffer; // 物理地址地址和用户虚拟地址和内核虚拟地址绑定
list_add(&buffer->entry, &alloc->buffers);
buffer->free = 1;
binder_insert_free_buffer(alloc, buffer);
alloc->free_async_space = alloc->buffer_size / 2;
binder_alloc_set_vma(alloc, vma);
mmgrab(alloc->vma_vm_mm);
...
}
需要注意的是:以上代码是早期版本内核的 mmap 处理步骤,等到 4.19 版本之后,内存不再申请一块内存地址了,而 binder_mmap 也不再做映射了,等到进程通信数据传递的时候,才会完成用户空间虚拟地址到物理地址的映射,然后再把数据拷贝到这块物理内存中完成数据传递。
架构图
如上,ServiceManager 的 binder 驱动已打开,并且已经做好 ServiceManager 和 Binder 内核的共享内存申请
架构图如下:
// todo
2. ServiceManager 注册自己为守护进程
在 Native 层
// frameworks/native/cmds/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs)
{
// 通过 ioctl 将 binder 描述符传入,并且通过指令 BINDER_SET_CONTEXT_MGR 设置为守护进程
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
在 Binder 内核中完成三件事
- 在 Binder 驱动层创建 binder_node 结构体对象
- 将当前的 binder_proc 加入到 binder_node 的 node->proc
- 创建 binder_node 的 async_todo 和 binder_work 两个队列
// device/renesas/kernel/drivers/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
...
case BINDER_SET_CONTEXT_MGR:
ret = binder_ioctl_set_ctx_mgr(filp, NULL);
if (ret)
goto err;
break;
...
}
}
static int binder_ioctl_set_ctx_mgr(struct file *filp,
struct flat_binder_object *fbo)
{
...
// 确保守护进程只注册一次
if (context->binder_context_mgr_node) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto out;
}
...
if (uid_valid(context->binder_context_mgr_uid)) {
// 检查 uid 有效
} else {
context->binder_context_mgr_uid = curr_euid; // 无效的话将当前线程 euid 设置为 service_manager
}
...
new_node = binder_new_node(proc, fbo); // 创建 service_manager 实体
...
new_node->local_weak_refs++; // 强弱引用 +1
new_node->local_strong_refs++;
...
}
static struct binder_node *binder_new_node(struct binder_proc *proc,
struct flat_binder_object *fp) {
// 分配内核空间
struct binder_node *new_node = kzalloc(sizeof(*node), GFP_KERNEL);
node = binder_init_node_ilocked(proc, new_node, fp); // 处理 binder_node
...
}
static struct binder_node *binder_init_node_ilocked(
struct binder_proc *proc,
struct binder_node *new_node,
struct flat_binder_object *fp)
{
...
node = new_node;
binder_stats_created(BINDER_STAT_NODE);
node->tmp_refs++;
rb_link_node(&node->rb_node, parent, p); // 将节点放入到红黑数树中
rb_insert_color(&node->rb_node, &proc->nodes);
node->debug_id = atomic_inc_return(&binder_last_id);
node->proc = proc; // 初始化 binder_node
node->ptr = ptr;
node->cookie = cookie;
node->work.type = BINDER_WORK_NODE; // 设置 binder_work 的type
...
}
3. ServiceManager 进入循环等待
在 native 层,ServiceManager 发送 looper 指令告诉内核
// 发送 BC_ENTER_LOOPER 指令到 Binder 内核
// frameworks/native/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func)
{
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
uint32_t readbuf[32];
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(uint32_t)); // 写一次 ioctl
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
// 有消息处理,没消息中断等待消息
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); // 再写,只读不写
...
// 消息处理,func
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
}
}
// 将 BC_ENTER_LOOPER 写入到 binder 内核中
int binder_write(struct binder_state *bs, void *data, size_t len)
{
struct binder_write_read bwr;
int res;
bwr.write_size = len; // 只写
bwr.write_consumed = 0;
bwr.write_buffer = (uintptr_t) data;
bwr.read_size = 0; // 不读
bwr.read_consumed = 0;
bwr.read_buffer = 0;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
fprintf(stderr,"binder_write: ioctl failed (%s)\n",
strerror(errno));
}
return res;
}
// frameworks/native/cmds/servicemanager/service_manager.c
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
...
// 根据传入的请求类型,做处理
// 获取/检查服务,添加服务,列出服务列表
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
...
case SVC_MGR_ADD_SERVICE:
...
case SVC_MGR_LIST_SERVICES:
...
}
}
binder 内核收到 BINDER_WRITE_READ 请求处理
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;
}
...
}
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
...
// 将传入的参数,从用户空间拷贝到内核空间
void __user *ubuf = (void __user *)arg;
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
...
// 有写入的数据,即用户进程向内核进程写数据,对应 binder_loop() 的第一次 ioctl
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) {
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
// 如果有需要读取的数据,即内核向用户写数据,对应 binder_loop() 的 for 循环中的 ioctl
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);
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;
}
}
...
}
// 第一次 ioctl 写 BC_ENTER_LOOPER 处理逻辑
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 (ptr < end && thread->return_error.cmd == BR_OK) {
int ret;
// 从 ptr 中获取数据,即 BC_ENTER_LOOPER
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
...
switch (cmd) {
...
// 将指定线程设置为 loop 状态,该线程是从 device 文件中获取。
case BC_ENTER_LOOPER:
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BC_ENTER_LOOPER\n",
proc->pid, thread->pid);
if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
thread->looper |= BINDER_LOOPER_STATE_INVALID;
binder_user_error("%d:%d ERROR: BC_ENTER_LOOPER called after BC_REGISTER_LOOPER\n",
proc->pid, thread->pid);
}
thread->looper |= BINDER_LOOPER_STATE_ENTERED; // 设置该线程的状态为 BINDER_LOOPER_STATE_ENTERED
break;
...
}
}
...
}
// for 循环中只读不写的处理
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)
{
...
// for 循环中的 read_consumed 值,是 0
if (*consumed == 0) {
if (put_user(BR_NOOP, (uint32_t __user *)ptr)) // 将 BR_NOOP 设置到 ptr 中
return -EFAULT;
ptr += sizeof(uint32_t);
}
...
// binder 线程是否能工作,此时肯定是 true
binder_inner_proc_lock(proc);
wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
binder_inner_proc_unlock(proc);
...
// filp->f_flags 是 O_RDWR | O_CLOEXEC( 02000000 | 02)
// non_block = filp->f_flags & O_NONBLOCK(00004000 & 02000002 = 0)
if (non_block) {
if (!binder_has_work(thread, wait_for_proc_work))
ret = -EAGAIN;
} else {
ret = binder_wait_for_work(thread, wait_for_proc_work); // 将传入的线程阻塞在此处
}
}
static int binder_wait_for_work(struct binder_thread *thread,
bool do_proc_work)
{
...
for (;;) {
prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE);
if (binder_has_work_ilocked(thread, do_proc_work))
break;
/*
* 调度函数,从运行队列的链表中找到一个进程,随后 CPU 分配给这个进程。
* 如果当前进程因不能获得必须的资源而要被阻塞时,需要使用调度函数
* 1. 先将当前进程插入到适当的等待队列
* 2. 将当前进程状态修改为 TASK_INTERRUPTIBLE 或者 TASK_UNINTERRUPTIBLE
* 3. 调用 schedule()
* 4. 检查资源是否可能,如果不可用转到步骤 2
* 5. 如果资源可用就从等待队列中删除当前进程
*/
schedule();
...
}
...
}
至此,ServiceManager 启动,到打开 Binder 内核,注册自己为守护进程,然后自己进入 loop 循环,而 Binder 内核也进入中断等待请求到来的逻辑分析完毕
疑问:
- binder 内核处进程阻塞后, BR_NOOP 是什么时候被通知到 ServiceManager 中的