0%

Binder驱动源码阅读

Binder驱动是Binder IPC的基石,学习了解Binder驱动的实现,有助于深入理解Binder。以下基于Orangepi3lts提供的源代码,分析Binder驱动的实现。

binder设备使用方法

了解Binder的使用方法,对理解驱动的设计思路有帮助。Binder是一种进程间通信的方法,采用主从结构,客户端可以借助Binder驱动给服务端发送消息,或者接收服务端的消息。自然可以得到下图。

但是有一个问题,客户端怎么知道服务端的handle?所以Android引入了一个叫做ServiceManager的进程,这是一个非常特殊的服务端,其handle为0,主要功能是注册和查询Service。所以上图就扩展演变成了下图。由于SM的handle为0,所以服务端可以通过SM注册成为一个Service,客户端可以通过SM查询Service的handle。当得到服务端的handle后,客户端就可以直接与服务端通信了。

通过上面的分析,Binder有以下的使用场景:

  1. 服务端注册
  2. 客服端查询
  3. 客户端向服务端发起通信

当然,还需要知道如何打开Binder驱动。下面以ServiceManager为例,展示了一个进程如何初始化Binder驱动。初始化大致分为以下3步。ServiceManager比较特殊,需要调用命令BINDER_SET_CONTEXT_MGR,整个系统也只有ServiceManager需要调用这个命令。

  1. 打开设备驱动节点
  2. 执行mmap映射内存
  3. 调用ioctl读写数据
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
// 仅保留了主要代码
// frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char** argv)
{
struct binder_state *bs;
char *driver = "/dev/binder";
...
bs = binder_open(driver, 128*1024); // 打开Binder驱动
binder_become_context_manager(bs); // 成为Binder大管家
binder_loop(bs, svcmgr_handler); // 循环处理消息
return 0;
}

// frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
struct binder_state *bs;

bs = malloc(sizeof(*bs));
bs->fd = open(driver, O_RDWR | O_CLOEXEC);

bs->mapsize = mapsize;
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
return bs;
}

int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

代码结构

binder驱动代码在kernel源码的drivers/android目录下,头文件在linux-4.9/include/uapi/linux/android目录下,代码可通过linux-orangepi获取。binder驱动代码主要分为两个文件,binder.c包含misc设备、fops等的实现代码,以及关键数据结构的定义。与内存分配有关的代码和数据结构都定义在binder_alloc.c中。

与binder有关的编译选项主要有两个,编译选项CONFIG_ANDROID_BINDER_IPC决定是否将binder编译进系统;CONFIG_ANDROID_BINDER_DEVICES提供了设备名字列表,用逗号分割,binder会根据这个列表提供的设备名,注册若干个设备。下面是orangepi3lts的编译选项设置。

1
2
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder,vndbinder"

设备驱动初始化

binder.c的最底部,device_initcall()注册了binder的初始化函数binder_init(),下面是初始化函数的主体结构,删除了局部变量声明、错误处理相关的代码。首先在debugfs目录下创建文件夹和文件,然后根据编译选项的配置创建若干个设备。

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 char *binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES;
module_param_named(devices, binder_devices_param, charp, S_IRUGO);

static int __init binder_init(void)
{
//...
// 在debugfs下创建目录 binder/proc,debugfs的挂载目录可以通过mount命令查看
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);
// 在debugfs/binder目录下创建若干个文件
if (binder_debugfs_dir_entry_root) {
//...
}
// 根据 CONFIG_ANDROID_BINDER_DEVICES 提供的设备名,创建若干个设备
device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL);
strcpy(device_names, binder_devices_param);
while ((device_name = strsep(&device_names, ","))) {
ret = init_binder_device(device_name);
}
return ret;
}
device_initcall(binder_init);

设备驱动节点的初始化由函数init_binder_device()完成,参数为设备名。首先为设备申请了一个设备结构体binder_device,然后就是对设备结构体的初始化。

