binder 驱动的操作详解手机开发

以下源码均来自
https://github.com/aosp-mirror/kernel_common.git

Binder驱动四个基本操作

在进程启动的时候跟binder驱动交互有四个操作:

init -> open -> mmap -> ioctl

  • init: 初始化驱动

  • open: 打开驱动

  • mmap: 在内核分配binder_buffer

  • ioctl: 进程跟驱动的操作交互

初始化字符设备

static int __init binder_init(void) 
{
 
int ret; 
char *device_name, *device_tmp; 
struct binder_device *device; 
struct hlist_node *tmp; 
char *device_names = NULL; 
// 初始化 binder缓冲区 
ret = binder_alloc_shrinker_init(); 
// 创建binder目录 /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); 
if (binder_debugfs_dir_entry_root) {
 
// 创建binder/proc/state文件  
debugfs_create_file("state", 
0444, 
binder_debugfs_dir_entry_root, 
NULL, 
&binder_state_fops); 
// 创建binder/proc/stats文件  
debugfs_create_file("stats", 
0444, 
binder_debugfs_dir_entry_root, 
NULL, 
&binder_stats_fops); 
// 创建binder/proc/transactions文件  
debugfs_create_file("transactions", 
0444, 
binder_debugfs_dir_entry_root, 
NULL, 
&binder_transactions_fops); 
// 创建binder/proc/transaction_log文件  
debugfs_create_file("transaction_log", 
0444, 
binder_debugfs_dir_entry_root, 
&binder_transaction_log, 
&binder_transaction_log_fops); 
// 创建binder/proc/failed_transaction_log文件  
debugfs_create_file("failed_transaction_log", 
0444, 
binder_debugfs_dir_entry_root, 
&binder_transaction_log_failed, 
&binder_transaction_log_fops); 
} 
if (!IS_ENABLED(CONFIG_ANDROID_BINDERFS) && 
strcmp(binder_devices_param, "") != 0) {
 
device_names = kstrdup(binder_devices_param, GFP_KERNEL); 
// 创建binder设备 
device_tmp = device_names; 
while ((device_name = strsep(&device_tmp, ","))) {
 
ret = init_binder_device(device_name); 
} 
} 
ret = init_binderfs(); 
return ret; 
} 

打开binder驱动设备

static int binder_open(struct inode *nodp, struct file *filp) 
{
 
struct binder_proc *proc; 
struct binder_device *binder_dev; 
struct binderfs_info *info; 
struct dentry *binder_binderfs_dir_entry_proc = NULL; 
//  分配binder_proc结构体 
proc = kzalloc(sizeof(*proc), GFP_KERNEL); 
if (proc == NULL) 
return -ENOMEM; 
spin_lock_init(&proc->inner_lock); 
spin_lock_init(&proc->outer_lock); 
get_task_struct(current->group_leader); 
proc->tsk = current->group_leader; 
// 初始化proc->todo队列 
INIT_LIST_HEAD(&proc->todo); 
// 设置进程优先级 
proc->default_priority = task_nice(current);// 初始化context,它记录了ServiceManager信息,是全局静态变量 
proc->context = &binder_dev->context; 
binder_alloc_init(&proc->alloc); 
binder_stats_created(BINDER_STAT_PROC); 
proc->pid = current->group_leader->pid; 
INIT_LIST_HEAD(&proc->delivered_death); 
INIT_LIST_HEAD(&proc->waiting_threads); 
// 将proc保存到文件结构中,供下次调用使用 
filp->private_data = proc; 
mutex_lock(&binder_procs_lock); 
// 将proc添加到全局的binder_procs 
hlist_add_head(&proc->proc_node, &binder_procs); 
mutex_unlock(&binder_procs_lock); 
if (binder_debugfs_dir_entry_proc) {
 
char strbuf[11]; 
// 创建文件 /sys/kernel/debug/binde/proc/pid 
proc->debugfs_entry = debugfs_create_file(strbuf, 0444, 
binder_debugfs_dir_entry_proc, 
(void *)(unsigned long)proc->pid, 
&proc_fops); 
} 
... 
return 0; 
} 

进程调用了binder_open打开驱动设备,binder内核会创建一个对应的binder_proc,初始化之后加入全局的红黑树binder_procs

mmap 分配 binder_buffer

