Binder Driver 打开及内存映射

涉及的代码:

1
2
kernel/include/uapi/linux/android/binder.h
kernel/drivers/android/binder.c

1. binder_open

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// kernel/drivers/android/binder.c
static bool is_rt_policy(int policy)
{
return policy == SCHED_FIFO || policy == SCHED_RR;
}
static bool is_fair_policy(int policy)
{
return policy == SCHED_NORMAL || policy == SCHED_BATCH;
}
static bool binder_supported_policy(int policy)
{
return is_fair_policy(policy) || is_rt_policy(policy);
}
static inline void binder_stats_created(enum binder_stat_types type)
{
atomic_inc(&binder_stats.obj_created[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
// kernel/drivers/android/binder.c
static int binder_open(struct inode *nodp, struct file *filp)
{
// 创建进程对应的binder_proc。
struct binder_proc *proc;
struct binder_device *binder_dev;
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
current->group_leader->pid, current->pid);
// 创建1个binder_proc描述为binder进程,并为其分配内存。
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
// 初始化两个自旋锁。
// inner_lock保护线程、binder_node以及所有与进程相关的的todo队列。
// outer_lock保护binder_ref。
spin_lock_init(&proc->inner_lock);
spin_lock_init(&proc->outer_lock);
// 获取当前进程组领头进程。
get_task_struct(current->group_leader);
// 初始化binder_proc。
proc->tsk = current->group_leader;
// 锁保护。
mutex_init(&proc->files_lock);
// 初始化proc的todo列表。
INIT_LIST_HEAD(&proc->todo);
// 判断当前进程的调度策略是否支持,binder只支持SCHED_NORMAL(00b)、SCHED_FIFO(01b)、SCHED_RR(10b)、SCHED_BATCH(11b)。
// prio为进程优先级,可通过normal_prio获取。一般分为实时优先级及静态优先级。
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);
}
// 通过miscdev获取binder_device。
binder_dev = container_of(filp->private_data, struct binder_device,
miscdev);
proc->context = &binder_dev->context;
// 初始化binder_proc的binder_alloc字段。
binder_alloc_init(&proc->alloc);
// binder驱动维护静态全局数组binder_stats,其中有一个成员数组obj_created。
// 当binder_open调用时,obj_created[BINDER_STAT_PROC]将自增。该数组用来统计binder对象的数量。
binder_stats_created(BINDER_STAT_PROC);
// 初始化binder_proc的pid为领头进程的pid值。
proc->pid = current->group_leader->pid;
// 初始化delivered_death及waiting_threads队列。
INIT_LIST_HEAD(&proc->delivered_death);
INIT_LIST_HEAD(&proc->waiting_threads);
// private_data保存binder_proc对象。
filp->private_data = proc;
// 将binder_proc加入到全局队列binder_procs中,该操作必须加锁。
mutex_lock(&binder_procs_lock);
hlist_add_head(&proc->proc_node, &binder_procs);
mutex_unlock(&binder_procs_lock);
// 若/sys/kernel/binder/proc目录已经创建好,则在该目录下创建一个以pid为名的文件。
if (binder_debugfs_dir_entry_proc) {
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
// proc调试条目在上下文之间共享,如果进程尝试使用其他上下文再次打开驱动程序,则此操作将失败。
proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
binder_debugfs_dir_entry_proc,
(void *)(unsigned long)proc->pid,
&binder_proc_fops);
}
return 0;
}
1
2
3
4
5
6
7
8
// kernel/drivers/android/binder_alloc.c
// 初始化新的binder proc。
void binder_alloc_init(struct binder_alloc *alloc)
{
alloc->pid = current->group_leader->pid;
mutex_init(&alloc->mutex);
INIT_LIST_HEAD(&alloc->buffers);
}
字段 描述 调度器类
SCHED_BATCH 用于非交互的处理器消耗型进程。 CFS
SCHED_DEADLINE 突发型的实时进程调度算法。 DL
SCHED_FIFO 先入先出调度算法 RT
SCHED_IDLE 用于系统负载很低时。 CFS-IDLE
SCHED_NORMAL 用于普通进程。 CFS
SCHED_RR 时间片轮转调度算法。 RT

2. 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
// kernel/drivers/android/binder.c
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
// private_data保存binder_proc进程结构体。
struct binder_proc *proc = filp->private_data;
const char *failure_string;
if (proc->tsk != current->group_leader)
return -EINVAL;
// 限定映射的用户空间不超过SZ_4M(0x0x400000=4MB),其中vma用户描述用户空间地址。
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
"%s: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
__func__, proc->pid, vma->vm_start, vma->vm_end,
(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
(unsigned long)pgprot_val(vma->vm_page_prot));
// FORBIDDEN_MMAP_FLAGS,用户申请映射的空间只可读。因此如果有写相关flag就会报错。
if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
ret = -EPERM;
failure_string = "bad vm_flags";
goto err_bad_arg;
}
// 定义不能拷贝、禁止可能写操作的标志位。
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);
if (ret)
return ret;
mutex_lock(&proc->files_lock);
proc->files = get_files_struct(current);
mutex_unlock(&proc->files_lock);
return 0;
err_bad_arg:
pr_err("binder_mmap: %d %lx-%lx %s failed %d\n",
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}
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
// kernel/drivers/android/binder_alloc.c
// 计算出给定buffer的大小。
static size_t binder_alloc_buffer_size(struct binder_alloc *alloc,
struct binder_buffer *buffer)
{
// binder进程中的binder_alloc成员管理着一个内核buffers链表。
// 先检查这个新的buffer是否在链表末尾,计算出尾部的地址,再减去当前buffer的内核地址(buffer->data是当前buffer的起始地址)。
// buffer大小为:基地址(alloc->buffer,总的buffer内核起始地址)+总大小(alloc->buffer_size,映射的总大小)。
if (list_is_last(&buffer->entry, &alloc->buffers))
return (u8 *)alloc->buffer +
alloc->buffer_size - (u8 *)buffer->data;
// 若不在尾部,则用下一个buffer的内核地址减去当前buffer的内核地址。
return (u8 *)binder_buffer_next(buffer)->data - (u8 *)buffer->data;
}

