03. Android Binder图解 小米系统专家 解析binder内核解析binder通信过程 (安卓12)

很多BAT也不一定能懂的binder机制!

因为搞懂binder需要会c,linux内核知识。看java根本就看不懂!

我同事从小米跳槽过来,干安卓framework层10年,是小米的专家级别

然后他把binder驱动层全部和我讲解了一遍,然后我这边做个笔记分享给大家。

分6篇文字讲解:

01. Android Binder图解 小米系统专家 解析Service 的addService注册过程 (安卓12)

02. Android Binder图解 小米系统专家 解析 ServiceManager和binder通信 (安卓12)

03. Android Binder图解 小米系统专家 解析binder驱动层解析binder通信过程 (安卓12)

04. Android Binder图解 小米系统专家 从binder java层解析binder整个流程(安卓12)

05. Android Binder图解 小米系统专家 解析binder总结调用流程 (安卓12)

06. Android Binder图解 小米系统专家 解析binder面试一网打尽(安卓12)

1642394033087-vni.jpg

问题:

1. binder 驱动层的几个方法

2. binder 驱动是怎么帮我们找到 ServiceManager 进程的

3. ServiceManager 进程是怎么进入等待怎么被唤醒的

4. 数据是怎么传递的,handle 和 type 是怎么被计算和管理的

相关源码文件:

/drivers/android/binder.c
/drivers/staging/android/binder.c

4个重要的方法分析:

open 是打开驱动、mmap 是映射驱动、ioctl 是操作驱动、close 是关闭驱动,

分别对应驱动层的 binder_open、binder_mmap、binder_ioctl 和 binder_colse 方法

最终通过ioctl函数和Binder驱动进行通信,最后是交给了binder 驱动的 ioctl 方法,这一部分涉及到Kernel Binder的内容

初始化 binder_init()

内核初始化时,会调用到device_initcall()进行初始化,从而启动binder_init。

binder_init()主要负责注册misc设备,通过调用misc_register()来实现。

在Android8.0之后,现在Binder驱动有三个:/dev/binder; /dev/hwbinder; /dev/vndbinder.

————————————————

<pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'JetBrains Mono',monospace;font-size:0.817rem;"> device_initcall(binder_init);
static HLIST_HEAD(binder_devices); static int __init binder_init(void)
{
int ret;
char *device_name, *device_names, *device_tmp;
struct binder_device *device;
struct hlist_node *tmp; ret = binder_alloc_shrinker_init();
if (ret)
return ret; atomic_set(&binder_transaction_log.cur, ~0U);
atomic_set(&binder_transaction_log_failed.cur, ~0U); //在debugfs文件系统中创建一个目录,返回值是指向dentry的指针
//在手机对应的目录:/sys/kernel/debug/binder,里面创建了几个文件,用来记录binder操作过程中的信息和日志: //failed_transaction_log、state、stats、transaction_log、transactions binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
if (binder_debugfs_dir_entry_root)
//创建目录:/sys/kernel/debug/binder/proc
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
binder_debugfs_dir_entry_root);
...

device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL);

if (!device_names) {
ret = -ENOMEM;
goto err_alloc_device_names_failed;
}
strcpy(device_names, binder_devices_param); device_tmp = device_names;
//Android8.0 中引入了hwbinder,vndbinder,所以现在有三个binder,分别需要创建三个binder device:
// /dev/binder、/dev/hwbinder、/dev/vndbinder //循环注册binder 的三个设备:/dev/binder、/dev/hwbinder、/dev/vndbinder while ((device_name = strsep(&device_tmp, ","))) {
ret = init_binder_device(device_name);
if (ret)
goto err_init_binder_device_failed; }

return ret;    err_init_binder_device_failed:
hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) {
misc_deregister(&device->miscdev);

hlist_del(&device->hlist);
kfree(device); }

kfree(device_names);    err_alloc_device_names_failed:
debugfs_remove_recursive(binder_debugfs_dir_entry_root);   return ret; }

static int __init init_binder_device(const char *name)

{
int ret;
struct binder_device binder_device; //申请内存空间,
binder_device = kzalloc(sizeof(
binder_device), GFP_KERNEL);
if (!binder_device)
return -ENOMEM; binder_device->miscdev.fops = &binder_fops;
binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
binder_device->miscdev.name = name; binder_device->context.binder_context_mgr_uid = INVALID_UID;
binder_device->context.name = name;
mutex_init(&binder_device->context.context_mgr_node_lock); ret = misc_register(&binder_device->miscdev);
if (ret < 0) {
kfree(binder_device);
return ret;
}

hlist_add_head(&binder_device->hlist, &binder_devices);   return ret; }</pre>

