美文网首页
[095]Binder调用的优先级降级

[095]Binder调用的优先级降级

作者: 王小二的技术栈 | 来源:发表于2024-04-18 17:50 被阅读0次

背景

这是一个来自朋友的疑问,在sf调用hwcbinder_f1的函数中hwc调用sfbinder_f2,会导致线程的优先级从97降级为120

请教一下,binder嵌套调用的优先级是怎么设定的呀
现在嵌套流程是这样的
1, sf sync binder to HWC    SF优先级是97,call到HWC,HWC的优先级是97
2, HWC sync binder to sf     这里binder嵌套,HWC call到SF,SF的优先级被改为120了,从trace上看HWC的优先级全程是97,不知道这个120是哪来的
3, sf reply hwc
4, hwc reply sf;

一、基础知识-Binder调用的优先级继承

我们要知道,Binder默认支持client端调用server端的时候,将client端的线程优先级传递给server端。
用的知识点可以参考我这篇《[051]Binder线程优先级继承

二、为什么线程优先级反而降了呢?

按照上面的知识点,从表面来看,不应该降级啊,我也很好奇为什么,接下来我来讲讲我的分析历程。
首先我拿到的信息就是120,我查看了binder驱动中的定义binder_priority接口体。

/**
 * struct binder_priority - scheduler policy and priority
 * @sched_policy            scheduler policy
 * @prio                    [100..139] for SCHED_NORMAL, [0..99] for FIFO/RT //这个线索很关键
 *
 * The binder driver supports inheriting the following scheduler policies:
 * SCHED_NORMAL
 * SCHED_BATCH
 * SCHED_FIFO
 * SCHED_RR
 */
struct binder_priority {
    unsigned int sched_policy;
    int prio;
};

根据[100..139] for SCHED_NORMAL, [0..99] for FIFO/RT这个注释,可以知道肯定在binder驱动中将sched_policy改成了SCHED_NORMAL
继续在binder驱动中搜SCHED_NORMAL,只有三处 = SCHED_NORMAL代码,我们来一一排除分析一下。

第一处:这是初始化,设置的进程的default_priority,应该不是这里降级的。

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);
    }
...
}

第二处:MIN_NICE是-20,对应优先级应该是100,所以也不是这里改的

