首页 > 其他分享 >Binder学习笔记-2——binder优先级传递

Binder学习笔记-2——binder优先级传递

时间:2022-11-02 23:25:33浏览次数:75  
标签:node 优先级 prio Binder priority binder policy

基于Linux-5.10,有补充Linux-5.15的说明

一、优先级相关结构体和成员

1. struct binder_priority

struct binder_priority { //binder_internal.h
    unsigned int sched_policy;
    int prio;
};

此结构用于对服务的binder线程的调度策略和优先级进行描述。

sched_policy: 此成员表示调度策略,binder驱动只支持 SCHED_NORMAL、SCHED_BATCH、SCHED_FIFO、SCHED_RR 这几类调度策略的继承。
prio: 表示内核优先级,对于 SCHED_NORMAL 是[100..139],对于 FIFO/RT 是[0..99]。

在内核空间,此结构的prio取值[0, 139], sched_policy取内核优先级对应的策略,见 binder_proc_transaction() 中的 node_prio 变量。

2. struct binder_node

struct binder_node { //binder_internal.h
    ...
    struct {
        ...
        u8 sched_policy:2;
        u8 inherit_rt:1;
        u8 min_priority;
    };
};

sched_policy: 节点的binder服务线程最小优先级对应的调度策略,初始化后就不再变化了。

inherit_rt: 是否允许本服务的binder线程从caller那里继承RT调度策略。若不允许但caller又是RT线程,那么就将响应的binder线程设置为CFS 120优先级。
min_priority: 节点的binder服务线程最小调度优先级,初始化后就不再变化了。相当于将向binder线程传递的最低优先级钳位到这个值上,见 binder_transaction_priority().

是在创建binder服务时,根据用户传入的 flat_binder_object::flags 成员进行初始化,具体初始化位置在 binder_init_node_ilocked() 中:

static struct binder_node *binder_init_node_ilocked(struct binder_proc *proc,
    struct binder_node *new_node, struct flat_binder_object *fp)
{
    struct binder_node *node = new_node;
    __u32 flags = fp ? fp->flags : 0; //优先级相关配置来自flags成员
    s8 priority;

    ...
    priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
    node->sched_policy = (flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK) >> FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT;
    /*用户配置下来的是用户优先级,需要转换 */
    node->min_priority = to_kernel_prio(node->sched_policy, priority);
    node->inherit_rt = !!(flags & FLAT_BINDER_FLAG_INHERIT_RT);
    ...
}

调用路径:

binder_thread_write  //1.注册binder服务时
    binder_transaction
        binder_translate_binder
            binder_translate_binder
            binder_ioctl_set_ctx_mgr //2.将自己设置为servicemanage时
                binder_new_node
                    binder_init_node_ilocked

假设服务还没注册,就还不存在于 proc->nodes 红黑树上,此时就是对参数 new_node 进行初始化。可以看到优先级和调度策略都是来自 flat_binder_object::flags 成员,根据用户传下来的调度策略和优先级转化成一个内核优先级,保存在 node->min_priority 成员中。

开机启动过程中:
(1) 对于 min_priority 成员,主要是主线程是 system_server 的线程(超大量)创建 binder_node 时传参 min_priority=120,其它如主线程是 com.android.phone、MmsServiceMain、idmap2d、com.qti.ltebc 等也有传
min_priority=120 的情况,其它绝大多数线程传的 min_priority=139。而且只有120和139这两个配置值。
(2) 对于 sched_policy 成员,全部传的都是0(SCHED_NORMAL)。
(3) 对于 inherit_rt 成员,极个别的传的是1,比如主线程是 perf-hal-servic、m.android.phone 等的线程,其它的都是0。

在正常使用手机的过程中,也会不停的创建binder node。

3. struct binder_proc

struct binder_proc { //binder_internal.h
    ...
    struct binder_priority default_priority;
};

default_priority: 在调用 binder_open() 时进行赋值,若调用者是CFS或RT线程,直接赋值为调用者的优先级,若是不支持的调度策略则赋值为prio=120的CFS优先级。