首先初始化成员miscdev,这表明binder是一个misc字符设备,相关的操作函数存储在binder_fops,这是一个非常重要的结构体。然后调用misc_register()向系统注册binder设备。

context表示binder设备的使用环境,主要记录了contex mgr相关的信息。可以从Android Binder文档了解到不同binder设备的用途。

最后,通过hlist将所有的设备结构体链在一起,通过链表头binder_devices可以遍历所有的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
static HLIST_HEAD(binder_devices);
struct binder_device {
struct hlist_node hlist;
struct miscdevice miscdev;
struct binder_context context;
};

static int __init init_binder_device(const char *name)
{
int ret;
struct binder_device *binder_device;
binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);

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);

ret = misc_register(&binder_device->miscdev);
hlist_add_head(&binder_device->hlist, &binder_devices);
return ret;
}

现在来验证一下binder驱动的初始化。在debugfs的挂载目录下可以看到binder创建的目录和文件。这些文件的作用后面再说。/dev目录下也可以看到binder驱动创建的三个设备节点。从Android Binder文档可以了解到每个设备节点的用途。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
console:/ # mount | grep debugfs
debugfs on /sys/kernel/debug type debugfs (rw,seclabel,relatime)
console:/ # ls -l /sys/kernel/debug/binder
total 0
-r--r--r-- 1 root root 0 1970-01-01 08:00 failed_transaction_log
drwxr-xr-x 2 root root 0 2022-03-20 17:39 proc
-r--r--r-- 1 root root 0 1970-01-01 08:00 state
-r--r--r-- 1 root root 0 1970-01-01 08:00 stats
-r--r--r-- 1 root root 0 1970-01-01 08:00 transaction_log
-r--r--r-- 1 root root 0 1970-01-01 08:00 transactions

console:/ # ls /dev/*binder* -l
crw-rw-rw- 1 root root 10, 54 1970-01-01 08:00 /dev/binder
crw-rw-rw- 1 root root 10, 53 1970-01-01 08:00 /dev/hwbinder
crw-rw-rw- 1 root root 10, 52 1970-01-01 08:00 /dev/vndbinder
IPC 域 说明
/dev/binder 框架/应用进程之间的 IPC,使用 AIDL 接口
/dev/hwbinder 框架/供应商进程之间的 IPC,使用 HIDL 接口
供应商进程之间的 IPC,使用 HIDL 接口
/dev/vndbinder 供应商/供应商进程之间的 IPC,使用 AIDL 接口

另外,在/sys/module/binder/parameters目录下,还可以看到binder驱动定义的参数。

1
2
3
4
5
console:/ # ls -l /sys/module/binder/parameters/
total 0
-rw-r--r-- 1 root root 4096 2022-03-20 19:51 debug_mask
-r--r--r-- 1 root root 4096 2022-03-20 19:51 devices
-rw-r--r-- 1 root root 4096 2022-03-20 19:51 stop_on_user_error

open函数

字符设备有一个非常重要的结构体,struct file_operations,以下是binder的定义。在对binder设备节点执行对应的系统调用时,最终会调用到设备驱动提供的函数。使用binder驱动的第一步是调用open()函数,所以需要先分析binder_open()

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_open()主要是围绕结构体binder_proc来做文章,这里记录了一些打开binder的进程的信息,然在debugfs/binder/proc目录下创建一个以pid命名的文件。binder_open()的绝大部分代码都非常好理解,个人觉得以下代码比较关键和难懂。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static int binder_open(struct inode *nodp, struct file *filp)
{
...
// misc 会将 filp->private_data 设置为 strcut miscdevice
// 而这个结构体包含在 struct binder_device中,所以可以通过
// container_of 拿到 binder_dev
binder_dev = container_of(filp->private_data, struct binder_device, miscdev);
proc->context = &binder_dev->context; // 记录当前进程打开的设备节点
binder_alloc_init(&proc->alloc);
...
// ioctl() 或其他fops中的函数,可通过private_data拿到proc
filp->private_data = proc;
// 所有的proc链接到链表binder_procs
mutex_lock(&binder_procs_lock);
hlist_add_head(&proc->proc_node, &binder_procs);
mutex_unlock(&binder_procs_lock);
...
return 0;
}

