0%

Android FrameWork - Binder 驱动解析(一)

Binder驱动概述

概述

Binder驱动是Android专用的,但底层的驱动架构与Linux驱动一样。binder驱动在以misc设备进行注册,作为虚拟字符设备没有直接操作硬件,只是对设备内存的处理。主要是驱动设备的**初始化(binder_init)打开 (binder_open)映射(binder_mmap)数据操作(binder_ioctl)**。

avatar

系统调用

用户态的程序调用Kernel层驱动是需要陷入内核态,**进行系统调用(syscall),比如打开Binder驱动方法的调用链为: open-> __open() -> binder_open()**。 open()为用户空间的方法,__open()便是系统调用中相应的处理方法,通过查找,对应调用到内核binder驱动的binder_open()方法,至于其他的从用户态陷入内核态的流程也基本一致

avatar

Binder核心方法

binder_init

主要工作是为了注册misc设备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static int __init binder_init(void)
{
int ret;
//创建名为binder的工作队列
binder_deferred_workqueue = create_singlethread_workqueue("binder");
...

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

// 注册misc设备
ret = misc_register(&binder_miscdev);
if (binder_debugfs_dir_entry_root) {
... //在debugfs文件系统中创建一系列的文件
}
return ret;
}
misc_register

注册misc设备,miscdevice结构体,便是前面注册misc设备时传递进去的参数

1
2
3
4
5
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR, //次设备号 动态分配
.name = "binder", //设备名
.fops = &binder_fops //设备的文件操作结构,这是file_operations结构
};

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
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc; // binder进程 见附录binder_proc
proc = kzalloc(sizeof(*proc), GFP_KERNEL); // 为binder_proc结构体在分配kernel内存空间
if (proc == NULL)
return -ENOMEM;
get_task_struct(current);
proc->tsk = current; //将当前线程的task保存到binder进程的tsk
INIT_LIST_HEAD(&proc->todo); //初始化todo列表
init_waitqueue_head(&proc->wait); //初始化wait队列
proc->default_priority = task_nice(current); //将当前进程的nice值转换为进程优先级

binder_lock(__func__); //同步锁,因为binder支持多线程访问
binder_stats_created(BINDER_STAT_PROC); //BINDER_PROC对象创建数加1
hlist_add_head(&proc->proc_node, &binder_procs); //将proc_node节点添加到binder_procs为表头的队列
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death); //初始化已分发的死亡通知列表
filp->private_data = proc; //file文件指针的private_data变量指向binder_proc数据
binder_unlock(__func__); //释放同步锁

return 0;
}

创建binder_proc对象,并把当前进程等信息保存到binder_proc对象,该对象管理IPC所需的各种信息并拥有其他结构体的根结构体;再把binder_proc对象保存到文件指针filp,以及把binder_proc加入到全局链表binder_procs

avatar

Binder驱动中通过static HLIST_HEAD(binder_procs);,创建了全局的哈希链表binder_procs,用于保存所有的binder_proc队列,每次新创建的binder_proc对象都会加入binder_procs链表中。

binder_mmap

主要功能:首先在内核虚拟地址空间申请一块与用户虚拟内存相同大小的内存;然后再申请1个page大小的物理内存再将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,从而实现了用户空间的Buffer和内核空间的Buffer同步操作的功能。

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_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
struct vm_struct *area; //内核虚拟空间
struct binder_proc *proc = filp->private_data;
const char *failure_string;
struct binder_buffer *buffer; //见附录binder_buffer

if (proc->tsk != current)
return -EINVAL;

if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M; //保证映射内存大小不超过4M

mutex_lock(&binder_mmap_lock); //同步锁
//采用IOREMAP方式,分配一个连续的内核虚拟空间,与进程虚拟空间大小一致
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
proc->buffer = area->addr; //指向内核虚拟空间的地址
//地址偏移量 = 用户虚拟地址空间 - 内核虚拟地址空间
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
mutex_unlock(&binder_mmap_lock); //释放锁

