1. __init binder_init(void)
(1)创建binder_deferred_workqueue工作队列。
(2)注册binder驱动。misc_register(&binder_miscdev);
2. __init binder_open(struct inode *nodp, struct file *filp)
(1)每次调用binder_open,驱动都会申请一个binder_proc对象并初始化(部分全局数据操作需要binder_lock锁)。
struct binder_proc {
struct hlist_node proc_node;-------------------->本proc_node将会链接到binder_procs这个全局链表中。
struct rb_root threads; -------------------->在ioctrl调用中,会将属于本进程的所有线程链入这颗红黑树。
struct rb_root nodes;
struct rb_root refs_by_desc;
struct rb_root refs_by_node;
int pid; -------------------->current->group_leader->pid;
struct vm_area_struct *vma;
struct mm_struct *vma_vm_mm;
struct task_struct *tsk; -------------------->指向调用binder_open的当前进程。
struct files_struct *files;
struct hlist_node deferred_work_node;
int deferred_work;
void *buffer;-------------------->mmap操作后,指向内存映射后的虚拟地址(内核空间使用)。
ptrdiff_t user_buffer_offset;---------------->mmap操作后,内核使用的虚拟地址和用户空间虚拟地址偏差。
struct list_head buffers;-------------------->mmap后,将映射的内存页面链接起来。详细结构体为binder_buffer。
struct rb_root free_buffers; -------------------->mmap后,空闲页面通过红黑树链接起来。详细结构体为binder_buffer。
struct rb_root allocated_buffers; -------------------->正在使用的页面数据,链入该红黑树。详细结构体为binder_buffer。
size_t free_async_space;-------------------->mmap操作后为proc->buffer_size / 2
struct page **pages;---------------->mmap操作后,保存内核页面物理地址。
size_t buffer_size; -------------------->mmap操作后,指示实际已映射的空间大小。
uint32_t buffer_free;
struct list_head todo; -------------------->指向进程工作队列(binder事物处理)。
wait_queue_head_t wait;-------------------->初始化等待队列。
struct binder_stats stats;
struct list_head delivered_death;--->初始化本队列。binder die后,会通过这个链表通知客户端本binder已经死了。
int max_threads;
int requested_threads;
int requested_threads_started;
int ready_threads;
long default_priority; -------------------->调用open的进程nice值(优先级号)。
struct dentry *debugfs_entry;
};
(2)binder_stats.obj_created[BINDER_STAT_PROC]++; //用于统计binder proc创建的数目。
(3)binder_procs:当前创建的binder_proc对象会链接到这里。
(4)filp->private_data:保存当前创建的binder_proc,供其他binder驱动调用使用。
3. static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
(1):客户端部分:使用api:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);来调用。
I. start:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址。
II. length:映射区的长度。
III. prot:期望的内存保护标志,不能与文件的打开模式冲突。
IV.flags:指定映射对象的类型,映射选项和映射页是否可以共享。
V.fd:有效的文件描述符。
VI.offset:被映射对象内容的起点。
(2):驱动部分:
1- 通过传入的filp,获取filp->private_data数据,拿到open_binder时创建的binder_proc对象指针。
2- 内存映射控制在4M以内。
3- vm_struct *area; area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); 申请内核使用的一片虚拟内存空间。需要获取binder_mmap_lock锁。
4- proc->buffer = area->addr; proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
5- proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);申请page结构体队列空间(后来会申请物理页面)。
6- vma->vm_ops = &binder_vm_ops; vma->vm_private_data = proc; //proc对象存入vma->vm_privae_data。
7- mm_struct结构描述了一个进程的整个虚拟地址空间。vm_struct结构来表示vmalloc使用的线性地址.
8- map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);//内核虚拟地址空间和物理空间关联起来。
9- user_page_addr = (uintptr_t)page_addr + proc->user_buffer_offset;//由于open_binder时,算出了应用空间虚拟地址与内核虚拟地址的偏移量。因此,给定一个内核地址虚拟地址,可以快速计算出用户虚拟地址。
10- ret = vm_insert_page(vma, user_page_addr, page[0]); //将物理页面与用户的虚拟地址空间关联起来。
4. static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
(1)binder_ioctl调用先从filp中获取binder_proc变量。并在使用这个文件句柄的binder_proc对象中,查找是否存在本pid的binder_thread对象,没有就创建一个,并链入filp对应的binder_proc中。这样每个open_binder中获得的文件句柄会包含一个binder_proc,使用该文件句柄的每个进程会有一个对应的binder_thread,并链入binder_proc。
struct binder_thread {
struct binder_proc *proc;-------------------->指向所属的binder_proc对象。
struct rb_node rb_node; -------------------->链入binder_proc中的threads红黑树所用的节点对象。
int pid;-------------------->当前线程的pid
int looper; --------------->指示当前线程状态,初始状态为:BIDER_LOOPER_STATE_NEED_RETURN
struct binder_transaction *transaction_stack; -------------------->保存本线程将要处理的事务列表
struct list_head todo; ---------------->保存线程的数据列表。
uint32_t return_error; /* Write failed, return error code in read buf */
uint32_t return_error2; /* Write failed, return error code in read */
/* buffer. Used when sending a reply to a dead process that */
/* we are also waiting on */
wait_queue_head_t wait;-------------------->用于将本线程加入某个等待队列用。
struct binder_stats stats;-------------------->线程统计信息。
};
(2)case BINDER_WRITE_READ:最重要的binder操作ioctl操作码。
struct binder_write_read {
signed long write_size; /* bytes to write */
signed long write_consumed; /* bytes consumed by driver */
unsigned long write_buffer;
signed long read_size; /* bytes to read */
signed long read_consumed; /* bytes consumed by driver */
unsigned long read_buffer;
};
如果write_size>0,表示有数据写。则开始执行一次binder命令操作。
I