mmap函数

设备驱动的mmap函数需要协助kernel完成struct vmware_area_struct结构体的设置,mmap函数就非常好理解了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 删除了错误处理
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
// 从 private_data取出proc,这是在open函数设置的
struct binder_proc *proc = filp->private_data;
const char *failure_string;
// 只有进程的主线程才能调用mmap
if (proc->tsk != current->group_leader)
return -EINVAL;
// 最大映射4MB
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;

vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
// 为进程映射虚拟地址空间
ret = binder_alloc_mmap_handler(&proc->alloc, vma);
return ret;
}

binder_alloc_mmap_handler()申请了一块连续的内核虚拟内存,为每个页面申请了struct binder_lru_page

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
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);
// 保留一个连续的内核虚拟区域
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
// 记录内核虚拟地址
alloc->buffer = area->addr;
alloc->user_buffer_offset = vma->vm_start - (uintptr_t)alloc->buffer;
mutex_unlock(&binder_alloc_mmap_lock);
// 为每个页面申请一个结构体 struct binder_lru_page
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;
// binder transactions的缓冲区
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);

buffer->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;
barrier();
alloc->vma = vma;
alloc->vma_vm_mm = vma->vm_mm;
/* Same as mmgrab() in later kernel versions */
atomic_inc(&alloc->vma_vm_mm->mm_count);

return 0;
}

ioctl函数

binder_ioctl()是binder驱动的关键,主要功能都是在这个函数实现。首先从private_data拿到binder_proc,然后获取binder_thread,这个结构体表示执行ioctl的线程。如果是第一次执行,binder_get_thread()会创建相关的结构体并初始化。进程所有的线程以红黑树的形式存储在proc->threads

1
2
3
4
5
6
7
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;
...
thread = binder_get_thread(proc);

然后就是一个大的switch语句,根据不同的cmd执行不同的函数。下表列出了所有cmd、对应的参数,以及功能。其中最关键的命令非BINDER_WRITE_READ莫属。

cmd arg 说明
BINDER_WRITE_READ 指向struct binder_write_read的指针。 使用函数binder_ioctl_write_read()处理。
BINDER_SET_MAX_THREADS 整数。 设置proc->max_threads
BINDER_SET_CONTEXT_MGR None ServiceManager进程的专属命令。
BINDER_THREAD_EXIT None proc->threads中删除线程,释放相关资源
BINDER_VERSION 指向struct binder_version的指针 版本号存储在宏BINDER_CURRENT_PROTOCOL_VERSION
BINDER_GET_NODE_DEBUG_INFO 指向struct binder_node_debug_info的指针

BINDER_SET_CONTEXT_MGR

ServiceManager在执行完mmap()函数后,会调用此命令。这个命令由函数binder_ioctl_set_ctx_mgr()处理。首先检查调用的进程是否符合要求,然后新建一个binder_node,并保存到context->binder_context_mgr_node

binder_context与binder设备有关,保存在设备结构体binder_device中,主要保存了mgr有关的信息。binder_node与Service有关,每个node表示一个service。

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 int binder_ioctl_set_ctx_mgr(struct file *filp)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
// open() 函数会为 proc->context赋值,最终指向 struct binder_device中的context
struct binder_context *context = proc->context;
struct binder_node *new_node;
kuid_t curr_euid = current_euid();

mutex_lock(&context->context_mgr_node_lock);
// 检查context mgr是否已经设置,检查uid / euid是否符号要求
new_node = binder_new_node(proc, NULL); // 每个node表示一个service