static int binder_open(struct inode *nodp, struct file *filp)
{
    ...
    if (binder_supported_policy(current->policy)) {
        proc->default_priority.sched_policy = current->policy;
        proc->default_priority.prio = current->normal_prio;
    } else {
        proc->default_priority.sched_policy = SCHED_NORMAL;
        proc->default_priority.prio = NICE_TO_PRIO(0);
    }
    ...
}

开机启动过程中,一般都是主线程执行 binder_open() 的动作,但是也有个别特殊情况,比如辅线程 PowerManagerSer(主线程system_server)、surfaceflinger 也执行了打开binder设备节点的操作。

开机启动过程中:
(1) 对于 default_priority.sched_policy 全部赋值为0(SCHED_NORMAL)
(2) 对于 default_priority.prio 绝大多数都是120,只有极个别是116,104,97等。

在正常使用机器的过程中,也会有不断打开binder节点的动作。


4. struct binder_transaction

struct binder_transaction {
    ...
    struct binder_priority    priority;
    struct binder_priority    saved_priority;
    bool    set_priority_called;
    ...
}

priority: 对于同步传输赋值为发起传输的caller的优先级(虽然没有区分reply,但是reply没有使用这个成员)。对异步传输赋值为目标进程的 target_proc->default_priority。若binder不支持
caller的调度策略,也赋值为target_proc->default_priority,见 binder_transaction()。
saved_priority: 在将要改变server的binder线程的优先级时,将其保存在这个成员中,以便之后进行恢复。见发起传输时的 binder_transaction_priority() 和 binder_transaction() 的reply部分。
set_priority_called: 在 binder_transaction_priority() 中唯一判断和设置。在client端发起传输和service的binder线程读取传输时都有设置binder优先级动作,这个成员标记针对当前 transaction t 的优先级传递是否已经设置了。若选到了thread,则在发起传输的caller进行传输进行时进行设置,若是没有选到thread,则在binder线程读取时进行设置。

//发起设置:
binder_proc_transaction
    struct binder_priority node_prio;
    node_prio.prio = node->min_priority;
    node_prio.sched_policy = node->sched_policy;
    /* 若是选到了thread,挂在thread->todo链表上是才会设置,挂在proc->todo和node->async_todo上时都不会设置 */
    if (thread) {
        binder_transaction_priority(thread->task, t, node_prio, node->inherit_rt);
        binder_enqueue_thread_work_ilocked(thread, &t->work);
    }

//读取设置:
binder_thread_read
    /* 此判断表示是服务端读取 */
    if (t->buffer->target_node) {
        struct binder_priority node_prio;
        node_prio.sched_policy = target_node->sched_policy;
        node_prio.prio = target_node->min_priority;
        binder_transaction_priority(current, t, node_prio, target_node->inherit_rt);
    }

5. binder_pri_desc/binder_pri_ptr_cookie

struct binder_pri_desc { //uapi/binder.h
    __s32 priority;
    __u32 desc;
};

struct binder_pri_ptr_cookie { //uapi/binder.h
    __s32 priority;
    binder_uintptr_t ptr;
    binder_uintptr_t cookie;
};

对应 binder 优先级相关 ioctl 设置的参数,目前还不支持设置。

6. flat_binder_object::flags

enum flat_binder_object_shifts {
    FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT = 9,
};

enum flat_binder_object_flags {
    ...
    FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff,
    FLAT_BINDER_FLAG_SCHED_POLICY_MASK = 3U << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT,
    FLAT_BINDER_FLAG_INHERIT_RT = 0x800,
    ...
};

此结构用来定义户空间设置下来的 flat_binder_object::flags 成员各个bit位的含义,在注册服务创建 binder_node 时在 binder_init_node_ilocked() 函数中进行解析。

