在Binder IPC通信过程中,进程间通信都要先通过向Binder驱动发送BC_XXX命令,然后Binder 驱动稍做处理后通过对应的BR_XXX将命令转给给目标进程。

如果有返回值,进程也是先将返回结果以BC_REPLY的形式先发给Binder驱动,然后通过驱动以BR_REPLY命令转发。

PS:从Driver发出的命令以BR开始,而发往Driver的命令以BC开头。

Binder1通过BC_Transaction将通信数据发到Binder Driver,Binder Driver通过BR_Transaction将命令转发给Binder2去处理,最后都是通过BC_REPLY的命令将返回结果传回Binder Driver,Binder Driver再通过BR_REPLY将返回结果转发给Binder1。

相关函数binder_transaction(...)

先看BC_TRANSACTION命令的处理过程(binder1->Driver)。

首先,第一步,Binder驱动判断当前命令接收方是Service Manager还是普通的Server端,判断依据是tr->target.handle.if (tr->target.handle == 0)   表示该命令是发送特殊结点,即Service Manager,这时else   针对一般情况,我们需要判断Binder驱动中有没有对应的结点引用,正常情况下应该是能够找到handle对应的Binder结点引用的。通过结点引用,我们就可以定位到处理命令的Binder结点(实体结点)。总之,我们要先知道这个命令是要发往何处。

else {if (tr->target.handle) {struct binder_ref *ref;ref = binder_get_ref(proc, tr->target.handle);if (ref == NULL) {binder_user_error("%d:%d got transaction to invalid handle\n",proc->pid, thread->pid);return_error = BR_FAILED_REPLY;goto err_invalid_target_handle;}target_node = ref->node;} else {target_node = binder_context_mgr_node;if (target_node == NULL) {return_error = BR_DEAD_REPLY;goto err_no_context_mgr_node;}}

有了Binder结点的信息,我们就可以知道它所处的进程了。

target_proc = target_node->proc;if (target_proc == NULL) {return_error = BR_DEAD_REPLY;goto err_dead_binder;}

然后会有一个安全检查,主要是判断这通信双方所处的进程能不能传输数据。

if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) {return_error = BR_FAILED_REPLY;goto err_invalid_target_handle;}

接下来,对于同步通信(即two way),且当前transaction_stack链表不为空的话(也即表示当前线程至少存在一个通信会话),那么尝试获取目标线程(即通信的另一方)。因为有可能之前通信双方已经建立了通信会话,这样的话就可以重复利用。

if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {struct binder_transaction *tmp;tmp = thread->transaction_stack;if (tmp->to_thread != thread) {binder_user_error("%d:%d got new transaction with bad transaction stack, transaction %d has target %d:%d\n",proc->pid, thread->pid, tmp->debug_id,tmp->to_proc ? tmp->to_proc->pid : 0,tmp->to_thread ?tmp->to_thread->pid : 0);return_error = BR_FAILED_REPLY;goto err_bad_call_stack;}while (tmp) {if (tmp->from && tmp->from->proc == target_proc)target_thread = tmp->from;tmp = tmp->from_parent;}}}

这样,我们就知道要唤醒哪个等待队列,如果通信的另一方已经存在处理的线程,那么将唤醒对应的线程处理。如果是第一次,那么先唤醒进程等待队列,由进程后续处理。

if (target_thread) {e->to_thread = target_thread->pid;target_list = &target_thread->todo;target_wait = &target_thread->wait;} else {target_list = &target_proc->todo;target_wait = &target_proc->wait;}

接下来,就是将Binder1通过BC_TRANSACTION传过来的一些数据(struct binder_transaction)复制到目标进程的buffer中,之后就可以将其发送目标进程了。

t = kzalloc(sizeof(*t), GFP_KERNEL);if (t == NULL) {return_error = BR_FAILED_REPLY;goto err_alloc_t_failed;}

同时,也要告诉当前进程BC_TRANSACTION命令已经发送完成,所以需要创建相应的数据结构struct binder_work实例:

tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);if (tcomplete == NULL) {return_error = BR_FAILED_REPLY;goto err_alloc_tcomplete_failed;}

接下来就是复制数据:

对于需要对方发送返回信息的情况,此时需要记录下发送方的信息:

if (!reply && !(tr->flags & TF_ONE_WAY))t->from = thread;elset->from = NULL;

还有其他的一些重要信息:

t->sender_euid = proc->tsk->cred->euid;t->to_proc = target_proc;t->to_thread = target_thread;t->code = tr->code;t->flags = tr->flags;t->priority = task_nice(current);

以上信息通信Binder驱动就能搞定,接下来进入实质阶段,即从发送方进程复制数据:

首先需要创建一个struct binder_buffer结构体实例,通信数据将存放在该结构体变量中。

t->buffer = binder_alloc_buf(target_proc, tr->data_size,tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));if (t->buffer == NULL) {return_error = BR_FAILED_REPLY;goto err_binder_alloc_buf_failed;}t->buffer->allow_user_free = 0;t->buffer->debug_id = t->debug_id;t->buffer->transaction = t;t->buffer->target_node = target_node;trace_binder_transaction_alloc_buf(t->buffer);if (target_node)binder_inc_node(target_node, 1, 0, NULL);

将发送方进程传递过来的信息复制过来:

offp = (binder_size_t *)(t->buffer->data +ALIGN(tr->data_size, sizeof(void *)));if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)tr->data.ptr.buffer, tr->data_size)) {binder_user_error("%d:%d got transaction with invalid data ptr\n",proc->pid, thread->pid);return_error = BR_FAILED_REPLY;goto err_copy_data_failed;}if (copy_from_user(offp, (const void __user *)(uintptr_t)tr->data.ptr.offsets, tr->offsets_size)) {binder_user_error("%d:%d got transaction with invalid offsets ptr\n",proc->pid, thread->pid);return_error = BR_FAILED_REPLY;goto err_copy_data_failed;}...off_end = (void *)offp + tr->offsets_size;

接下来的部分是BC_TRANSACTION命令处理的重点部分,即要将发送方命令数据中引用的一些Binder引用或实体找出来,并按如下规则处理:

如果当前传输给目标进程中包含一个本地binder对象,首先需要在Binder驱动中在当前进程创建一个实体结点(如果不存在的话)

fp = (struct flat_binder_object *)(t->buffer->data + *offp);switch (fp->type) {case BINDER_TYPE_BINDER:case BINDER_TYPE_WEAK_BINDER: {struct binder_ref *ref;struct binder_node *node = binder_get_node(proc, fp->binder);if (node == NULL) {node = binder_new_node(proc, fp->binder, fp->cookie);if (node == NULL) {return_error = BR_FAILED_REPLY;goto err_binder_new_node_failed;}node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);}

然后在目标进程中为其创建一个引用结点,并相应的修改fp的类型值:对于BINDER_TYPE_BINDER类型的结点,修改为BINDER_TYPE_HANDLE,而对于BINDER_TYPE_WEAK_BINDER,修改为BINDER_TYPE_WEAK_HANDLE。

ref = binder_get_ref_for_node(target_proc, node);if (ref == NULL) {return_error = BR_FAILED_REPLY;goto err_binder_get_ref_for_node_failed;}if (fp->type == BINDER_TYPE_BINDER)fp->type = BINDER_TYPE_HANDLE;elsefp->type = BINDER_TYPE_WEAK_HANDLE;fp->handle = ref->desc;binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,&thread->todo);

如果当前传输给目标进程中包含一个远程binder对象的句柄引用,那以首先获得它对应的Binder结点引用对象,