binder_node_lock(new_node);
new_node->local_weak_refs++;
new_node->local_strong_refs++;
new_node->has_strong_ref = 1;
new_node->has_weak_ref = 1;
// 表示ServiceManager的node
context->binder_context_mgr_node = new_node;
binder_node_unlock(new_node);
binder_put_node(new_node);
out:
mutex_unlock(&context->context_mgr_node_lock);
return ret;
}

BINDER_WRITE_READ

BINDER_WRITE_READ命令通过函数binder_ioctl_write_read()来处理,前三个参数来自于ioctl()函数的参数,threadbinder_get_thread(proc)的返回值。函数开头,首先将参数复制到结构体struct 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
struct binder_write_read {
binder_size_t write_size; /* 需要写入的字节数 */
binder_size_t write_consumed; /* 驱动消耗的字节数 */
binder_uintptr_t write_buffer; /* 用户空间指针,指向要写入的数据 */
binder_size_t read_size; /* 需要读取的字节数 */
binder_size_t read_consumed; /* 驱动消耗的字节数 */
binder_uintptr_t read_buffer; /* 用户空间指针,读取的数据复制到此buffer */
};

static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
struct binder_proc *proc = filp->private_data;
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;
...
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
...

然后根据bwr.write_sizebwr.read_size的值决定是否调用binder_thread_write()binder_thread_read()做进一步处理。最后将修改后的bwr结构体复制到用户空间,因为driver会更改bwr.write_consumedbwr.read_consumed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
	if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread,
bwr.write_buffer, bwr.write_size,
&bwr.write_consumed);
...
}
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);
if (!binder_worklist_empty_ilocked(&proc->todo))
binder_wakeup_proc_ilocked(proc);
...
}
...
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
...
}

binder_thread_write()

函数binder_thread_write()非常长,但好在结构比较清晰。指针ptr是一个用户空间指针,始终指向待处理的字节。不断的从ptr取出cmd,然后根据cmd进行不同的处理,最后更新consumed,直到buffer所有的数据处理完毕。也就是说,bwr.write_buffer由若干个cmd和紧随其后的可选固定长度参数构成,参数的长度根据cmd的不同而不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
...
switch (cmd) {
...
*consumed = ptr - buffer;
}
}
return 0;

下表列出了binder_thread_write支持的所有cmd。后面结合具体的使用场景,再来分析这些命令的作用。

binder command protocol param 说明
BC_INCREFS
BC_ACQUIRE
BC_RELEASE
BC_DECREFS
无符号32位整数,target 增加或减少引用计数
BC_INCREFS_DONE
BC_ACQUIRE_DONE
两个指针参数,依次是node_ptr和cookie
BC_ATTEMPT_ACQUIRE
BC_ACQUIRE_RESULT
None 这两个命令不支持
BC_FREE_BUFFER 一个指针参数,data_ptr
BC_TRANSACTION_SG
BC_REPLY_SG
有一个struct binder_transaction_data_sg参数
BC_TRANSACTION
BC_REPLY
有一个struct binder_transaction_data参数
BC_REGISTER_LOOPER None
BC_ENTER_LOOPER None
BC_EXIT_LOOPER None
BC_REQUEST_DEATH_NOTIFICATION
BC_CLEAR_DEATH_NOTIFICATION
一个无符号32位整数,target;
一个指针参数,cookie
BC_DEAD_BINDER_DONE 一个指针参数,cookie

TODO:详细理解每一个cmd的作用

binder_thread_read()

binder_thread_read()函数比较长,不是很好理解。主要工作流程如下:

  1. 进入阻塞状态,等待binder_work
  2. 如果队列不为空,取出一个binder_work
  3. 根据work的不同类型,进行不同的处理
  4. 给读取的线程回复一个BR命令,某些命令还会带一个参数。