static int binder_mmap(struct file *filp, struct vm_area_struct *vma) 
{
 
int ret; 
// 从flip文件取出binder_proc 
struct binder_proc *proc = filp->private_data; 
const char *failure_string; 
// 映射分配内存 proc->alloc 
ret = binder_alloc_mmap_handler(&proc->alloc, vma); 
return 0; 
} 

vm_struct描述的是内核虚拟地址, vm_area_struct 描述的是用户虚拟地址

// binder_alloc.c 
int binder_alloc_mmap_handler(struct binder_alloc *alloc, 
struct vm_area_struct *vma) 
{
 
int ret; 
const char *failure_string; 
struct binder_buffer *buffer; 
mutex_lock(&binder_alloc_mmap_lock); 
// 内存大小,默认4m 
alloc->buffer_size = min_t(unsigned long, vma->vm_end - vma->vm_start, SZ_4M); 
mutex_unlock(&binder_alloc_mmap_lock); 
// 设置alloc->buffer为用户虚拟地址vma首地址,vma->vm_start为用户空间起始地址 
alloc->buffer = (void __user *)vma->vm_start; 
// 分配page页数组物理空间,实际上page页还没申请物理内存 
alloc->pages = kcalloc(alloc->buffer_size / PAGE_SIZE, sizeof(alloc->pages[0]), GFP_KERNEL); 
// 分配创建一个binder_buffer 
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); 
// 将binder_buffer的用户缓存地址指向alloc->buffer,也就是用户虚拟地址vma首地址 
buffer->user_data = alloc->buffer; 
//  将buffer加入到alloc当前进程的buffers列表中 
list_add(&buffer->entry, &alloc->buffers); 
buffer->free = 1; 
// 从binder_proc的结构可得,每个binder进程会管理一个空闲内核缓冲区红黑树,当binder_mmap映射内存到进程空间时,将会把一个新创建的空闲buffer加入到红黑树中 
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); 
return 0; 
} 

kmalloc、kzalloc、vmalloc

  • kmalloc: 在内核空间申请内存,不做清零初始化操作,申请获得物理内存。
  • kzalloc: 在kmalloc基础上增加初始化清零操作
  • vmalloc: 在内核空间申请内存,它申请的内存是位于vmalloc_start到vmalloc_end之间的虚拟内存,获取的是虚拟内存地址。

binder_mmap未做用户空间和内核空间的映射

开始调用binder_mmap()的时候,binder驱动只为进分配了一个buffer,所以这里的alloc->buffer与buffer->data地址相同,但是还没有将用户虚拟地址与内核的虚拟地址映射在物理页面中,真正的申请物理页和映射动作是在binder_transaction才会开始

参考:
https://segmentfault.com/a/1190000014643994?utm_source=channel-hottest#item-3-5

binder_update_page_range

binder_update_page_range: 管理物理内存的分配和释放alloc、映射用户空间和内核空间

// allocate 为1表示申请分配内存,为0表示释放内存 
static int binder_update_page_range(struct binder_alloc *alloc, int allocate, 
void __user *start, void __user *end) {
 
void __user *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; 
trace_binder_update_page_range(alloc, allocate, start, end); 
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 && mmget_not_zero(alloc->vma_vm_mm)) 
mm = alloc->vma_vm_mm; 
if (mm) {
 
down_read(&mm->mmap_sem); 
vma = alloc->vma; 
} 
// 遍历所有page 
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]; 
if (page->page_ptr) {
 // 已经有分配内存和映射 
trace_binder_alloc_lru_start(alloc, index); 
on_lru = list_lru_del(&binder_alloc_lru, &page->lru); 
WARN_ON(!on_lru); 
trace_binder_alloc_lru_end(alloc, index); 
continue; 
} 
trace_binder_alloc_page_start(alloc, index); 
//分配一个page的物理内存,直接赋值给内核虚拟进程空间page->page_ptr 
page->page_ptr = alloc_page(GFP_KERNEL |__GFP_HIGHMEM | __GFP_ZERO); 
page->alloc = alloc; 
INIT_LIST_HEAD(&page->lru); 
user_page_addr = (uintptr_t)page_addr; 
// 物理空间映射到用户虚拟进程空间 
ret = vm_insert_page(vma, user_page_addr, page[0].page_ptr); 
if (index + 1 > alloc->pages_high) 
alloc->pages_high = index + 1; 
trace_binder_alloc_page_end(alloc, index); 
} 
if (mm) {
 
up_read(&mm->mmap_sem); 
mmput(mm); 
} 
return 0; 
free_range: // 释放内存 
for (page_addr = end - PAGE_SIZE; 1; page_addr -= PAGE_SIZE) {
 
bool ret; 
size_t index; 
index = (page_addr - alloc->buffer) / PAGE_SIZE; 
page = &alloc->pages[index]; 
trace_binder_free_lru_start(alloc, index); 
ret = list_lru_add(&binder_alloc_lru, &page->lru); 
WARN_ON(!ret); 
trace_binder_free_lru_end(alloc, index); 
if (page_addr == start) 
break; 
continue; 
} 