1. biner_open 源码分析

这里我们只需要了解 open 是打开驱动、 分别对应驱动层的 binder_open、

创建 binder_proc 对象,并把当前进程等信息保存到 binder_proc 对象,该对象管理 IPC 所需的各种信息并拥有其他结构体的根结构体;再把 binder_proc 对象保存到文件指针 filp,以及把 binder_proc 加入到全局链表 binder_procs。

binder_proc 就是用来存放 binder 相关数据的结构体,每个进程独有一份。

总结:****biner_open**** 里面创建了一个****binder_proc 对象。存放4个红黑树!

红黑树结构:

特点:具有二叉树的特点;根节点是黑色的;叶子节点不存数据;任何相邻的节点都不能同时为红色;每个节点,从该节点到达其可达的叶子节点是所路径,都包含相同数目的黑色节点

优点:具有二叉树所有特点,与平衡树不同的是,红黑树在插入、删除等操作,不会像平衡树那样,频繁着破坏红黑树的规则,所以不需要频繁着调整,减少性能消耗;

缺点:代码复杂,查找效率比平衡二叉树低

————————————————

<wiz_code_mirror><pre class=" CodeMirror-line " role="presentation">static int binder_open(struct inode *nodp, struct file *filp)</pre>

<pre class=" CodeMirror-line " role="presentation">{</pre>

<pre class=" CodeMirror-line " role="presentation"> struct binder_proc *proc;</pre>

<pre class=" CodeMirror-line " role="presentation"> binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",</pre>

<pre class=" CodeMirror-line " role="presentation"> current->group_leader->pid, current->pid);</pre>

<pre class=" CodeMirror-line " role="presentation"> // 在内核空间开辟一块连续内存,而且大小不能超过 128K ,初始化为 0 </pre>

<pre class=" CodeMirror-line " role="presentation"> proc = kvalloc(sizeof(*proc), GFP_KERNEL);</pre>

<pre class=" CodeMirror-line " role="presentation"> if (proc == NULL)</pre>

<pre class=" CodeMirror-line " role="presentation"> return -ENOMEM;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 获取当前线程的 task_struct </pre>

<pre class=" CodeMirror-line " role="presentation"> get_task_struct(current);</pre>

<pre class=" CodeMirror-line " role="presentation"> proc->tsk = current;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 初始化 todo 队列列表 (工作列表)</pre>

<pre class=" CodeMirror-line " role="presentation"> INIT_LIST_HEAD(&proc->todo);</pre>

<pre class=" CodeMirror-line " role="presentation"> // 初始化等待队列 </pre>

<pre class=" CodeMirror-line " role="presentation"> init_waitqueue_head(&proc->wait);</pre>

<pre class=" CodeMirror-line " role="presentation"> // 把 binder_proc 加入到 binder 驱动的列表中</pre>

<pre class=" CodeMirror-line " role="presentation"> hlist_add_head(&proc->proc_node, &binder_procs);</pre>

<pre class=" CodeMirror-line " role="presentation"> // 文件对象的私有数据 = binder_proc </pre>

<pre class=" CodeMirror-line " role="presentation"> filp->private_data = proc;</pre>

<pre class=" CodeMirror-line " role="presentation"> return 0;</pre>

<pre class=" CodeMirror-line " role="presentation">}</pre></wiz_code_mirror>

<wiz_code_mirror><pre class=" CodeMirror-line " role="presentation">struct binder_proc {</pre>

<pre class=" CodeMirror-line " role="presentation"> struct rb_root threads; // 线程处理的红黑树</pre>

<pre class=" CodeMirror-line " role="presentation"> struct rb_root nodes; // 内部 binder 对象的红黑树</pre>

<pre class=" CodeMirror-line " role="presentation"> struct rb_root refs_by_desc;// 外部对应的 binder 对象的红黑树,以 handle 做key</pre>

<pre class=" CodeMirror-line " role="presentation"> struct rb_root refs_by_node;// 外部对应的 binder 对象的红黑树,以 地址 做key</pre>

<pre class=" CodeMirror-line " role="presentation"> int pid; // 进程id</pre>

<pre class=" CodeMirror-line " role="presentation"> struct vm_area_struct *vma;</pre>

<pre class=" CodeMirror-line " role="presentation"> struct mm_struct *vma_vm_mm;</pre>

<pre class=" CodeMirror-line " role="presentation"> struct task_struct *tsk;</pre>

<pre class=" CodeMirror-line " role="presentation"> struct files_struct *files;</pre>

<pre class=" CodeMirror-line " role="presentation"> void *buffer;</pre>

<pre class=" CodeMirror-line " role="presentation"> struct page **pages;</pre>

<pre class=" CodeMirror-line " role="presentation"> size_t buffer_size;</pre>

<pre class=" CodeMirror-line " role="presentation"> uint32_t buffer_free;</pre>

<pre class=" CodeMirror-line " role="presentation"> struct list_head todo;</pre>

<pre class=" CodeMirror-line " role="presentation"> wait_queue_head_t wait;</pre>

<pre class=" CodeMirror-line " role="presentation"> int max_threads;</pre>

<pre class=" CodeMirror-line " role="presentation">};</pre></wiz_code_mirror>