下面来分析实现代码。首先,设置线程的等待标志位。Binder同时支持阻塞和非阻塞模式,假设是阻塞模式,则调用binder_wait_for_work()等待一个binder_work的到来。binder_wait_for_work()基于Linux内核的等待队列实现,等待队列是thread->wait。如果当前线程已经有binder_work,则不会阻塞,直接返回。binder_work是写入线程发来的。

1
2
3
4
5
6
7
8
thread->looper |= BINDER_LOOPER_STATE_WAITING;
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);
}
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;

然后进入一个死循环,根据work的不同类型进行不同的处理,具体参考下方代码块的注释。处理完毕后给读取线程的缓冲区写入一个BR命令,部分BR命令还有一个参数。BINDER_WORK_TRANSACTION的处理过于复杂,所以放在了switch语句的后面处理。暂时先不具体分析每个work 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
while (1) {
//...
w = binder_dequeue_work_head_ilocked(list);
switch (w->type) {
case BINDER_WORK_TRANSACTION: { // 处理`binder_transaction`结构体
t = container_of(w, struct binder_transaction, work);
} break;
case BINDER_WORK_RETURN_ERROR: { // 直接在读取线程的缓冲区写入`e->cmd`
struct binder_error *e = container_of( w, struct binder_error, work);
// ...
} break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
// 直接在读取线程的缓冲区写入命令`BR_TRANSACTION_COMPLETE`
cmd = BR_TRANSACTION_COMPLETE;
put_user(cmd, (uint32_t __user *)ptr):
ptr += sizeof(uint32_t);
// ...
} break;
case BINDER_WORK_NODE: { // 处理`binder_node`结构体
struct binder_node *node = container_of(w, struct binder_node, work);
// ...
} break;
case BINDER_WORK_DEAD_BINDER:
case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: { // 处理`binder_ref_death`结构体
struct binder_ref_death *death;
death = container_of(w, struct binder_ref_death, work);
// ...
} break;
}
if (!t) continue;
// 处理binder_transaction结构体
break;
}

BR命令的全程是binder driver return protocol,由binder driver发送给service进程。下表列出了所有的BR命令及其对应的参数。

binder return protocol param 说明
BR_OK None
BR_TRANSACTION
BR_REPLY
struct binder_transaction_data the received command.
BR_DEAD_REPLY None The target of the last transaction (either a bcTRANSACTION or a bcATTEMPT_ACQUIRE) is no longer with us.
BR_INCREFS
BR_ACQUIRE
BR_RELEASE
BR_DECREFS
struct binder_ptr_cookie ptr to binder, cookie for binder
BR_NOOP None 什么都不做,检查下一个命令。 它的存在主要是可以用 BR_SPAWN_LOOPER 命令替换它。
BR_SPAWN_LOOPER None 驱动程序已确定进程没有线程等待为传入事务提供服务。 当一个进程接收到这个命令时,它必须产生一个新的服务线程并通过 bcENTER_LOOPER 注册它。
BR_DEAD_BINDER binder_uintptr_t, cookie
BR_CLEAR_DEATH_NOTIFICATION_DONE binder_uintptr_t, cookie
BR_FAILED_REPLY None 最后一个事务(bcTRANSACTION 或 bcATTEMPT_ACQUIRE)失败(例如内存不足)。

Binder协议

之前的分析,更多是语法层面的分析,还没有深入理解分析Binder的数据结构。所以,接下来根据具体的通信实例,来深入理解Binder协议,理解BC、BR命令及其参数的含义。

注册Service

binder设备使用方法提到过,进程或线程可以向ServiceManager注册成为Service。所以现在来看看注册为Service需要发送什么样的数据。下面的代码片段来自MediaPlayerService.cppdefaultServiceManager()会获取一个handle为0的BpBinder对象,并以此创建出一个BpServiceManager对象,BpBinder对象保存在mRemote

1
2
3
4
5
// frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
void MediaPlayerService::instantiate() {
defaultServiceManager()->addService(
String16("media.player"), new MediaPlayerService());
}