FLAT_BINDER_FLAG_PRIORITY_MASK:最小调度优先级的位掩码。这些位可用于设置进入该node的事务应运行的最低调度优先级。这些位中的有效值取决于@FLAT_BINDER_FLAG_SCHED_POLICY_MASK 中编码的调度策略。
SCHED_NORMAL/SCHED_BATCH,有效范围在 [-20..19] 之间,对于 SCHED_FIFO/SCHED_RR,该值可以在 [1..99] 之间运行。
FLAT_BINDER_FLAG_SCHED_POLICY_MASK: 调度策略的位掩码。这两位可用于设置该Node上的事务应运行的最小调度策略。这些匹配 UAPI 调度程序策略值,例如:0=SCHED_NORMAL, 1=SCHED_FIFO, 2=SCHED_RR, 3=SCHED_BATCH.
FLAT_BINDER_FLAG_INHERIT_RT: 节点是否继承RT策略。仅当设置时,对该Node的调用将继承调用者的实时调度策略(用于同步传输)。

 

二、优先级相关函数

1. 底层函数

static bool is_rt_policy(int policy)
{
    return policy == SCHED_FIFO || policy == SCHED_RR;
}

static bool is_fair_policy(int policy)
{
    return policy == SCHED_NORMAL || policy == SCHED_BATCH;
}

/* binder只支持RT和CFS */
static bool binder_supported_policy(int policy)
{
    return is_fair_policy(policy) || is_rt_policy(policy);
}

/* 内核优先级转用户优先级 */
static int to_userspace_prio(int policy, int kernel_priority)
{
    if (is_fair_policy(policy))
        return PRIO_TO_NICE(kernel_priority);
    else
        return MAX_USER_RT_PRIO - 1 - kernel_priority;
}
/* 用户优先级转内核优先级 */
static int to_kernel_prio(int policy, int user_priority)
{
    if (is_fair_policy(policy))
        return NICE_TO_PRIO(user_priority);
    else
        return MAX_USER_RT_PRIO - 1 - user_priority;
}

/* 被 binder_transaction_priority 唯一调用 */
static void binder_set_priority(struct task_struct *task, struct binder_priority desired)
{
    /* set的时候是需要verify的 */
    binder_do_set_priority(task, desired, /* verify = */ true);
}

static void binder_restore_priority(struct task_struct *task, struct binder_priority desired)
{
    /* restore的时候不需要verify的 */
    binder_do_set_priority(task, desired, /* verify = */ false);
}

2. binder_transaction_priority() 函数实现

static void binder_transaction_priority(struct task_struct *task,
                struct binder_transaction *t, struct binder_priority node_prio, bool inherit_rt)
{
    struct binder_priority desired_prio = t->priority;
    bool skip = false;

    /* 对此成员唯一判断和赋值路径 */
    if (t->set_priority_called)
        return;

    /* 保存到 saved_priority 成员中 */
    t->set_priority_called = true;
    t->saved_priority.sched_policy = task->policy;
    t->saved_priority.prio = task->normal_prio;

    trace_android_vh_binder_priority_skip(task, &skip);
    if (skip)
        return;

    /* 若目标是RT线程,但是又不允许继承RT,就改为prio=120的CFS优先级 */
    if (!inherit_rt && is_rt_policy(desired_prio.sched_policy)) {
        desired_prio.prio = NICE_TO_PRIO(0);
        desired_prio.sched_policy = SCHED_NORMAL;
    }

    /* binder最低优先级限制还比发起传输的线程优先级高,那就取优先级高的 */
    if (node_prio.prio < t->priority.prio ||
        (node_prio.prio == t->priority.prio && node_prio.sched_policy == SCHED_FIFO)) {
        /*
         * 翻译:如果Node上的最低优先级较高(数值较小),请使用Node的优先级。
         * 如果优先级相同,但Node服务的使用的是 SCHED_FIFO(大部分设置的就是它),则首选
         * SCHED_FIFO,因为与 SCHED_RR 不同,它可以无限运行。
         */
        desired_prio = node_prio;
    }
    /* 实际设置优先级的函数 */
    binder_set_priority(task, desired_prio);

    trace_android_vh_binder_set_priority(t, task);
}

binder_set_priority() 只是调用 binder_do_set_priority(task, desired, /* verify = */ true),后者实现如下:

/*
 * 若传参 verify=true,会校验task是否有 CAP_SYS_NICE 权限,若没有,只能设置到其
 * rlimt运行的最高优先级。若传false则没有这个校验。
 */
static void binder_do_set_priority(struct task_struct *task, struct binder_priority desired, bool verify)
{
    int priority; /* user-space prio value */
    bool has_cap_nice;
    unsigned int policy = desired.sched_policy;

    /* task的优先级已经和要设置的优先级是一样的了,直接退出 */
    if (task->policy == policy && task->normal_prio == desired.prio)
        return;

    has_cap_nice = has_capability_noaudit(task, CAP_SYS_NICE);

    /* 这里又转换为用户优先级 */
    priority = to_userspace_prio(policy, desired.prio);

    /* 需要校验,要设为RT,又没有 CAP_SYS_NICE 权限(若有这个权限就可以忽略rlimit限制) */
    if (verify && is_rt_policy(policy) && !has_cap_nice) {
        /*
         * 为0表示task不允许被设置为RT线程,那就设置为prio=100
         * 的CFS线程,否则设置为最大允许的RT优先级。
         * 注: 上面已经转换为用户Rt优先级了,数值越大优先级越高。
         */
        long max_rtprio = task_rlimit(task, RLIMIT_RTPRIO);
        if (max_rtprio == 0) {
            policy = SCHED_NORMAL;
            priority = MIN_NICE;
        } else if (priority > max_rtprio) {
            priority = max_rtprio;
        }
    }

    /* 需要校验,要设为CFS,又没有 CAP_SYS_NICE 权限 */
    if (verify && is_fair_policy(policy) && !has_cap_nice) {
        /* 只能设置到task所支持的最大CFS优先级 */
        long min_nice = rlimit_to_nice(task_rlimit(task, RLIMIT_NICE));
        if (min_nice > MAX_NICE) {
            binder_user_error("%d RLIMIT_NICE not set\n", task->pid);
            return;
        } else if (priority < min_nice) {
            priority = min_nice;
        }
    }

    /* 实际设置的和外部传参要求设置的不一致,给个debug打印 */
    if (policy != desired.sched_policy || to_kernel_prio(policy, priority) != desired.prio)
        binder_debug(BINDER_DEBUG_PRIORITY_CAP, "%d: priority %d not allowed, using %d instead\n",
                  task->pid, desired.prio, to_kernel_prio(policy, priority));

    /*
     * 这个会打印出task设置前的优先级,要设置的优先级和调用者希望设置的优先级
     * HwBinder:2957_2-4054    [001] ...2 101180.500974: binder_set_priority: proc=1490 thread=1490 old=120 => new=105 desired=105 //binder线程优先级升高
     * Loc_hal_worker-1426    [000] ...2 101334.089782: binder_set_priority: proc=2957 thread=7091 old=105 => new=120 desired=120 //binder线程优先级降低
     */
    trace_binder_set_priority(task->tgid, task->pid, task->normal_prio, to_kernel_prio(policy, priority), desired.prio);

    /* Set the actual priority */
    /*
     * 设置调度策略和RT优先级,对于正在运行的任务,从trace上看这个设置不会打断其运行。
     * 若是RT设置为CFS,这个函数调用后设置为CFS 120优先级。
     *
     * 对于policy=0,prio=0的设置,下面因判断不是fair policy,就只设置成了CFS 120优先级。
     */
    if (task->policy != policy || is_rt_policy(policy)) {
        struct sched_param params;
        params.sched_priority = is_rt_policy(policy) ? priority : 0;
        sched_setscheduler_nocheck(task, policy | SCHED_RESET_ON_FORK, &params);
    }

    /* 设置CFS优先级 */
    if (is_fair_policy(policy))
        set_user_nice(task, priority);
}

binder_transaction_priority()函数作用: 将task设置为 t->priority 指定的优先级。但是受参数和服务对自己binder优先级配置的影响,设置逻辑如下:

(1) 若服务配置不允许自己的binder线程继承RT优先级,而 t->priority 又是RT线程,就将响应的binder线程设置为优先级为120的CFS线程。
(2) 若服务允许自己binder线程运行的最低优先级比 t->priority 还高,就将响应的binder线程设置为服务允许的最低优先级。
(3) 底层函数传参 verify=true,还会校验 CAP_SYS_NICE 权限,若没有这个权限,只能设置 rlimit 允许的最高优先级。

3. 从函数调用路径中可以看出,这组优先级传递函数中,对外的函数分别是 binder_transaction_priority() 和 binder_restore_priority(),则看这两个函数的调用路径:

(1) binder_transaction_priority 调用路径

binder_ioctl
    case BINDER_WRITE_READ:
        binder_ioctl_write_read
            binder_thread_write
                case BC_TRANSACTION/BC_REPLY:
                    binder_transaction(proc, thread, &tr, cmd == BC_REPLY, 0); //好像没用这里
                case BC_TRANSACTION_SG/BC_REPLY_SG: //4.X内核上用这个
                    binder_transaction(proc, thread, &tr.transaction_data, cmd == BC_REPLY_SG, tr.buffers_size);
                        binder_proc_transaction //发起同步传输
                            binder_transaction_priority //《调用路径1》
                        binder_proc_transaction //发起的异步传输
                            binder_transaction_priority //《调用路径2》

binder_ioctl
    case BINDER_WRITE_READ:
        binder_ioctl_write_read(filp, cmd, arg, thread);
            binder_thread_read 
                binder_transaction_priority //同步传输服务端读 《调用路径3》

(2) binder_restore_priority 调用路径:

binder_ioctl
    case BINDER_WRITE_READ:
        binder_ioctl_write_read(filp, cmd, arg, thread);
            binder_thread_read
                if (wait_for_proc_work)
                    binder_restore_priority(current, proc->default_priority); //将要休眠《调用路径4》

binder_ioctl
    case BINDER_WRITE_READ:
        binder_ioctl_write_read
            binder_thread_write
                case BC_TRANSACTION_SG/BC_REPLY_SG:
                    binder_transaction
                        if (reply) 
                            binder_restore_priority(current, in_reply_to->saved_priority); //对同步传输的reply《调用路径5》
                        if (in_reply_to)
                            binder_restore_priority(current, in_reply_to->saved_priority); //这个是传输报错后的处理路径

可见,binder优先级的传递都发生在 ioctl write/read 时刻。要搞清楚优先级传递,只需要搞清 binder_transaction_priority() 和 binder_restore_priority() 在各种传输场景下各参数和 t->priority 含义即可。


三、优先级传递逻辑

1. client sync transaction

static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread,
    struct binder_transaction_data *tr, int reply, binder_size_t extra_buffers_size)
{
    struct binder_transaction *t, *in_reply_to = NULL;;

    if (reply) {
        /* client端发起请求时创建的binder_transaction结构 */
        in_reply_to = thread->transaction_stack;
    }
    ...
    
    /* 同步传输(但是也没区分reply,单reply不用此成员),赋值的是调用线程的优先级 */
    if (!(t->flags & TF_ONE_WAY) && binder_supported_policy(current->policy)) {
        /* Inherit supported policies for synchronous transactions */
        t->priority.sched_policy = current->policy;
        t->priority.prio = current->normal_prio;
    } else {
        /* Otherwise, fall back to the default priority 赋值为目标主线程优先级 */
        t->priority = target_proc->default_priority;
    }

    t->work.type = BINDER_WORK_TRANSACTION;

    if (reply) { //reply
        /* 对同步传输的reply, 将服务的binder线程设置为之前的优先级 */
        binder_restore_priority(current, in_reply_to->saved_priority); //《调用路径5》
    } else if (!(t->flags & TF_ONE_WAY)) { //sync transaction
        t->need_reply = 1;
        t->from_parent = thread->transaction_stack;
        thread->transaction_stack = t;
        binder_inner_proc_unlock(proc);
        /* 此函数中会传递优先级 */
        binder_proc_transaction(t, target_proc, target_thread);
    } else { //async transaction
        binder_proc_transaction(t, target_proc, NULL);
    }
    ...
}