非常重要的数据结构:binder_proc

   struct binder_proc {
   struct hlist_node proc_node;
   // 四棵比较重要的树 
   struct rb_root threads;
   struct rb_root nodes;
   struct rb_root refs_by_desc;
   struct rb_root refs_by_node;
   int pid;
   struct vm_area_struct *vma; //虚拟地址空间,用户控件传过来
   struct mm_struct *vma_vm_mm;
   struct task_struct *tsk;
   struct files_struct *files;
   struct hlist_node deferred_work_node;
   int deferred_work;
   void *buffer; //初始地址
   ptrdiff_t user_buffer_offset; //这里是偏移

   struct list_head buffers;//这个列表连接所有的内存块,以地址的大小为顺序,各内存块首尾相连
   struct rb_root free_buffers;//连接所有的已建立映射的虚拟内存块,以内存的大小为index组织在以该节点为根的红黑树下
   struct rb_root allocated_buffers;//连接所有已经分配的虚拟内存块,以内存块的开始地址为index组织在以该节点为根的红黑树下

   }

2. binder_mmap 源码分析

mmap 是映射驱动,binder_mmap、

(binder_mmap 的主要作用就是开辟一块连续的内核空间,并且开辟一个物理页的地址空间,同时映射到用户空间和内核空间。)

总结:binder_mmap,通过内存映射,使用进程虚拟地址空间和内核虚拟地址空间来映射同一个物理页面


//mmap把设备内存(内核空间)映射到用户空间,从而用户程序可以在用户空间操作内核空间的地址;

//binder_mmap能映射的最大内存空间为4M,而且不能映射具有写权限的内存区域;

//binder_mmap现在内核虚拟映射表上获取一块可以使用的区域,然后分配物理页,再把物理页映射到虚拟映射表上;

内核空间的数据映射到用户空间其实就是添加一个偏移地址,并且将数据的首地址、数据的大小都复制到一个用户空间的Parcel结构体

<wiz_code_mirror><pre class=" CodeMirror-line " role="presentation">static int binder_mmap(struct file *filp, struct vm_area_struct *vma)</pre>

<pre class=" CodeMirror-line " role="presentation">{</pre>

<pre class=" CodeMirror-line " role="presentation"> int ret;</pre>

<pre class=" CodeMirror-line " role="presentation"> struct vm_struct *area;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 获取 binder_proc</pre>

<pre class=" CodeMirror-line " role="presentation"> struct binder_proc *proc = filp->private_data;</pre>

<pre class=" CodeMirror-line " role="presentation"> const char *failure_string;</pre>

<pre class=" CodeMirror-line " role="presentation"> struct binder_buffer *buffer;</pre>

<pre class=" CodeMirror-line " role="presentation"> if (proc->tsk != current)</pre>

<pre class=" CodeMirror-line " role="presentation"> return -EINVAL;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 普通进程 1M - 8k ,ServiceManager 进程是 128K</pre>

<pre class=" CodeMirror-line " role="presentation"> if ((vma->vm_end - vma->vm_start) > SZ_4M)</pre>

<pre class=" CodeMirror-line " role="presentation"> vma->vm_end = vma->vm_start + SZ_4M;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 内核空间的开辟的首地址</pre>

<pre class=" CodeMirror-line " role="presentation"> area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);</pre>

<pre class=" CodeMirror-line " role="presentation"> // 内核空间的首地址</pre>

<pre class=" CodeMirror-line " role="presentation"> proc->buffer = area->addr;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 计算用户空间与内核空间的偏移量</pre>

<pre class=" CodeMirror-line " role="presentation"> proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 按页去开辟内存</pre>