下面是BpServiceManager::addService()的实现。首先使用Parcel打包数据。Parcel是一种数据容器,提供了非常多的接口来打包各种类型的数据,打包和解包需要使用对应的接口即可。然后调用remote()->transact()发送并接收返回的消息。remote()返回mRemote,而mRemote是handle等于0的BpBinder对象,所以remote()->transact()实际上是将消息发送给ServiceManager。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// frameworks/native/libs/binder/IServiceManager.cpp
class BpServiceManager : public BpInterface<IServiceManager> {
virtual status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated, int dumpsysPriority) {
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service); // 对象类型是BINDER_TYPE_HANDLE
data.writeInt32(allowIsolated ? 1 : 0); // allowIsolated 默认等于 false
data.writeInt32(dumpsysPriority); // dumpsysPriority 默认等于 DUMP_FLAG_PRIORITY_DEFAULT
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
}

BpBinder::transact()实际上会借助IPCThreadState::transact()来完成实际的发送动作,IPCThreadState对象每个线程有且只有一个。下面是函数的具体实现,handle=0code=ADD_SERVICE_TRANSACTIONdataBpServiceManager::addService()函数中写入了一些对象,flags是默认值0,reply是ServiceManager返回的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// frameworks/native/libs/binder/IPCThreadState.cpp, flags 默认为0
// 仅保留主要代码,删除了log和错误处理
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err;
flags |= TF_ACCEPT_FDS;
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
} else {
err = waitForResponse(NULL, NULL);
}
return err;
}

writeTransactionData()负责将待发送数据的相关信息组合成binder_transaction_data结构,然后将cmdbinder_transaction_data结构写入到mOut对象,这也是一个Parcel对象。如下代码所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
binder_transaction_data tr;
tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
tr.target.handle = handle; // 等于0,代表ServiceManager
tr.code = code; // ADD_SERVICE_TRANSACTION
tr.flags = binderFlags;
tr.cookie = 0;
tr.sender_pid = 0;
tr.sender_euid = 0;
tr.data_size = data.ipcDataSize(); // 待发送数据的大小
tr.data.ptr.buffer = data.ipcData(); // 指向待发送数据的首地址
// 待发送数据中对象的数量
tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
tr.data.ptr.offsets = data.ipcObjects(); // 对象相对tr.data.ptr.buffer的偏移

mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));

return NO_ERROR;
}

IPCThreadState::waitForResponse()主要完成以下两个工作:

  1. 调用talkWithDriver(),将mOut发送到driver,然后等待driver回复的信息并写入到mIn
  2. 根据mIn中的数据,进行不同的处理

由于现在还不知道driver会返回什么数据,所以这个函数先不分析。先来看看talkWithDriver()。首先判断mProcess对象的文件描述符是否大于0,然后根据mInmOut来构建binder_write_read结构体,最后调用ioctl的BINDER_WRITE_READ命令将数据发送到driver。不妨回头看看前面对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
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
// ...
binder_write_read bwr;
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
// We don't want to write anything if we are still reading
// from data left in the input buffer and the caller
// has requested to read the next data.
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
// This is what we'll read.
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
// Return immediately if there is nothing to do.
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
} while (err == -EINTR);
// 一些错误处理,略
return err;
}

下图展示了从addService()talkWithDriver()是如何对数据封装的,箭头表示指针指向对应的数据结构。前面提到过,不同的BC指令会带有不同的参数,BC_TRANSACTION的参数是一个binder_transaction_data结构体,这个结构体表明了要发送的数据、目标以及如何处理数据。