进程跟驱动的操作交互

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 
{
 
int ret; 
// 从文件取出进程proc 
struct binder_proc *proc = filp->private_data; 
struct binder_thread *thread; 
unsigned int size = _IOC_SIZE(cmd); 
void __user *ubuf = (void __user *)arg; 
binder_selftest_alloc(&proc->alloc); 
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); 
// 获取当前访问驱动的线程 
thread = binder_get_thread(proc); 
switch (cmd) {
 
case BINDER_WRITE_READ: 
// 读写数据,数据都封装在binder_write_read数据结构中 
ret = binder_ioctl_write_read(filp, cmd, arg, thread); 
break; 
case BINDER_SET_MAX_THREADS: {
 
// 设置最大线程数 
int max_threads; 
// 从用户空间读取最大线程数 
if (copy_from_user(&max_threads, ubuf, 
sizeof(max_threads))) {
 
ret = -EINVAL; 
goto err; 
} 
binder_inner_proc_lock(proc); 
proc->max_threads = max_threads; 
binder_inner_proc_unlock(proc); 
break; 
} 
case BINDER_SET_CONTEXT_MGR_EXT: {
 
// 注册Service Manager服务 
struct flat_binder_object fbo; 
if (copy_from_user(&fbo, ubuf, sizeof(fbo))) {
 
ret = -EINVAL; 
goto err; 
} 
ret = binder_ioctl_set_ctx_mgr(filp, &fbo); 
break; 
} 
case BINDER_SET_CONTEXT_MGR: 
// 注册Service Manager服务 
ret = binder_ioctl_set_ctx_mgr(filp, NULL); 
break; 
case BINDER_THREAD_EXIT: 
// 退出释放当前线程 
binder_thread_release(proc, thread); 
thread = NULL; 
break; 
case BINDER_VERSION: {
 
// 返回版本信息 
struct binder_version __user *ver = ubuf; 
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, 
&ver->protocol_version)) {
 
ret = -EINVAL; 
goto err; 
} 
break; 
} 
case BINDER_GET_NODE_INFO_FOR_REF: {
 
// 获取binder_node信息 
struct binder_node_info_for_ref info; 
if (copy_from_user(&info, ubuf, sizeof(info))) {
 
ret = -EFAULT; 
goto err; 
} 
ret = binder_ioctl_get_node_info_for_ref(proc, &info); 
if (copy_to_user(ubuf, &info, sizeof(info))) {
 
ret = -EFAULT; 
goto err; 
} 
break; 
} 
case BINDER_GET_NODE_DEBUG_INFO: {
 
// 获取binder_node debug信息 
struct binder_node_debug_info info; 
if (copy_from_user(&info, ubuf, sizeof(info))) {
 
ret = -EFAULT; 
goto err; 
} 
ret = binder_ioctl_get_node_debug_info(proc, &info); 
if (copy_to_user(ubuf, &info, sizeof(info))) {
 
ret = -EFAULT; 
goto err; 
} 
break; 
} 
ret = 0; 
return ret; 
} 

ioctl 基本流程

  1. 获取binder_proc
  2. binder_thread
  3. copyFromUser()
  4. 处理数据
  5. copyToUser()