<pre class=" CodeMirror-line " role="presentation"> proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);</pre>

<pre class=" CodeMirror-line " role="presentation"> // 计算大小 1M - 8K</pre>

<pre class=" CodeMirror-line " role="presentation"> proc->buffer_size = vma->vm_end - vma->vm_start;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 开辟映射物理页,一次拷贝的关键在这个方法</pre>

<pre class=" CodeMirror-line " role="presentation"> if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> buffer->free = 1;</pre>

<pre class=" CodeMirror-line " role="presentation"> proc->files = get_files_struct(current);</pre>

<pre class=" CodeMirror-line " role="presentation"> proc->vma = vma;</pre>

<pre class=" CodeMirror-line " role="presentation"> proc->vma_vm_mm = vma->vm_mm;</pre>

<pre class=" CodeMirror-line " role="presentation"> return ret;</pre>

<pre class=" CodeMirror-line " role="presentation">}</pre></wiz_code_mirror>

<wiz_code_mirror><pre class=" CodeMirror-line " role="presentation">static int binder_update_page_range(struct binder_proc *proc, int allocate,</pre>

<pre class=" CodeMirror-line " role="presentation"> void *start, void *end,</pre>

<pre class=" CodeMirror-line " role="presentation"> struct vm_area_struct *vma)</pre>

<pre class=" CodeMirror-line " role="presentation">{</pre>

<pre class=" CodeMirror-line " role="presentation"> void *page_addr;</pre>

<pre class=" CodeMirror-line " role="presentation"> unsigned long user_page_addr;</pre>

<pre class=" CodeMirror-line " role="presentation"> struct vm_struct tmp_area;</pre>

<pre class=" CodeMirror-line " role="presentation"> struct page **page;</pre>

<pre class=" CodeMirror-line " role="presentation"> struct mm_struct *mm;</pre>

<pre class=" CodeMirror-line " role="presentation"> if (end <= start)</pre>

<pre class=" CodeMirror-line " role="presentation"> return 0;</pre>

<pre class=" CodeMirror-line " role="presentation"> if (vma)</pre>

<pre class=" CodeMirror-line " role="presentation"> mm = NULL;</pre>

<pre class=" CodeMirror-line " role="presentation"> else</pre>

<pre class=" CodeMirror-line " role="presentation"> mm = get_task_mm(proc->tsk);</pre>

<pre class=" CodeMirror-line " role="presentation"> // 判断是不是释放 1 ,开辟(物理内存)</pre>

<pre class=" CodeMirror-line " role="presentation"> if (allocate == 0)</pre>

<pre class=" CodeMirror-line " role="presentation"> goto free_range;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 这里只有一个物理页</pre>

<pre class=" CodeMirror-line " role="presentation"> for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {</pre>

<pre class=" CodeMirror-line " role="presentation"> int ret;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 在物理内存上开辟一个页</pre>

<pre class=" CodeMirror-line " role="presentation"> *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);</pre>

<pre class=" CodeMirror-line " role="presentation"> if (*page == NULL) {</pre>

<pre class=" CodeMirror-line " role="presentation"> pr_err("%d: binder_alloc_buf failed for page at %p\n",</pre>

<pre class=" CodeMirror-line " role="presentation"> proc->pid, page_addr);</pre>

<pre class=" CodeMirror-line " role="presentation"> goto err_alloc_page_failed;</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> tmp_area.addr = page_addr;</pre>

<pre class=" CodeMirror-line " role="presentation"> tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 把开辟好的这一个页映射到内核空间</pre>

<pre class=" CodeMirror-line " role="presentation"> ret = map_vm_area(&tmp_area, PAGE_KERNEL, page);</pre>

<pre class=" CodeMirror-line " role="presentation"> // 根据偏移量计算用户空间的首地址</pre>

<pre class=" CodeMirror-line " role="presentation"> user_page_addr =</pre>

<pre class=" CodeMirror-line " role="presentation"> (uintptr_t)page_addr + proc->user_buffer_offset;</pre>

<pre class=" CodeMirror-line " role="presentation"> // 把开辟好的这一个页映射到用户空间</pre>

<pre class=" CodeMirror-line " role="presentation"> ret = vm_insert_page(vma, user_page_addr, page[0]);</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> return 0;</pre>

<pre class=" CodeMirror-line " role="presentation">// 释放的逻辑</pre>

<pre class=" CodeMirror-line " role="presentation">free_range:</pre>

<pre class=" CodeMirror-line " role="presentation"> for (page_addr = end - PAGE_SIZE; page_addr >= start;</pre>

<pre class=" CodeMirror-line " role="presentation"> page_addr -= PAGE_SIZE) {</pre>

<pre class=" CodeMirror-line " role="presentation"> page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];</pre>

<pre class=" CodeMirror-line " role="presentation"> if (vma)</pre>

<pre class=" CodeMirror-line " role="presentation"> zap_page_range(vma, (uintptr_t)page_addr +</pre>

<pre class=" CodeMirror-line " role="presentation"> proc->user_buffer_offset, PAGE_SIZE, NULL);</pre>

<pre class=" CodeMirror-line " role="presentation">err_vm_insert_page_failed:</pre>

<pre class=" CodeMirror-line " role="presentation"> unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);</pre>

<pre class=" CodeMirror-line " role="presentation">err_map_kernel_failed:</pre>

<pre class=" CodeMirror-line " role="presentation"> __free_page(*page);</pre>

<pre class=" CodeMirror-line " role="presentation"> *page = NULL;</pre>

<pre class=" CodeMirror-line " role="presentation">err_alloc_page_failed:</pre>

<pre class=" CodeMirror-line " role="presentation"> ;</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation">err_no_vma:</pre>

<pre class=" CodeMirror-line " role="presentation"> if (mm) {</pre>

<pre class=" CodeMirror-line " role="presentation"> up_write(&mm->mmap_sem);</pre>

<pre class=" CodeMirror-line " role="presentation"> mmput(mm);</pre>

<pre class=" CodeMirror-line " role="presentation"> }</pre>

<pre class=" CodeMirror-line " role="presentation"> return -ENOMEM;</pre>

<pre class=" CodeMirror-line " role="presentation">}</pre></wiz_code_mirror>