static int binder_proc_transaction(struct binder_transaction *t,
    struct binder_proc *proc, struct binder_thread *thread)
{

    thread = binder_select_thread_ilocked(proc);

    if (thread) {
        /* 不区分同步传输还是异步传输 */
        binder_transaction_priority(thread->task, t, node_prio, node->inherit_rt); //《调用路径1》《调用路径2》
        /* 将work放在binder线程的thread->todo链表上 */
        binder_enqueue_thread_work_ilocked(thread, &t->work);
    } else if (!pending_async) {
        /* 若是挂在其它链表上则不会设置优先级 */
        binder_enqueue_work_ilocked(&t->work, &proc->todo);
    } else {
        binder_enqueue_work_ilocked(&t->work, &node->async_todo);
    }
    ...
    if (!pending_async)
        /* 然后唤醒服务的binder线程 */
        binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);

    ...
}

此过程对应上面《调用路径1》,binder_proc_transaction 调用传参 (thread->task, t, node_prio, node->inherit_rt),参数依次为:
thread->task:响应请求的服务的binder线程,
t: 发起传输的 client 构造的,其 t->priority 成员同步传输来自调用线程,异步传输来自 target_proc->default_priority,见 binder_transaction()。
node_prio: 服务 node 对自己的 binder 线程指定的最小优先级限制,分别取自 node->min_priority/node->sched_policy。
node->inherit_rt: 服务是否允许自己的 binder 线程继承RT优先级, 创建服务时由用户的 flat_binder_object::flags 配置,若不允许,当RT线程call过来时就将服务的binder线程设置为prio=120的CFS优先级。

解释起来就是在client发起同步传输过程中尝试将响应我们请求的 binder 线程设置为发起binder传输的线程的优先级。但是设置下去的最终优先级是受 binder_node 对地板和 CAP_SYS_NICE 对天花板的限制的。

 

2. client async transaction

此过程对应上面《调用路径2》,和上面同步传输过程几乎一致,不同点在于对于异步传输中 t->priority 来自 target_proc->default_priority,也就是服务端执行 binder_open() 线程的优先级。

解释起来就是在client发起异步传输过程中尝试将响应我们请求的 binder 线程设置为服务端执行 binder_open() 线程的优先级(一般是服务主线程)。


3. server read sync transaction

static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread,
                  binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed, int non_block)
{
    int wait_for_proc_work;

    /*
     * 判断一个thread能否用来处理proc的work,对于同步传输,thread->transaction_stack 不为NULL,
     * 对于同步传输和异步传输 thread->todo 链表上都有work, 就返回false。
     * 只有对于异步的又没有选到thread线程将work挂在proc->todo链表上时的请求才可能返回true,但是
     * 概率应该较低,虽然这里设置成了proc->default_priority(异步传输t->priority保存的也是service
     * 执行binder_open()时的优先级),下面会再次根据t来进行设置的,不会出现优先级出错。
     *
     * 但是线程在处理完同步/异步请求之后,要进行阻塞休眠时的下一次读,这里会返回true,因此要
     * 进入休眠的binder线程会设置为 proc->default_priority。
     */
    wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);

    if (wait_for_proc_work) {
        /* 应该是 */
        binder_restore_priority(current, proc->default_priority); //《调用路径4》
    }

    /* 平时无work要处理时在这里休眠 */
    binder_wait_for_work(thread, wait_for_proc_work);

    while (1) {
        struct binder_work *w = NULL;
        struct binder_transaction *t = NULL;

        /* 能处理proc的work才会从proc->todo链表上取任务下来 */
        if (!binder_worklist_empty_ilocked(&thread->todo))
            list = &thread->todo;
        else if (!binder_worklist_empty_ilocked(&proc->todo) && wait_for_proc_work)
            list = &proc->todo;

        switch (w->type) {
            case BINDER_WORK_TRANSACTION: {
                binder_inner_proc_unlock(proc);
                t = container_of(w, struct binder_transaction, work);
            } break;
            ...
        }
        ...
        /* server端读取执行位置 */
        if (t->buffer->target_node) {
            /* 目标服务node, 对于service的binder线程就是自己服务的node */
            struct binder_node *target_node = t->buffer->target_node;
            struct binder_priority node_prio;

            trd->target.ptr = target_node->ptr;
            trd->cookie =  target_node->cookie;
            node_prio.sched_policy = target_node->sched_policy;
            node_prio.prio = target_node->min_priority;
            /* 对于work对于的每一个transaction都执行,此时current就是服务的binder线程 */
            binder_transaction_priority(current, t, node_prio, target_node->inherit_rt); //《调用路径3》
            cmd = BR_TRANSACTION;
        } else {
            /* client端读取执行位置 */
            trd->target.ptr = 0;
            trd->cookie = 0;
            cmd = BR_REPLY;
        }
    }
    ...
}