...
//分配物理页的指针数组,数组大小为vma的等效page个数;
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
if (proc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
proc->buffer_size = vma->vm_end - vma->vm_start;

vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;

//分配物理页面,同时映射到内核空间和进程空间,先分配1个物理页 【见小节2.3.1】
if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
ret = -ENOMEM;
failure_string = "alloc small buf";
goto err_alloc_small_buf_failed;
}
buffer = proc->buffer; //binder_buffer对象 指向proc的buffer地址
INIT_LIST_HEAD(&proc->buffers); //创建进程的buffers链表头
list_add(&buffer->entry, &proc->buffers); //将binder_buffer地址 加入到所属进程的buffers队列
buffer->free = 1;
//将空闲buffer放入proc->free_buffers中
binder_insert_free_buffer(proc, buffer);
//异步可用空间大小为buffer总大小的一半。
proc->free_async_space = proc->buffer_size / 2;
barrier();
proc->files = get_files_struct(current);
proc->vma = vma;
proc->vma_vm_mm = vma->vm_mm;
return 0;

...// 错误flags跳转处,free释放内存之类的操作
return ret;
}

binder_mmap通过加锁,保证一次只有一个进程分配内存,保证多进程间的并发访问。其中user_buffer_offset是虚拟进程地址与虚拟内核地址的差值(该值为负数)。也就是说同一物理地址,当内核地址为kernel_addr,则用户虚拟地址为proc_addr = kernel_addr + user_buffer_offset。

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
static int binder_update_page_range(struct binder_proc *proc, int allocate,
void *start, void *end,
struct vm_area_struct *vma)
{
void *page_addr;
unsigned long user_page_addr;
struct page **page;
struct mm_struct *mm; // 内存结构体

if (vma)
mm = NULL; //binder_mmap过程vma不为空,其他情况都为空
else
mm = get_task_mm(proc->tsk); //获取mm结构体

if (mm) {
down_write(&mm->mmap_sem); //获取mm_struct的写信号量
vma = proc->vma;
}

//此处allocate为1,代表分配过程。如果为0则代表释放过程
if (allocate == 0)
goto free_range;

for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
int ret;
page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
//分配一个page的物理内存
*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);

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

user_page_addr = (uintptr_t)page_addr + proc->user_buffer_offset;
//物理空间映射到虚拟用户空间
ret = vm_insert_page(vma, user_page_addr, page[0]);
}

if (mm) {
up_write(&mm->mmap_sem); //释放内存的写信号量
mmput(mm); //减少mm->mm_users计数
}
return 0;

free_range:
... //释放内存的流程

return -ENOMEM;
}

主要工作如下:

avatar

binder_update_page_range主要完成工作:分配物理空间,将物理空间映射到内核空间,将物理空间映射到进程空间. 另外,不同参数下该方法也可以释放物理页面。

binder_ioctl

binder_ioctl()函数负责在两个进程间收发IPC数据和IPC reply数据

  1. 文件描述符,是通过open()方法打开Binder Driver后返回值

  2. ioctl命令和数据类型是一体的,不同的命令对应不同的数据类型

ioctl命令 数据类型 操作
BINDER_WRITE_READ struct binder_write_read 收发Binder IPC数据
BINDER_SET_MAX_THREADS __u32 设置Binder线程最大个数
BINDER_SET_CONTEXT_MGR __s32 设置Service Manager节点
BINDER_THREAD_EXIT __s32 释放Binder线程
BINDER_VERSION struct binder_version 获取Binder版本信息
BINDER_SET_IDLE_TIMEOUT __s64 没有使用
BINDER_SET_IDLE_PRIORITY __s32 没有使用

这些命令中BINDER_WRITE_READ命令使用率最为频繁,也是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
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
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; // binder线程
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;

binder_lock(__func__);
//获取binder_thread
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}

switch (cmd) {
case BINDER_WRITE_READ: //进行binder的读写操作
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS: //设置binder最大支持的线程数
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR: //成为binder的上下文管理者,也就是ServiceManager成为守护进程
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
break;
case BINDER_THREAD_EXIT: //当binder线程退出,释放binder线程
binder_free_thread(proc, thread);
thread = NULL;
break;
case BINDER_VERSION: { //获取binder的版本号
struct binder_version __user *ver = ubuf;

if (size != sizeof(struct binder_version)) {
ret = -EINVAL;
goto err;
}
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}
default:
ret = -EINVAL;
goto err;
}
ret = 0;
err:
if (thread)
thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
binder_unlock(__func__);
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);