为啥只映射一个页呢?传 20K 数据怎么办?

如何避免内存的浪费?

问题:mmap是在哪个过程中调用的???

一次拷贝的原理:和linux有关,内存,页,物理地址和虚拟地址有关!

开辟一个页映射到用户空间

开辟一个页映射到内核空间

[图片上传失败...(image-294baf-1642735792543)]

[图片上传失败...(image-ef40c2-1642735792543)]

[图片上传失败...(image-51a969-1642735792543)]

[图片上传失败...(image-b2c581-1642735792543)]

一个用户进程包含:用户空间和内核空间????

问题: 为什么会同时使用进程虚拟地址空间和内核虚拟地址空间来映射同一个物理页面呢?
同一个物理页面,一方映射到进程虚拟地址空间,一方映射到内核虚拟地址空间,这样,进程和内核之间就可以减少一次内存拷贝了,提到了进程间通信效率

问题:一页是多少?

4k

问题:这里用户空间mmap (1M-8K)的空间,为什么要减去8K,而不是直接用1M?

系统定义:BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2) = (1M- sysconf(_SC_PAGE_SIZE) * 2)

这里的8K,其实就是两个PAGE的SIZE, 物理内存的划分是按PAGE(页)来划分的,一般情况下,一个Page的大小为4K。

————————————————

3.binder_ioctl 源码分析 (代码异常复杂)--->发命令

binder_ioctl()函数负责在两个进程间收发IPC数据和IPC reply数据,Native C\C++ 层传入不同的cmd和数据,根据cmd的值,进行相应的处理并返回

参数:

filp:文件描述符

cmd:ioctl命令

arg:数据类型

binder 驱动并不提供常规的 read()、write() 等文件操作!

Binder主要通过ioctl命令对底层进行调用,不会直接用read和write对其进行操作

ioctl 是操作驱, binder_ioctl

ioctl 命令有 BINDER_WRITE_READ (binder 读写交互)、BINDER_SET_CONTEXT_MGR(servicemanager进程成为上下文管理者)、BINDER_SET_MAX_THREADS(设置最大线程数)、BINDER_VERSION(获取 binder 版本)。有两个核心复杂方法 binder_thread_write 和 binder_thread_read

binder 驱动作为接收方 binder_ioctl() 方法接收的命令,还有一些与之对应的 BR_ 开头的命令,由 binder 驱动主动发出,比如 BR_TRANSACTION、BR_REPLY,在一次 IPC 调用中是这样应用的

记住流程图

[图片上传失败...(image-44110d-1642735792543)]

[图片上传失败...(image-1d7486-1642735792543)]

Binder协议中BC与BR的区别