driver使用函数binder_transaction()来处理BC_TRANSACTION,这个函数非常的长(600行左右),并且和BC_REPLAY共用一个函数,所以分析起来有点困难。当replay=0是,函数的主要流程如下:

  1. 根据target.handle找到target_node / target_proc
  2. 构建结构体 binder_transaction
  3. 依次取出tr->data.buffer中的binder对象,并根据不同的对象类型进行不同的处理,并处理结果附加到binder_transaction
  4. t->work.type = BINDER_WORK_TRANSACTION
  5. 调用binder_proc_transaction(),将t->work附加到target_proc->todo队列,然后唤醒target_proc。

addService()写入了一个类型为BINDER_TYPE_HANDLE的binder对象,其存储了即将注册的service的handle值。binder_translate_handle()的工作是获取注册service的binder_node,并增加引用。现在,这里的target_procservice manager,唤醒目标进程后,binder_thread_read()会继续运行。注意这里的work type是BINDER_WORK_TRANSACTION

TODO:binder_node是什么时候在driver注册的

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
static void binder_transaction(
struct binder_proc *proc,
struct binder_thread *thread, // 想注册成为Service的线程
struct binder_transaction_data *tr, // 在函数writeTransactionData()构建的结构体
int reply, // 0, replay = cmd == BC_REPLY
binder_size_t extra_buffers_size) // 0
{
if (reply) { // 处理BC_REPLAY
} else { // 处理BC_TRANSACTION
// 找到 target_node / target_proc
if (tr->target.handle) { // 目标进程不是ServiceManager
} else {
target_node = context->binder_context_mgr_node;
target_node = binder_get_node_refs_for_txn(target_node, &target_proc, &return_error);
}
}
// 构建 binder_transaction
t->code = tr->code; // ADD_SERVICE_TRANSACTION
t->buffer->target_node = target_node;
for (; offp < off_end; offp++) {
struct binder_object_header *hdr;
hdr = (struct binder_object_header *)(t->buffer->data + *offp);
switch (hdr->type) {
// 根据不同的类型进行不同的处理
case BINDER_TYPE_HANDLE: {
struct flat_binder_object *fp;
fp = to_flat_binder_object(hdr);
ret = binder_translate_handle(fp, t, thread);
}
}
}
t->work.type = BINDER_WORK_TRANSACTION;
if (reply) { // 处理BC_REPLAY
} else if (!(t->flags & TF_ONE_WAY)) {
binder_proc_transaction(t, target_proc, target_thread);
} else {
binder_proc_transaction(t, target_proc, NULL);
}
}

从前面binder_thread_read()的分析看,BINDER_WORK_TRANSACTION的处理在binder_thread_read()函数的最后面,其主要工作是根据binder_transaction结构体构造binder_transaction_data结构体,然后写回到用户空间的buffer。注意这里的命令是BR_TRANSACTIONtr.codeADD_SERVICE_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
while (1) {
if (t->buffer->target_node) {
struct binder_node *target_node = t->buffer->target_node;
tr.target.ptr = target_node->ptr;
tr.cookie = target_node->cookie;
cmd = BR_TRANSACTION;
} else {
}
tr.code = t->code; // ADD_SERVICE_TRANSACTION
tr.flags = t->flags;

tr.data_size = t->buffer->data_size;
tr.offsets_size = t->buffer->offsets_size;
tr.data.ptr.buffer = (binder_uintptr_t)
((uintptr_t)t->buffer->data +
binder_alloc_get_user_buffer_offset(&proc->alloc));
tr.data.ptr.offsets = tr.data.ptr.buffer +
ALIGN(t->buffer->data_size, sizeof(void *));
put_user(cmd, (uint32_t __user *)ptr);
ptr += sizeof(uint32_t);
copy_to_user(ptr, &tr, sizeof(tr));
ptr += sizeof(tr);
//...
break;
}

最后看service manager对消息的处理,历经千难万险,SM终于拿到了消息,并完成了添加service的动作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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_ADD_SERVICE:
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
handle = bio_get_ref(msg);
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
dumpsys_priority = bio_get_uint32(msg);
if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
txn->sender_pid))
return -1;
break;
}
}

获取service

TODO

参考资料