case BINDER_TYPE_HANDLE:case BINDER_TYPE_WEAK_HANDLE: {struct binder_ref *ref = binder_get_ref(proc, fp->handle);if (ref == NULL) {binder_user_error("%d:%d got transaction with invalid handle, %d\n",proc->pid,thread->pid, fp->handle);return_error = BR_FAILED_REPLY;goto err_binder_get_ref_failed;}

接下来需要判断它对应的实体结点是否处于目标进程当中,如果是的话,则相应地也需要修改fp的类型值:对于BINDER_TYPE_HANDLE,修改为BINDER_TYPE_BINDER,对于BINDER_TYPE_WEAK_BINDER,修改为BINDER_TYPE_WEAK_BINDER。

if (ref->node->proc == target_proc) {if (fp->type == BINDER_TYPE_HANDLE)fp->type = BINDER_TYPE_BINDER;elsefp->type = BINDER_TYPE_WEAK_BINDER;fp->binder = ref->node->ptr;fp->cookie = ref->node->cookie;binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);trace_binder_transaction_ref_to_node(t, ref);binder_debug(BINDER_DEBUG_TRANSACTION,"        ref %d desc %d -> node %d u%016llx\n",ref->debug_id, ref->desc, ref->node->debug_id,(u64)ref->node->ptr);}

如果对应的实体结点不是处于目标进程当中,那么则需要在目标进程中获取或创建该实体结点的引用对象。

else {struct binder_ref *new_ref;new_ref = binder_get_ref_for_node(target_proc, ref->node);if (new_ref == NULL) {return_error = BR_FAILED_REPLY;goto err_binder_get_ref_for_node_failed;}fp->handle = new_ref->desc;binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);trace_binder_transaction_ref_to_ref(t, ref,new_ref);

当然,最后都需要增加引用计数。

如果传输的数据包含一个文件描述符,则需要做的事情就是在目标进程中分配一个新的文件描述,并与对应的struct file类型的对象关联。这样两个进程中的文件描述符关联的文件是同一个文件了。此时,需要修改fp的handle值为新的文件描述符。

file = fget(fp->handle);if (file == NULL) {binder_user_error("%d:%d got transaction with invalid fd, %d\n",proc->pid, thread->pid, fp->handle);return_error = BR_FAILED_REPLY;goto err_fget_failed;}...target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);if (target_fd < 0) {fput(file);return_error = BR_FAILED_REPLY;goto err_get_unused_fd_failed;}task_fd_install(target_proc, target_fd, file);.../* TODO: fput? */fp->handle = target_fd;

处理完这些事情后,接下来就是准备提交到目标进程或线程的工作队列中去执行了。有一点需要注意的是,如果目标结点上已经存在了一个未完成的异步处理,则接下来的异步transaction就会暂时分流到目标结点的async_todo等待队列中去,延后处理。

else {BUG_ON(target_node == NULL);BUG_ON(t->buffer->async_transaction != 1);if (target_node->has_async_transaction) {target_list = &target_node->async_todo;target_wait = NULL;} elsetarget_node->has_async_transaction = 1;}

向当前线程反馈信息:

tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;list_add_tail(&tcomplete->entry, &thread->todo);

唤醒目标进程等待队列:

t->work.type = BINDER_WORK_TRANSACTION;list_add_tail(&t->work.entry, target_list);...if (target_wait)wake_up_interruptible(target_wait);

转载于:https://my.oschina.net/fuyajun1983cn/blog/263819

