Binder驱动概述 概述
Binder驱动是Android专用的,但底层的驱动架构与Linux驱动一样。binder驱动在以misc设备进行注册 ,作为虚拟字符设备 ,没有直接操作硬件,只是对设备内存的处理 。主要是驱动设备的**初始化(binder_init), 打开 (binder_open), 映射(binder_mmap), 数据操作(binder_ioctl)**。
系统调用
用户态的程序调用Kernel层驱动是需要陷入内核态 ,**进行系统调用(syscall),比如 打开Binder驱动方法的调用链为: open-> __open() -> binder_open()**。 open()为用户空间的方法,__open()便是系统调用中相应的处理方法,通过查找,对应调用到内核binder驱动的binder_open()方法,至于其他的从用户态陷入内核态的流程也基本一致
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
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; }
主要工作如下:
binder_update_page_range 主要完成工作:分配物理空间,将物理空间映射到内核空间,将物理空间映射到进程空间 . 另外,不同参数下该方法也可以释放物理页面。
binder_ioctl
binder_ioctl()函数负责在两个进程间收发IPC数据和IPC reply数据
文件描述符,是通过open()方法打开Binder Driver后返回值 ;
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 的流程图,如下:
流程:
首先,把用户空间数据ubuf拷贝到内核空间bwr;
当bwr写缓存有数据,则执行binder_thread_write;当写失败则将bwr数据写回用户空间并退出;
当bwr读缓存有数据,则执行binder_thread_read;当读失败则再将bwr数据写回用户空间并退出;
最后,把内核数据bwr拷贝到用户空间ubuf。
ioctl命令常见命令的使用场景,其中BINDER_WRITE_READ最为频繁
BINDER_WRITE_READ
Binder读写交互场景,IPC.talkWithDriverBINDER_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核心数据图表
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