BC与BR主要是标志数据及Transaction流向,其中BC是从用户空间流向内核,而BR是从内核流线用户空间,比如Client向Server发送请求的时候,用的是BC_TRANSACTION,当数据被写入到目标进程后,target_proc所在的进程被唤醒,在内核空间中,会将BC转换为BR,并将数据与操作传递该用户空间。

4).close 是关闭驱动, 和 binder_colse 方法就可以了

问题 2.binder驱动是怎么帮我们找到servermanager进程的

handle = 0 , 驱动层会创建一个静态的变量 binder_context_mgr_node

里面有4给红黑树,存放了很多信息!

问题3.servermanager进程是怎进入等待,怎么被唤醒的?

我自己的理解:开始servermanager在等待,然后因为客户端往binder写入了数据,然后binder驱动往serverManager发送了

东西,之后就把它唤醒了!

等待是在 wait 队列上等,条件是 todo 队列是否为空,

客户端找到 tagert_prco 了之后把数据拷贝到目标进程,什么时候去找?这个代码在哪里?

找到了tagert_prco之后就能找到等待队列!****

同时把数据拷贝到目标进程往目标进程写入处理的命令,

然后往自己进程写入一个接受请求的命令(让自己进入等待),

最后唤醒服务进程的wait队列,这个时候服务进程被唤醒就会继续往下处理todo队列请求。

问题4.数据是如何传递的,handle和type是怎么被计算和管理的

binder 驱动会判断 type 是什么,然后呢会往自己的进程挂上一个 binder_node ,

会往目标进程挂上两个 binder_node 一个挂在以 handle 值为 key 的红黑树上,一个挂在以 cookie 为 key 的红黑树上,handle 值是根据红黑树的数据了累加的。

主要在: 驱动层有两个核心复杂方法 binder_thread_write 和 binder_thread_read

// ServiceManager 进程:获取判断 binder 驱动版本号是否一致

if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) || (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {

goto fail_open;

}

// ServiceManager 进程:让 ServiceManager 进程成为管理者

int binder_become_context_manager(struct binder_state *bs)

{

return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);

}

// ServiceManager 进程:binder 线程进入循环等待

readbuf[0] = BC_ENTER_LOOPER;

binder_write(bs, readbuf, sizeof(uint32_t));

// ServiceManager 进程:进入循环不断的读写 binder 内容

for (;;) {

bwr.read_size = sizeof(readbuf);

bwr.read_consumed = 0;

bwr.read_buffer = (uintptr_t) readbuf;

res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

...

}

// media 进程:添加 MediaPlayerService 服务

do {

// 通过 ioctl 不停的读写操作,跟 Binder Driver 进行通信,转发给 ServiceManager 进程

if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)

err = NO_ERROR;

...

} while (err == -EINTR); //当被中断,则继续执行

**********整体的一个过程:**

1. 获取 Binder 驱动版本

2. 成为 Binder 驱动管理者

3. ServiceManager 进程进入循环等待

4. Binder 驱动添加系统服务

5. ServiceManager 进程处理添加请求

[图片上传失败...(image-acea45-1642735792536)]

ServiceManager需要查询吗?

  • ServiceManager是比较特殊的服务,所有应用都能直接使用,因为ServiceManager对于Client端来说Handle句柄是固定的,都是0,所以ServiceManager服务并不需要查询,可以直接使用。

2. app 客户请求服务过程分析

[图片上传失败...(image-1af274-1642735792535)]

服务端查询返回结果分析

[图片上传失败...(image-92d60e-1642735792536)]

3.1. 驱动层的客户端数据是怎么被回复的

[图片上传失败...(image-b58b2a-1642735792536)]

服务端之所以能回复客户端是因为 之前在请求的时候就已经记录 from 从哪里来,也就是已经记录了客户端的 binder_proc 对象,这里 getService 返回给客户端的是一个 flat_binder_object ,

[图片上传失败...(image-55460d-1642735792536)]

3.2. 客户端是怎么进入等待的

客户端是因为在请求服务端的时候驱动层会写入一个 BINDER_WORK_TRANSACTION_COMPLETE ,

然后会清空 writeData , 从新进入驱动层,进入读方法,因为todo队列里面没东西进入等待,等待服务端执行完毕唤醒客户端

4. 客户端等待响应过程分析

1641954543296-nu2.png

内核分析binder:

https://cloud.tencent.com/developer/article/1689220(一系列的文章 )

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,200评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,526评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,321评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,601评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,446评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,345评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,753评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,405评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,712评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,743评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,529评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,369评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,770评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,026评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,301评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,732评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,927评论 2 336

推荐阅读更多精彩内容