static void binder_do_set_priority(struct binder_thread *thread,
                   const struct binder_priority *desired,
                   bool verify)
{
...
    if (verify && is_rt_policy(policy) && !has_cap_nice) {
        long max_rtprio = task_rlimit(task, RLIMIT_RTPRIO);

        if (max_rtprio == 0) {
            policy = SCHED_NORMAL;
            priority = MIN_NICE;//-20
        } else if (priority > max_rtprio) {
            priority = max_rtprio;
        }
    }
...
}
第三处:NICE_TO_PRIO(0)正好是120,应用就是这里改的。
static void binder_transaction_priority(struct binder_thread *thread,
                    struct binder_transaction *t,
                    struct binder_node *node)
{
...
    if (!node->inherit_rt && is_rt_policy(desired.sched_policy)) {//条件正好满足
        desired.prio = NICE_TO_PRIO(0);
        desired.sched_policy = SCHED_NORMAL;
    }
...
现场还原

HWC作为97的优先级调用SF的时候,会调用binder_transaction_priority这个函数,由于调用sfbinder nodeinherit_rtfalse,并且desired.sched_policy也就是HWCsched_policyis_rt_policy,因为97就是FIFO/RT,这里具体是哪个policy就不重要了。最后就将desired.prio设置成了120

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

三、为什么sf的binder node的inherit_rt为false

因为默认aidl的binder对象inherit_rt就是false

frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::flattenBinder(const sp<IBinder>& binder) {
    if (binder != nullptr) {
        if (!local) {
            ...
        } else {
            int policy = local->getMinSchedulerPolicy();
            int priority = local->getMinSchedulerPriority();

            if (policy != 0 || priority != 0) {
                // override value, since it is set explicitly
                schedBits = schedPolicyMask(policy, priority);
            }
            obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
            if (local->isRequestingSid()) {
                obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
            }
            if (local->isInheritRt()) {//调用BBinder的isInheritRt
                obj.flags |= FLAT_BINDER_FLAG_INHERIT_RT;
            }
            obj.hdr.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
    } else {
    ...
    }
}

bool BBinder::isInheritRt() {
    Extras* e = mExtras.load(std::memory_order_acquire);

    return e && e->mInheritRt;
}

除非你主动调用setInheritRt这个接口


void BBinder::setInheritRt(bool inheritRt) {
    LOG_ALWAYS_FATAL_IF(mParceled,
                        "setInheritRt() should not be called after a binder object "
                        "is parceled/sent to another process");

    Extras* e = mExtras.load(std::memory_order_acquire);

    if (!e) {
        if (!inheritRt) {
            return;
        }

        e = getOrCreateExtras();
        if (!e) return; // out of memory
    }

    e->mInheritRt = inheritRt;
}

规范的用法

更加规范的用法是这样子设置,BnCameraDeviceCallback是aidl文件自动生成的,但是要注意,需要在传递给Binder驱动之前,对应的Binder对象就需要设置完成。

::ndk::SpAIBinder AidlCamera3Device::AidlCameraDeviceCallbacks::createBinder() {
    auto binder = BnCameraDeviceCallback::createBinder();
    AIBinder_setInheritRt(binder.get(), /*inheritRt*/ true);//调用下面这个函数。
    return binder;
}

void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) {
    ABBinder* localBinder = binder->asABBinder();
    if (localBinder == nullptr) {
        LOG(FATAL) << "AIBinder_setInheritRt must be called on a local binder";
    }

    localBinder->setInheritRt(inheritRt);
}
小结

至此如何解决问题,只要找到HWC调用SF的接口对应的Binder对象,在初始化的地方,按照上述规范用法就可以了。

四、HWBinder的inherit_rt默认为true

HwBinder有一些特殊,就是默认加了FLAT_BINDER_FLAG_INHERIT_RT,所以HwBinder默认是可以继承RT的调度策略的。

system/libhwbinder/Parcel.cpp
status_t flatten_binder(const sp<ProcessState>& /*proc*/,
    const sp<IBinder>& binder, Parcel* out)
{
    if (binder != nullptr) {
        BHwBinder *local = binder->localBinder();
        if (!local) {
        } else {
            // Get policy and convert it
            int policy = local->getMinSchedulingPolicy();
            int priority = local->getMinSchedulingPriority();

            obj.flags = priority & FLAT_BINDER_FLAG_PRIORITY_MASK;
            obj.flags |= FLAT_BINDER_FLAG_ACCEPTS_FDS | FLAT_BINDER_FLAG_INHERIT_RT;//默认就加了
            obj.flags |= (policy & 3) << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT;
            if (local->isRequestingSid()) {
                obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
            }
            obj.hdr.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
    } else {

    }
    return finish_flatten_binder(binder, obj, out);
}

但是有意思的Android U上HWC已经改成aidl,朋友的设备恰好是Android U,那问题的开头SF调用HWC的binder的时候为什么没有降级呢。聪明的你应该想到了,就是在HWC初始化Binder服务的时候,主动调用了AIBinder_setInheritRt,具体HWC代码要看供应商,这个是AOSP中代码例子。

device/generic/goldfish-opengl/system/hwc3/Composer.cpp
::ndk::SpAIBinder Composer::createBinder() {
    DEBUG_LOG("%s", __FUNCTION__);

    auto binder = BnComposer::createBinder();
    AIBinder_setInheritRt(binder.get(), true);
    return binder;
}

这里还有一个细节大家要知道,因为HWC改成了aidl,也就意味着图中binder_f1的调用和binder_f2的响应是同一个sf线程。
这个知识点可以参考我之前的文章《[031]Binder线程栈复用

总结

每次自己感觉对binder已经很了解,总会有新的问题触及自己的知识盲区,binder真的博大精深。

相关文章

  • 降级、熔断

    降级,系统在调用服务时,服务方异常,调用方切换备用接口或返回备用数据;系统自身异常,返回降级数据 熔断,系统在调用...

  • Android系统源码学习

    Binder机制和AIDL Activity的启动过程 进程的优先级 ServerManager Window和 ...

  • Binder使用

    Binder 在安卓使用Binder实现进程间通信需要做哪些工作 如何模糊跨进程调用与进程内调用? 如何使用AID...

  • Spring-cloud-alibaba 之 Sentinel

    Sentienl 熔断降级 Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比...

  • Binder系列7—framework层分析

    一、概述 1.1 Binder架构 binder在framework层,采用JNI技术来调用native(C/C+...

  • Sentinel降级规则

    服务降级 Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如:调用超时或异常比例升高),对这个...

  • Android Binder调用

    准备工作 aidl定义接口int AService.a()方法 构造对象流程 客户端注册服务,从ServiceCo...

  • Binder方法调用

    Service Stub实现类 客户端调用类 getBookList和addBook这两个AIDL方法是在服务端的...

  • Binder驱动注册

    Binder驱动注册 Android进程间利用Binder机制实现通信,在App进程中,调用ioctl方法使程序陷...

  • 基于本地缓存的 fallback 降级机制

    基于本地缓存的 fallback 降级机制 Hystrix 出现以下四种情况,都会去调用 fallback 降级机...

网友评论

      本文标题:[095]Binder调用的优先级降级

      本文链接:https://www.haomeiwen.com/subject/uztdxjtx.html