err_unlocked:
trace_binder_ioctl_done(ret);
return ret;
}
binder_get_thread

从binder_proc中查找binder_thread,如果当前线程已经加入到proc的线程队列则直接返回,如果不存在则创建binder_thread,并将当前线程添加到当前的proc

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
static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{
struct binder_thread *thread = NULL;
struct rb_node *parent = NULL;
struct rb_node **p = &proc->threads.rb_node;
while (*p) { //根据当前进程的pid,从binder_proc中查找相应的binder_thread
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
break;
}
if (*p == NULL) {
thread = kzalloc(sizeof(*thread), GFP_KERNEL); //新建binder_thread结构体
if (thread == NULL)
return NULL;
binder_stats_created(BINDER_STAT_THREAD);
thread->proc = proc;
thread->pid = current->pid; //保存当前进程(线程)的pid
init_waitqueue_head(&thread->wait);
INIT_LIST_HEAD(&thread->todo);
rb_link_node(&thread->rb_node, parent, p);
rb_insert_color(&thread->rb_node, &proc->threads);
thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
thread->return_error = BR_OK;
thread->return_error2 = BR_OK;
}
return thread;
}

binder_ioctl_write_read

对于ioctl()方法中,传递进来的命令是cmd = BINDER_WRITE_READ时执行该方法,arg是一个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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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;

if (size != sizeof(struct binder_write_read)) {
ret = -EINVAL;
goto out;
}
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { //把用户空间数据ubuf拷贝到bwr
ret = -EFAULT;
goto out;
}

