背景
这是一个来自朋友的疑问,在sf
调用hwc
的binder_f1
的函数中hwc
调用sf
的binder_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,应该不是这里降级的。
代码语言:javascript复制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,所以也不是这里改的
代码语言:javascript复制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,应用就是这里改的。
代码语言:javascript复制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
这个函数,由于调用sf
的binder node
的inherit_rt
为false
,并且desired.sched_policy
也就是HWC
的sched_policy
为is_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
代码语言:javascript复制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对象就需要设置完成。
代码语言:javascript复制::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真的博大精深。