“九层之台,始于垒土;千里之行,始于足下”,不论binder机制在Android源码中多庞大繁杂,总归它得从地基开始–Binder驱动,我们知道Android系统是基于Linux的内核的,因而Binder驱动也必须是一个标准的Linux驱动。具体来说,Binder Driver 会将自己注册为一个misc device,并向上层提供一个/dev/binder节点,当然我们需要牢记,Binder并不真实对应硬件设备。

一、binder驱动

Binder驱动运行于内核态,提供open()、mmap()、ioctl()等常用的文件操作接口。
一般来说Binder驱动源码在Kernel工程的 drivers/staging/android目录中,Linux的字符设备通常要经过alloc_chrdev_region(),cdev_init()等一系列操作才能在内核中注册自己,但misc类型驱动的注册相对简单,只需要调用misc_register()即可,主设备号统一为10,次设备号则为每种设备所独有,也可由系统动态分配次设备号。Binder中与驱动注册相关代码如下:

/*drivers/staging/android/Binder.c*/
static struct miscdevice binder_miscdev = {.minor = MISC_DYNAMIC_MINOR,//动态分配次设备号.name = "binder",//驱动名称.fops = &binder_fops//Binder驱动支持的文件操作
};
static int __init binder_init(void)
{....ret = misc_register(binder_miscdev);....
}
static const struct file_operations binder_fops = {.owner = THIS_MODULE,.poll = binder_poll,.unlocked_ioctl = binder_ioctl,.mmap = binder_mmap,.open = binder_open,.flush = binder_flush,.release = binder_release,
};

从这里可以看出,Binder驱动总共为上层应用提供了6个接口–其中使用最多的就是binder_ioctl、binder_mmap、binder_open,常见的read、write在这里没有看到,因为binder_ioctl接口可以代替读写操作,因此一个接口就实现了两种操作,另外Binder所使用的设备驱动入口函数不是module_init(),而是:

device_initcall(binder_init);

这样做的目的可能是Android系统不想支持动态编译的驱动模块,我们可以将其改为动态编译,也就是将设备驱动入口函数改为module_init和module_exit

1、binder_open–打开Binder驱动

上层进程在访问Binder驱动时,首先就是打开/dev/binder节点,这个操作最终的实现是在binder_open()中。

/*drivers/staging/android/Binder.c*/
static int binder_open(truct inode *nodp, struct file *filp)
{struct binder_proc *proc;...proc = kzalloc(sizeof(*proc), GFP_KERNEL); //分配空间if (proc == NULL)return -ENOMEM;get_task_struct(current);proc->tsk = current;// 初始化binder_procINIT_LIST_HEAD(&proc->todo);init_waitqueue_head(&proc->wait);proc->default_priority = task_nice(current);    //加入Binder的全局管理中binder_lock(__func__);binder_stats_created(BINDER_STAT_PROC);// 添加到全局列表binder_procs中hlist_add_head(&proc->proc_node, &binder_procs);proc->pid = current->group_leader->pid;INIT_LIST_HEAD(&proc->delivered_death);filp->private_data = proc;binder_unlock(__func__);return 0;
}

在Binder驱动中,通过binder_procs记录了所有使用Binder的进程。每个初次打开Binder设备的进程都会被添加到这个列表中的。binder_open()已经为用户创建了一个Binder驱动的binder_proc实体,之后用户对Binder设备的操作就是以这个对象为基础的。

2、binder_mmap–映射内存空间

binder_mmap函数对应了mmap系统调用的处理,这个函数也是Binder驱动的精华所在,使用Binder机制,数据只需要经历一次拷贝就可以了,其原理就在这个函数中,
binder_mmap这个函数中,进程B会申请一块物理内存,然后在用户空间和内核空间同时对应到这块内存上。在这之后,当进程A要发送数据给进程B的时候,只需一次,将进程A发送过来的数据拷贝到进程B的内核空间指定的内存地址即可,由于这个内存地址在服务端已经同时映射到用户空间,因此无需再做一次复制,进程B即可直接访问,我们来看源码:

/*drivers/staging/android/Binder.c
*vm_area_struct *vma描述了一块供应用程序使用的虚拟内存,
*其中vma->vm_start和vma->end分别是这块连续的虚拟内存起止点
*/
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{int ret;//step1、Binder驱动的虚拟内存struct vm_struct *area;/*step2、这里的proc结构体在binder_open()已经看到过,它是Binder驱动为应用进程分配的一个数据结构,用于存储和该进程有关的所有信息,如内存分配、线程管理*/struct binder_proc *proc = filp->private_data;const char *failure_string;struct binder_buffer *buffer;.../*step3、在做实际工作前,Binder驱动会判断应用程序申请的内存大小是否合理,它最多能申请4MB的mmap操作*/if((vma -> vm_end - vma -> vm_start ) > SZ_4M){vma -> vm_end = vma -> vm_start + SZ_4M}// 在内核空间获取一块地址范围,也就是说此时还没有分配实际的物理地址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);//这里只是分配了pages数组控件,用于管理物理页面的,也就是用于指示//Binder申请的物理页面状态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;// 通过下面这个函数真正完成内存的申请和地址的映射// 初次使用,先申请一个PAGE_SIZE大小的内存ret = binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma);...

我们解释下tatic int binder_update_page_range(struct binder_proc *proc, int allocate,
void *start, void *end,
struct vm_area_struct *vma)函数的参数:

1、proc:申请内存的进程所持有的binder_proc对象
2、allocate:是申请还是释放。1 表示申请,0 表示释放
3、start:Binder中虚拟内存起点(页表映射代表从虚拟内存到物理内存)
4、end:Binder中虚拟内存终点

对照上面函数的传参就能看出来,在mmap中Binder驱动实际上只为进程分配了一页物理内存,虽然最多支持4MB的mmap,继续看binder_update_page_range

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 vm_struct tmp_area;struct page **page;struct mm_struct *mm;...for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {int ret;struct page **page_array_ptr;page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];BUG_ON(*page);// 真正进行内存的分配*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);if (*page == NULL) {pr_err("%d: binder_alloc_buf failed for page at %p\n",proc->pid, page_addr);goto err_alloc_page_failed;}tmp_area.addr = page_addr;tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;page_array_ptr = page;// 在内核空间进行内存映射ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);if (ret) {pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n",proc->pid, page_addr);goto err_map_kernel_failed;}user_page_addr =(uintptr_t)page_addr + proc->user_buffer_offset;// 在用户空间进行内存映射ret = vm_insert_page(vma, user_page_addr, page[0]);if (ret) {pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",proc->pid, user_page_addr);goto err_vm_insert_page_failed;}/* vm_insert_page does not seem to increment the refcount */}if (mm) {up_write(&mm->mmap_sem);mmput(mm);}preempt_disable();return 0;
...

3、binder_ioctl

这是Binder接口中工作量最大的一个函数,承担了Binder驱动的大部分业务,看看接口提供了哪些操作:

如果命令是BINDER_WRITE_READ,并且如果 bwr.write_size > 0,则调用binder_thread_write如果 bwr.read_size > 0,则调用binder_thread_read
如果命令是BINDER_SET_MAX_THREADS,则设置进程的max_threads,即进程支持的最大线程数
如果命令是BINDER_SET_CONTEXT_MGR,ServiceManager专用,将自己设置为Binder大管家,系统中只能有一个SM存在
如果命令是BINDER_THREAD_EXIT,则调用binder_free_thread,释放binder_thread
如果命令是BINDER_VERSION,则返回当前的Binder版本号

这其中,最关键的就是binder_thread_write方法。当Client请求Server的时候,便会发送一个BINDER_WRITE_READ命令,同时框架会将将实际的数据包装好。关于这个命令,可以查资料或者翻看源码。
最后来总结一下,Binder驱动依然是Linux的典型驱动模型,提供多个文件操作接口。其中binder_ioctl实现了应用程序与Binder驱动之间的命令交互,承载了Binder驱动的绝大部分工作。
接下来分析“DNS服务器”–ServiceManager[Binder Server](三)

Binder驱动与协议(二)相关推荐

  1. 深入分析Android Binder 驱动

    Android Binder是一种在Android里广泛使用的一种远程过程调用接口.从结构上来说Android Binder系统是一种服务器/客户机模式,包括Binder Server.Binder ...

  2. Android10.0 Binder通信原理(五)-Binder驱动分析

    摘要:本节主要来讲解Android10.0 Binder的驱动层分析 阅读本文大约需要花费35分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Android的平台设计 ...

  3. Binder 驱动详解(下)

    前言 通过 Binder 上一篇文章的分析, 我们知道了 Binder 驱动在我们应用开发过程中的使用方式, 了解到了 BBinder 和 BpBinder 两个非常重要的 Native 对象, 本次 ...

  4. 【Binder系列课】二、Binder通信服务应知应会

    Binder问答 概括Binder机制 1.整体业务层CS架构,BpBinder/BnBinder 2.通信层Binder驱动,writeStrongBinder/readStrongBinder完成 ...

  5. wifi linux 驱动分析,Linux 下wifi 驱动开发(二)—— WiFi模块浅析

    一.什么是wifi 模块百度百科上这样定义: Wi-Fi模块又名串口Wi-Fi模块,属于物联网传输层,功能是将串口或TTL电平转为符合Wi-Fi无线网络通信标准的嵌入式模块,内置无线网络协议IEEE8 ...

  6. 关于Binder的点点滴滴(二)

    http://www.linuxidc.com/Linux/2011-07/39271p2.htm 4.Binder协议 Binder协议基本格式是(命令+数据),使用ioctl(fd, cmd, a ...

  7. Linux 下wifi 驱动开发(二)—— WiFi模块浅析

    一.什么是wifi 模块 百度百科上这样定义: Wi-Fi模块又名串口Wi-Fi模块,属于物联网传输层,功能是将串口或TTL电平转为符合Wi-Fi无线网络通信标准的嵌入式模块,内置无线网络协议IEEE ...

  8. STM32驱动开发(二)--USB Device RNDIS虚拟网卡(USB2.0 基础概念讲解)

    STM32驱动开发(二)–USB Device RNDIS虚拟网卡(USB2.0基础概念讲解) 一.简介   本文基于stm32 Rndis实例,github开源, 使用STM32F407单板.结合协 ...

  9. Android Binder机制学习总结(二)-Driver部分

    本文主要分析Binder Driver的实现.Binder Driver 的实现代码位于 android kernel中:<kernel>\drivers\staging\android\ ...

最新文章

  1. 宏基因组学习交流5群成立
  2. 《C语言及程序设计》实践参考——分数的累加
  3. 高可用软件heartbeat服务章节目录(草稿)
  4. forms oracle runtime_Oracle EBS R12 clone 完毕后,登陆到系统,在运行FORMS程序的时候出现FRM-92101...
  5. ABBYY FineReader 12中怎样禁用自动处理
  6. 征战蓝桥 —— 2014年第五届 —— C/C++A组第3题——神奇算式
  7. python 将pdf分页后插入至word中
  8. 台式计算机驱动程序未被安装,电脑未能成功安装设备驱动程序怎么办
  9. Java Spring MVC分层设计
  10. java mysql proxy_Amoeba是一个类似MySQL Proxy的分布式数据库中间代理层软件,是由陈思儒开发的一个开源的java项目...
  11. C#3.0新特性 和 Javascript
  12. sql 统计节点和子节点下面的数量_一次900万+数据量的 SQL 查询优化分析「上百倍性能优化」...
  13. Nginx缓存配置以及nginx ngx_cache_purge模块的使用
  14. win下MySQL 8.0.11 修改密码、开启远程访问
  15. 64位的windows server 2003运行IIS6运行32位的.NET程序
  16. 用mysql搭建蚂蚁笔记_利用蚂蚁笔记搭建个人云笔记/博客
  17. axios和layUI配合加载数据表格demo
  18. Fedora 34 Workstation安装后的配置
  19. 聊聊端口映射和内网穿透
  20. 2021春 算法复习

热门文章

  1. 手写简单版 Promise
  2. 简单写一个python爬虫程序:爬取音乐资源
  3. 论劝退,没人比土木工程老哥更专业,想转行,看看这个热门行业
  4. Google、Facebook等美国IT名企在面试中最看重什么?
  5. 摄像头模组:CMOS图像传感器(Sensor)与镜头(Lens)参数指标
  6. 北大本科转行计算机,北京教委公布本科跨省转学名单,两学霸为学计算机转出北大...
  7. RevitAPI之创建文字和注释【比目鱼原创】
  8. 【linux】使用sed删除大文件中的指定行
  9. android手机评测工具,ADB-Toolkit 测试Android设备入侵Android手机的工具
  10. 生成函数(母函数)入门详解