if (bwr.write_size > 0) {
//当写缓存中有数据,则执行binder写操作
ret = binder_thread_write(proc, thread,
bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
trace_binder_write_done(ret);
if (ret < 0) { //当写失败,再将bwr数据写回用户空间,并返回
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
if (bwr.read_size > 0) {
//当读缓存中有数据,则执行binder读操作
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);
if (!list_empty(&proc->todo))
wake_up_interruptible(&proc->wait); //唤醒等待状态的线程
if (ret < 0) { //当读失败,再将bwr数据写回用户空间,并返回
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}

if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { //将内核数据bwr拷贝到用户空间ubuf
ret = -EFAULT;
goto out;
}
out:
return ret;
}

对于binder_ioctl_write_read的流程图,如下:

avatar

流程:

  • 首先,把用户空间数据ubuf拷贝到内核空间bwr;
  • 当bwr写缓存有数据,则执行binder_thread_write;当写失败则将bwr数据写回用户空间并退出;
  • 当bwr读缓存有数据,则执行binder_thread_read;当读失败则再将bwr数据写回用户空间并退出;
  • 最后,把内核数据bwr拷贝到用户空间ubuf。

ioctl命令常见命令的使用场景,其中BINDER_WRITE_READ最为频繁

BINDER_WRITE_READ

Binder读写交互场景,IPC.talkWithDriver
BINDER_SET_CONTEXT_MGR
servicemanager进程成为上下文管理者,binder_become_context_manager()
BINDER_SET_MAX_THREADS
初始化ProcessState对象,open_driver()
主动调整参数,ProcessState.setThreadPoolMaxThreadCount()
BINDER_VERSION
初始化ProcessState对象,open_driver()

小节

  • binder_init:初始化字符设备;
  • binder_open:打开驱动设备,过程需要持有binder_main_lock同步锁;
  • binder_mmap:申请内存空间,该过程需要持有binder_mmap_lock同步锁;
  • binder_ioctl:执行相应的ioctl操作,该过程需要持有binder_main_lock同步锁;
    • 当处于binder_thread_read过程,read_buffer无数据则释放同步锁,并处于wait_event_freezable过程,等有数据到来则唤醒并尝试持有同步锁。

附录

序号 结构体 名称 解释
1 binder_proc binder进程每个进程调用open()打开binder驱动都会创建该结构体,用于管理IPC所需的各种信息
2 binder_thread binder线程对应于上层的binder线程
3 binder_node binder实体应于BBinder对象,记录BBinder的进程、指针、引用计数等
4 binder_ref binder引用对应于BpBinder对象,记录BpBinder的引用计数、死亡通知、BBinder指针等
5 binder_ref_death binder死亡引用记录binder死亡的引用信息
6 binder_write_read binder读写记录buffer中读和写的数据信息
7 binder_transaction_data binder事务数据记录传输数据内容,比如发送方pid/uid,RPC数据
8 flat_binder_object binder扁平对象Binder对象在两个进程间传递的扁平结构
9 binder_buffer binder内存调用mmap()创建用于Binder传输数据的缓存区
10 binder_transaction binder事务记录传输事务的发送方和接收方线程、进程等
11 binder_work binder工作记录binder工作类型
12 binder_state binder状态

BWR核心数据图表

avatar

binder_write_read是整个Binder IPC过程,最为核心的数据结构之一。

binder_proc

binder_proc结构体:用于管理IPC所需的各种信息,拥有其他结构体的结构体。

类型 成员变量 解释
struct hlist_node proc_node 进程节点
struct rb_root threads binder_thread红黑树的根节点
struct rb_root nodes binder_node红黑树的根节点
struct rb_root refs_by_desc binder_ref红黑树的根节点(以handle为key)
struct rb_root refs_by_node binder_ref红黑树的根节点(以ptr为key)
int pid 相应进程id
struct vm_area_struct * vma 指向进程虚拟地址空间的指针
struct mm_struct * vma_vm_mm 相应进程的内存结构体
struct task_struct * tsk 相应进程的task结构体
struct files_struct * files 相应进程的文件结构体
struct hlist_node deferred_work_node
int deferred_work
void * buffer 内核空间的起始地址
ptrdiff_t user_buffer_offset 内核空间与用户空间的地址偏移量
struct list_head buffers 所有的buffer
struct rb_root free_buffers 空闲的buffer
struct rb_root allocated_buffers 已分配的buffer
size_t free_async_space 异步的可用空闲空间大小
struct page ** pages 指向物理内存页指针的指针
size_t buffer_size 映射的内核空间大小
uint32_t buffer_free 可用内存总大小
struct list_head todo 进程将要做的事
wait_queue_head_t wait 等待队列
struct binder_stats stats binder统计信息
struct list_head delivered_death 已分发的死亡通知
int max_threads 最大线程数
int requested_threads 请求的线程数
int requested_threads_started 已启动的请求线程数
int ready_threads 准备就绪的线程个数
long default_priority 默认优先级
struct dentry * debugfs_entry

binder_thread

binder_thread结构体代表当前binder操作所在的线程

类型 成员变量 解释
struct binder_proc * proc 线程所属的进程
struct rb_node rb_node
int pid 线程pid
int looper looper的状态
struct binder_transaction * transaction_stack 线程正在处理的事务
struct list_head todo 将要处理的链表
uint32_t return_error write失败后,返回的错误码
uint32_t return_error2 write失败后,返回的错误码2
wait_queue_head_t wait 等待队列的队头
struct binder_stats stats binder线程的统计信息

binder_write_read

用户空间程序和Binder驱动程序交互基本都是通过BINDER_WRITE_READ命令,来进行数据的读写操作。

类型 成员变量 解释
binder_size_t write_size write_buffer的总字节数
binder_size_t write_consumed write_buffer已消费的字节数
binder_uintptr_t write_buffer 写缓冲数据的指针
binder_size_t read_size read_buffer的总字节数
binder_size_t read_consumed read_buffer已消费的字节数
binder_uintptr_t read_buffer 读缓存数据的指针
  • write_buffer变量:用于发送IPC(或IPC reply)数据,即传递经由Binder Driver的数据时使用。
  • read_buffer 变量:用于接收来自Binder Driver的数据,即Binder Driver在接收IPC(或IPC reply)数据后,保存到read_buffer,再传递到用户空间;

write_buffer和read_buffer都是包含Binder协议命令和binder_transaction_data结构体。

  • copy_from_user()将用户空间IPC数据拷贝到内核态binder_write_read结构体;
  • copy_to_user()将用内核态binder_write_read结构体数据拷贝到用户空间;

感谢
http://gityuan.com