对应于《调用路径3》,service端读取时,对于每一个work对应的transaction都会执行 binder_transaction_priority(), 由于通过 t->set_priority_called 做了针对一个t防止重复设置的机制,
若是从thread->todo链表上取下来的work不会执行,若是从proc->todo链表上取下来的才会执行。


4. server read async transaction

也对应于《调用路径3》,对于异步传输,虽然《调用路径4》也有可能对binder线程进行设置,但是随后又会在下面《调用路径3》中设置回t->priority。

对于《调用路径4》的传参可以看出,service执行open(/dev/binder)节点后再设置其优先级是不合适的,因为其 proc->default_priority 成员只在open binder节点时赋值一次,但是surfaceflinger偏偏在
open节点之后设置了自己的优先级。


4. server reply

对应于《调用路径5》, service端在对client端进行reply时会将自己binder线程的优先级设置回之前的状态。


5. client read

client读取路径中不涉及优先级传递。

 

四、优先级相关Trace和HOOK

1. 相关DEBUG Trace

(1) 打印binder线程当前的优先级和期望设置的优先级和最终要被设置的优先级

binder_do_set_priority
    trace_binder_set_priority(task->tgid, task->pid, task->normal_prio, to_kernel_prio(policy, priority), desired.prio);

HwBinder:1643_2-2089    [003] .....  6910.598526: binder_set_priority: proc=1643 thread=2089 old=120 => new=97 desired=97
HwBinder:1643_2-2089    [003] .....  6910.615109: binder_set_priority: proc=1643 thread=2089 old=97 => new=0 desired=0

2. 相关DEBUG Log

(1) 实际设置值和外部传参不同时给个打印

binder_do_set_priority
    binder_debug(BINDER_DEBUG_PRIORITY_CAP, "%d: priority %d not allowed, using %d instead\n", task->pid, desired.prio, to_kernel_prio(policy, priority));

# echo 8192 > /sys/module/binder/parameters/debug_mask //使对此mask(1<<13)的打印生效

补充:
Linux-5.15内核中改动:

binder_do_set_priority
    binder_debug(BINDER_DEBUG_PRIORITY_CAP, "%d: priority %d not allowed, using %d instead\n", task->pid, desired->prio, to_kernel_prio(policy, priority));
    binder_debug(BINDER_DEBUG_PRIORITY_CAP, "%d: %s: aborting priority restore\n", thread->pid, __func__);

binder_transaction_priority
    binder_debug(BINDER_DEBUG_PRIORITY_CAP, "%d: saved pending priority %d\n", current->pid, thread->prio_next.prio);

# dmesg -c | grep binder
[ 6448.719215] binder: 17920: saved pending priority 120
[ 6764.256906] binder: 1792: priority 98 not allowed, using 100 instead
...

3. 相关HOOK

binder_transaction_priority
    trace_android_vh_binder_priority_skip(task, &skip); //用户可以决定是否传递优先级
    trace_android_vh_binder_set_priority(t, task); //传递完后也给个HOOK

binder_transaction
    trace_android_vh_binder_restore_priority(in_reply_to, current); //只在reply时有一个
    trace_android_vh_binder_restore_priority(in_reply_to, current); //传输失败时

