博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Binder机制情景分析之transaction_stack
阅读量:5885 次
发布时间:2019-06-19

本文共 5384 字,大约阅读时间需要 17 分钟。

一. 概述

这里以注册服务为例,当led_control_service请求注册服务时是通过handle找到的ServiceManager,但是ServiceManager是如何找到led_control_service进行回复的呢?

答:这里驱动中用到了一个传送栈记录了发送和接收进程,线程的信息; 接下来我们讲下具体的流程;

二. transaction_stack的来源

主要代码在binder_transaction()binder_thread_read()中;

2.1 BC_TRANSACTION

led_control_service调用BC_TRANSACTION开始,此时驱动会调用binder_transaction()函数;

如下是该函数中的一段代码,这段代码前面在深入驱动时未讲解:

if (!reply && !(tr->flags & TF_ONE_WAY))                  ①        t->from = thread;    else        t->from = NULL;    t->sender_euid = task_euid(proc->tsk);    t->to_proc = target_proc;                                 ②    t->to_thread = target_thread;    t->code = tr->code;    t->flags = tr->flags;    t->priority = task_nice(current);
①: 判断是否需要回复,需要则记录下当前进程的thread信息;
②: 记录下要目标进程信息和线程信息;

这里tstruct binder_transaction 结构,和transaction_stack类型相同;

struct binder_transaction {    int debug_id;    struct binder_work work;    struct binder_thread *from;                                 ①    struct binder_transaction *from_parent;                     ②    struct binder_proc *to_proc;                                ③    struct binder_thread *to_thread;                            ④    struct binder_transaction *to_parent;                       ⑤    unsigned need_reply:1;    .....};
①: 记录发送线程信息;
②: 记录发送线程的传输栈的父栈;
③: 记录接收进程的进程信息;
④: 记录接收线程的进程信息;
⑤: 记录接收进程的传输栈的父栈;

此时t变量中的主要信息如下:

transaction_starck
from led_control_service'thread
to_proc ServiceManager
to_thread ServiceManager'thread

此时已经记录下了接收方的信息了,继续往下看(binder_transaction()函数内):

if (reply) {        BUG_ON(t->buffer->async_transaction != 0);        binder_pop_transaction(target_thread, in_reply_to);    } else if (!(t->flags & TF_ONE_WAY)) {                     ①        BUG_ON(t->buffer->async_transaction != 0);        t->need_reply = 1;        t->from_parent = thread->transaction_stack;            ②        thread->transaction_stack = t;                         ③    } 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;        } else            target_node->has_async_transaction = 1;    }
①: 判断是否需要回复;
②: 这里一个入栈操作将当前线程的传输压入,但是
thread->transaction_stack此时为
NULL,因为第一次执行前面没有赋值;
③: 接下给当前线程的传输栈赋值,;

此时led_control_service线程的传输栈信息如下:

transaction_starck
from led_control_service'thread
to_proc ServiceManager
to_thread ServiceManager'thread
from_parent NULL

2.2 BR_TRANSACTION

前面驱动讲解过,在led_contrl_server执行binder_transaction()后,ServiceManager的进程会被唤醒,则ServiceManager会从休眠中醒来继续执行binder_thread_read;

binder_thread_read有段代码如下:

if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {        t->to_parent = thread->transaction_stack;                ①        t->to_thread = thread;                                   ②        thread->transaction_stack = t;                           ③    } else {        t->buffer->transaction = NULL;        kfree(t);        binder_stats_deleted(BINDER_STAT_TRANSACTION);    }
①: 将当前线程传输栈入栈;
②: 记录接收进程的信息,也就是自己本身,因为他本身是被唤醒的;
③: 当前线程传输栈记录下线程信息;

这里的t是从带处理事务的链表中取出来的,也就是前面led_control_service挂到ServiceManager的todo链表上;

ServiceManager线程的传输栈的信息如下:

transaction_starck
from led_control_service'thread
to_proc ServiceManager
to_thread ServiceManager'thread
from_parent NULL
to_parent NULL

到这里线程传输栈的数据来源和构造讲完;

三. transaction_stack的使用

ServiceManager的用户态在处理完注册信息后,调用BC_REPLAY命令回复注册结果给led_control_service,此时驱动中也是调用到binder_transaction();

binder_transaction代码片如下:

if (reply) {        in_reply_to = thread->transaction_stack;                   ①        ALOGD("%s:%d,%d %s in_reply_to = thread->transaction_stack\n", proc->tsk->comm, proc->pid, thread->pid, __FUNCTION__);        if (in_reply_to == NULL) {            binder_user_error("%d:%d got reply transaction with no transaction stack\n",                      proc->pid, thread->pid);            return_error = BR_FAILED_REPLY;            goto err_empty_call_stack;        }        binder_set_nice(in_reply_to->saved_priority);        if (in_reply_to->to_thread != thread) {                    ②            return_error = BR_FAILED_REPLY;            in_reply_to = NULL;            goto err_bad_call_stack;        }        thread->transaction_stack = in_reply_to->to_parent;        ③        target_thread = in_reply_to->from;                         ④        if (target_thread == NULL) {            return_error = BR_DEAD_REPLY;            goto err_dead_binder;        }        if (target_thread->transaction_stack != in_reply_to) {            return_error = BR_FAILED_REPLY;            in_reply_to = NULL;            target_thread = NULL;            goto err_dead_binder;        }        target_proc = target_thread->proc;    } else {
①: 用个临时变量记录下当前线程额传输栈信息;
②: 判断下接收的线程是否为自己本身,如果不是则出错,看迷糊的可以看下再2.2节;
③: 一次出栈操作,此时
thread->transaction_stack值为
NULL了;
④: 获取到目标线程,
from中记录着发送线程
led_contol_service的信息;

这里就通过ServiceManager在read的时候入栈的传送栈信息,获取到发送进程的信息,即回复进程的信息;

接着往下看:

if (reply) {        BUG_ON(t->buffer->async_transaction != 0);        binder_pop_transaction(target_thread, in_reply_to);    ①    } else if (!(t->flags & TF_ONE_WAY)) {        BUG_ON(t->buffer->async_transaction != 0);        t->need_reply = 1;        t->from_parent = thread->transaction_stack;        thread->transaction_stack = t;    } else {      ...    }
①: 一个出栈操作;

此时led_control_service的传输栈也指向了父栈,即为空且清除了in_reply_tofrom的信息;

上面那部ServiceManager进程已经知道此时要发送给谁,到此该问题就完美解答了;

转载地址:http://selix.baihongyu.com/

你可能感兴趣的文章
Nomad添加acl认证
查看>>
“TI门外汉”网路知识笔记一 OSI参考模型
查看>>
你不需要jQuery(五)
查看>>
DatanodeDescriptor说明
查看>>
ServlertContext
查看>>
Python WOL/WakeOnLan/网络唤醒数据包发送工具
查看>>
sizeof(long)
查看>>
pxe网络启动和GHOST网克
查看>>
2.5-saltstack配置apache
查看>>
django数据库中的时间格式与页面渲染出来的时间格式不一致的处理
查看>>
增强myEclipse的提示功能
查看>>
[翻译]Protocol Buffer 基础: C++
查看>>
runloop与线程的关系
查看>>
[Bzoj2246]迷宫探险(概率+DP)
查看>>
详解消息队列的设计与使用
查看>>
使用Sqoop从mysql向hdfs或者hive导入数据时出现的一些错误
查看>>
控制子窗口的高度
查看>>
处理 Oracle SQL in 超过1000 的解决方案
查看>>
Alpha线性混合实现半透明效果
查看>>
chkconfig 系统服务管理
查看>>