binder IPC TRANSACTION过程分析(BC_TRANSACTION-Binder Driver)相关推荐

  1. 深入理解Binder机制4-bindService过程分析

    一.概述 1.1 Binder架构 Android内核基于Linux系统,而Linux系统进程间通信方式有很多,如管道,共g享内存,信号,信号量,消息队列,套接字.而Android为什么要用binde ...

  2. Android Binder IPC机制

    之前有很多人问过我TaintDroid的内容,所以一点点吧涉及的思路整理一下. 今天又看了一遍,确实需要很多的技术支持以及扎实的功底,不管是Java的还是Android,从系统底层一直到顶层,涉及的知 ...

  3. Binder跨进程通信原理(三):Binder IPC实现原理

    1. 动态内核可加载模块 && 内存映射 正如上一章所说, 跨进程通信是需要内核空间做支持的. 传统的 IPC 机制如 管道, Socket, 都是内核的一部分, 因此通过内核支持来实 ...

  4. 【Android Binder 系统】一、Binder 系统核心 ( IPC 进程间通信 | RPC 远程调用 )

    文章目录 一.Binder 系统两个核心 二.IPC 进程间通信 三.RPC 远程过程调用 一.Binder 系统两个核心 Binder 系统 最重要的两个核心是 IPC 和 RPC ; IPC ( ...

  5. Binder IPC的权限控制

    PS:个人理解:当进程1通过Binder调用组件2时,会将进程1的pid及uid赋给组件2,并检测进程1的pid及uid是否有权限调用组件2.而后组件2需要调用组件3,此时组件2保存的pid及uid为 ...

  6. Android开发艺术探索--第二章IPC机制(2)之Binder

    最近在拜读任主席的Android开发艺术探索,现在看了一半,再回头看前面的,感觉跟没有看一样,所以还是把知识点总结一下吧,这一节咱们来讲一下IPC中的Binder 直观来说,Binder是Androi ...

  7. Binder IPC

    1 前言 先复制一段来自于android官方文档的文字 https://source.android.google.cn/devices/architecture/hidl/binder-ipc 一直 ...

  8. Android10.0 Binder通信原理(九)-AIDL Binder示例

    摘要:本节主要来讲解Android10.0 Binder中如何使用AIDL 阅读本文大约需要花费20分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Android的平 ...

  9. 【Binder 机制】Native 层 Binder 机制分析 ( binder_loop | svcmgr_handler | binder.c | binder_parse )

    文章目录 前言 一.binder_loop 方法调用 二.binder_loop 方法参数 svcmgr_handler 三.binder_loop 方法 四.binder_parse 方法 前言 在 ...

最新文章

  1. 一些改进模型速度/精度的工程方法
  2. 如何从0-1构建自己的”pytorch“(自己专属的深度学习框架)——part01
  3. 【iOS】控件截图、MP4格式视频流和m3u8格式视频流截取某一帧功能的实现
  4. Python 技术篇-百度语音API鉴权认证获取Access Token实例演示
  5. linux 下 upx 脱壳笔记
  6. 升级PowerShell至4.0版本
  7. html怎么无损插入背景音乐,HTML插入背景音乐方法【全】
  8. Vmware 中Linux中NAT网络异常解决方法
  9. 跑了10千米,再一次伤了膝盖
  10. 阿里云宣布与Facebook达成合作 让AI开发更简单
  11. 山大往年自招计算机系试题,2018山大自招经验分享
  12. 【数据结构和算法笔记】图的相关概念(有向图,无向图......)
  13. 【amp;#9733;】SPF(Dijkstra)算法完美教程
  14. hevc参考代码matlab版,HEVC部分源码剖析
  15. 随机森林(Random Forest)算法原理总结
  16. Error: Flash Download failed - Target DLL has been cancelled报错
  17. 区块链技术在物联网中的应用概述
  18. 爬虫python教程百度云_【宝宝学爬】宝宝几个月会爬,婴儿几个月会爬,宝宝几个月会走路 - 妈妈网百科...
  19. 面经合集(包含python、mysql、linux、测试等内容)
  20. 万能 Makefile 模板

热门文章

  1. linux 下批量修改文件的编码
  2. windows快捷启动命令
  3. 借东西的小人阿莉埃蒂
  4. npm ERR! asyncWrite is not a function
  5. Win7启动显示无法加载用户配置文件
  6. Bmob图片上传遇到的坑
  7. MySQL主从配置的一些总结
  8. PostgreSQL schemaless 的实现(类mongodb collection)
  9. [LeetCode]--118. Pascal#39;s Triangle
  10. 内核compiler.h的学习