static void binder_insert_free_buffer(struct binder_alloc *alloc,
struct binder_buffer *new_buffer)
{
// 获取Binder进程的红黑树根节点。
struct rb_node **p = &alloc->free_buffers.rb_node;
struct rb_node *parent = NULL;
struct binder_buffer *buffer;
size_t buffer_size;
size_t new_buffer_size;
BUG_ON(!new_buffer->free);
// 通过binder_alloc_buffer_size计算当前new_buffer的大小,之后将用于比较。
new_buffer_size = binder_alloc_buffer_size(alloc, new_buffer);
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%d: add free buffer, size %zd, at %pK\n",
alloc->pid, new_buffer_size, new_buffer);
while (*p) {
parent = *p;
// 获取当前红黑树节点的buffer。
buffer = rb_entry(parent, struct binder_buffer, rb_node);
BUG_ON(!buffer->free);
// 计算当前红黑树节点的buffer大小。
buffer_size = binder_alloc_buffer_size(alloc, buffer);
// 红黑树遵循二叉树规则:当新的buffer比当前节点的buffer小时,向左子节点继续重复,否则转向右子节点。
if (new_buffer_size < buffer_size)
p = &parent->rb_left;
else
p = &parent->rb_right;
}
// 找到合适位置后,将new_buffer插入到该位置。
rb_link_node(&new_buffer->rb_node, parent, p);
rb_insert_color(&new_buffer->rb_node, &alloc->free_buffers);
}

// 映射proc的虚拟地址空间。
// 参数alloc:此过程的分配结构。
// 参数vma:将vma传递给mmap()。
// 由binder_mmap()调用以初始化在vma中指定的用于分配binder buffer的空间。
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;
// 分配buffer时加互斥锁。
mutex_lock(&binder_alloc_mmap_lock);
if (alloc->buffer) {
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
// 在进程内核空间中分配vma->vm_end到vma->vm_start的连续空间。
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;
}
// binder_alloc的buffer指向内核空间地址。
alloc->buffer = area->addr;
// vma->vm_start是用户空间起始地址,alloc->buffer是内核空间起始地址。
// 用户空间地址与内核空间地址的差值保存在user_buffer_offset中。
alloc->user_buffer_offset =
vma->vm_start - (uintptr_t)alloc->buffer;
mutex_unlock(&binder_alloc_mmap_lock);
// VIPT(Virtual Index Physical Tag)-虚拟地址做索引,物理地址做标签。
#ifdef CONFIG_CPU_CACHE_VIPT
if (cache_is_vipt_aliasing()) {
while (CACHE_COLOUR(
(vma->vm_start ^ (uint32_t)alloc->buffer))) {
pr_info("%s: %d %lx-%lx maps %pK bad alignment\n",
__func__, alloc->pid, vma->vm_start,
vma->vm_end, alloc->buffer);
vma->vm_start += PAGE_SIZE;
}
}
#endif
// pages管理的是物理页,分配的长度是(vma->vm_end-vma->vm_start)/PAGE_SIZE)。
// 单位是sizeof(alloc->pages[0])的数组,pages指向其起始地址。
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;
}
// 通过get_vm_area分配的buffer_size是内核空间大小。
alloc->buffer_size = vma->vm_end - vma->vm_start;
// 分配binder_buffer结构体。
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->data = alloc->buffer;
// 将buffer加到binder_alloc中的内核buffers链表中管理。
list_add(&buffer->entry, &alloc->buffers);
buffer->free = 1;
// 该buffer未被使用,故调用binder_insert_free_buffer红黑树进行管理。
binder_insert_free_buffer(alloc, buffer);
// 将最大可用于异步事务的内核buffer大小设置为内核buffer大小的一半。
alloc->free_async_space = alloc->buffer_size / 2;
barrier();
// 描述用户空间的vma。
alloc->vma = vma;
alloc->vma_vm_mm = vma->vm_mm;
atomic_inc(&alloc->vma_vm_mm->mm_count);
return 0;
err_alloc_buf_struct_failed:
kfree(alloc->pages);
alloc->pages = NULL;
err_alloc_pages_failed:
mutex_lock(&binder_alloc_mmap_lock);
vfree(alloc->buffer);
alloc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
mutex_unlock(&binder_alloc_mmap_lock);
pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
alloc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}