ioctl 命令

  1. BINDER_WRITE_READ
    向驱动读取和写入数据.可同时读和写,数据结构是binder_write_read

  2. BINDER_SET_MAX_THREADS
    设置线程池的最大的线程数,达到上限后驱动将不会在通知应用层启动新线程

  3. BINDER_SET_CONTEXT_MGR
    将本进程设置为binder系统的管理进程,只有servicemanager进程才可以注册

  4. BINDER_THREAD_EXIT
    通知驱动当前线程要退出了,以便驱动清理该线程相关的数据

  5. BINDER_VERSION
    获取binder的版本号

  6. BC_INCREFS 、BC_ACQUIRE 、BC_RELEASE 、BC_DECREFS
    增加或减少Binder的引用计数,用以实现强指针或弱指针的功能。

binder_write_read命令

binder_ioctl_write_read是最重要的一步,跟用户进程如何交互数据

binder_write_read 是驱动内核处理binder数据的数据结构,内部分成读缓存和写缓存

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;  
// 从用户空间的binder_write_read拷贝到内核空间的binder_write_read结构 
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
  
ret = -EFAULT;  
goto out;  
} 
// 如果bwr有写缓存的数据 
if (bwr.write_size > 0) {
  
ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed); 
}  
// 如果bwr有读缓存的数据 
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); 
}// 将bwr拷贝回用户空间 
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
  
ret = -EFAULT; 
goto out; 
} 
out: 
return ret; 
} 

binder_thread_write

binder_thread_write表示处理写缓存数据,表示的是binder的发起端,发起了一次请求

proc表示目标进程,thread表示目标线程

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; 
ptr += sizeof(uint32_t); 
switch (cmd) {
 
... 
case BC_TRANSACTION: 
case BC_REPLY: {
} 
} 
return 0; 
} 

binder_thread_read

binder_thread_read表示处理读缓存数据,表示的是binder的接收端,接收到了一次请求,可能需要写回去返回值

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) 
{
  
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; 
retry: 
binder_inner_proc_lock(proc); 
// 获取当前线程是否空闲等待,即线程事务栈和todo队列都为空 
wait_for_proc_work = binder_available_for_proc_work_ilocked(thread); 
binder_inner_proc_unlock(proc); 
// 线程即将进入睡眠等待,设置线程状态改为BINDER_LOOPER_STATE_WAITING*/ 
thread->looper |= BINDER_LOOPER_STATE_WAITING; 
if (non_block) {
 
if (!binder_has_work(thread, wait_for_proc_work)) 
ret = -EAGAIN; 
} else {
 
// wait_for_proc_work 线程没有事务项,需要进入阻塞等待 
ret = binder_wait_for_work(thread, wait_for_proc_work); 
} 
thread->looper &= ~BINDER_LOOPER_STATE_WAITING; 
if (ret) 
return ret; 
while (1) {
 
uint32_t cmd; 
struct binder_transaction_data_secctx tr;      
struct binder_transaction_data *trd = &tr.transaction_data;  
struct binder_work *w = NULL;  
switch (w->type) {
 
case BINDER_WORK_TRANSACTION: {
 
... 
} break;  
case BINDER_WORK_RETURN_ERROR: {
 
... 
} break;} 
done: 
*consumed = ptr - buffer;  
// 当满足条件时候,会创建一个binder_thread,同时告诉给用户进程空间 BR_SPAWN_LOOPER 去创建多一个线程 
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)  
proc->requested_threads++; 
if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))  
return -EFAULT; 
}  
} 
return 0; 
} 

binder_wait_for_work

内核空间等待事务和唤醒,用户空间下的线程也会阻塞在ioctl中

// do_proc_work 表示是否阻塞等待 
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);      
if (binder_has_work_ilocked(thread, do_proc_work)) 
break; 
if (do_proc_work) 
list_add(&thread->waiting_thread_node, &proc->waiting_threads); 
binder_inner_proc_unlock(proc); 
schedule(); 
binder_inner_proc_lock(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; 
} 

Linux 手工休眠线程方式:

  1. 建立并初始化一个等待队列项
    DEFINE_WAIT(my_wait) <==> wait_queue_t my_wait; init_wait(&my_wait);
  2. 将等待队列项添加到等待队列头中,并设置进程的状态
    prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state)
  3. 调用schedule(),告诉内核调度别的进程运行
  4. schedule返回,完成后续清理工作 finish_wait()

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/6243.html

(0)
上一篇 2021年7月17日
下一篇 2021年7月17日

相关推荐

发表回复

登录后才能评论