binder_thread_read
    trace_android_vh_binder_restore_priority(NULL, current); //处理proc的work路径中

 

五、总结

1. 用户空间对服务node优先级的配置是通过 flat_binder_object::flags 成员设置下来的,见 binder_init_node_ilocked()。

2. binder优先级传递其实就只是对service的binder线程进行的设置。简要总结起来就是在client发起同步binder请求时,尝试将响应请求的binder线程设置为发起线程的优先级,但是对优先级有个限制,不能低于binder node指定的最低优先级,也不能高于rlimit限制的优先级(若无 CAP_SYS_NICE 权限),在reply时对binder线程的优先级进行恢复; 当client发送异步请求时会进行同样的尝试,将响应请求的binder线程设置为service主线打开binder节点时的优先级(proc->default_priority)后进行响应。

3. 当binder线程没有事情做要休眠等待work时,也会设置为 proc->default_priority, 然后休眠等待.

4. 启动服务时在rc脚本中使用"capabilities SYS_NICE"给服务赋予 CAP_SYS_NICE 权限。

5. 优先级的设置是 per-binder_transaction t 的,对于每一个t都会进行一次优先级传递。

 

标签:node,优先级,prio,Binder,priority,binder,policy
From: https://www.cnblogs.com/hellokitty2/p/16852917.html

相关文章

  • Python判断中使用多个and和or的优先级与踩坑
    tags:PythonDebug一个问题最近刷力扣,想试试Python新支持的海象操作符,其实就是能在语句中赋值,类似下面这样:if(n:=len(nums)):returnFalse但是当出现下面这种......
  • 单网卡设置多IP时Windows下的IP优先级排序问题!(只能做服务器环境):
    本策略只能接收辅助IP收到的包,而无法通过辅助IP发送包,因此只能作为服务器时使用。     局域网下同时单网卡可以设置多IP,同时访问不同网段设备,但普遍而言Windows并......
  • nginxlocation优先级
    nginx反向代理一个server下配置多个location域名问题当你设置两个location/时,当然只会匹配第一个了。你的意思是不是要配置两个域名?server{listen80;server_namewww.1.com;......
  • 运算符与运算符优先级
    运算符算术运算符:+,-,*,/,%,++,--赋值运算符:=,+=,-=,*=,/=关系运算符:>,<,>=,<=,==,!=逻辑运算符:&&,||,!位运算符:&,|移位运算符:<<,>>,>>>条件运算符:?: 优先级1、分隔符2、单目运算(++,--......
  • c语言运算符号(c语言运算符号的优先级表)
    符号的运算系统用英语怎么翻?学生是一个语言、符号的运算系统用英语学生是一个语言、符号的运算系统Thestudentistheoperationsystem,alanguagesymbolc语言运算符号......
  • c语言运算符优先级表(c语言中各种运算符的优先级)
    运算符的优先级是怎样的?运算符的优先级从高到低大致是:单目运算符、算术运算符、关系运算符、逻辑运算符、条件运算符、赋值运算符()和逗号运算符c语言上运算符优先级是怎样......
  • ctfshow web181(sql注入where后运算符优先级利用)
    //拼接sql语句查找指定ID用户$sql="selectid,username,passwordfromctfshow_userwhereusername!='flag'andid='".$_GET['id']."'limit1;";//对传入的参数......
  • java线程的优先级
    packageA_ShangGuiGu.Thread.ThreadDemo;/***优先级*1.MAX_Priority-----最大优先级为10*2.MIN_Priority-----最小优先级为1*3.NORM_Priority----默认优先级5*4......
  • STM32的优先级的理解及其使用
     有组0~4。同时对每一个中断设置一个抢占优先级和一个响应优先级值。分组配置是在寄存器SCB->AIRCR中配置:SCB->AIRCR为111,就是0位抢占,4位响应中断优先级表格  NVI......
  • 宋宝华: 关于Linux进程优先级数字混乱的彻底澄清
    找了好久终于找到你了,这是网上关于优先级描述最清晰的一个帖子。没有之一。必须转发。                所以从上面的描述来说,先是区分调......