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

背景

这是一个来自朋友的疑问,在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真的博大精深。

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

推荐阅读更多精彩内容