android 系统核心机制binder(14)binder调试总结

112 篇文章 91 订阅

该系列文章总纲链接:专题分纲目录 android 系统核心机制 binder


1binder调试基础解读

1.1 binder的设备节点简单解读

binder调试相关的设备节点再目录/sys/kernel/debug/binder目录下,节点内容如下所示:

 这里可以看到binder下所有通信进程信息,打开后cat 其中一个,内容如下所示:

这里包含了该进程中binder相关内容,比如:Binder线程池(thread)、实体对象(binder_node)、引用对象(binder_ref)、内核缓冲区(buffer)等信息。

1.2 binder 调试日志开关解读

binder driver存在16类调试log开关,debug_mask表信息在binder.c文件中的定义为:

enum {
	BINDER_DEBUG_USER_ERROR             = 1U << 0,
	BINDER_DEBUG_FAILED_TRANSACTION     = 1U << 1,
	BINDER_DEBUG_DEAD_TRANSACTION       = 1U << 2,
	BINDER_DEBUG_OPEN_CLOSE             = 1U << 3,
	BINDER_DEBUG_DEAD_BINDER            = 1U << 4,
	BINDER_DEBUG_DEATH_NOTIFICATION     = 1U << 5,
	BINDER_DEBUG_READ_WRITE             = 1U << 6,
	BINDER_DEBUG_USER_REFS              = 1U << 7,
	BINDER_DEBUG_THREADS                = 1U << 8,
	BINDER_DEBUG_TRANSACTION            = 1U << 9,
	BINDER_DEBUG_TRANSACTION_COMPLETE   = 1U << 10,
	BINDER_DEBUG_FREE_BUFFER            = 1U << 11,
	BINDER_DEBUG_INTERNAL_REFS          = 1U << 12,
	BINDER_DEBUG_BUFFER_ALLOC           = 1U << 13,
	BINDER_DEBUG_PRIORITY_CAP           = 1U << 14,
	BINDER_DEBUG_BUFFER_ALLOC_ASYNC     = 1U << 15,
};

debug_mask表信息 详细解读如下:

debug_maskmask值解释
BINDER_DEBUG_USER_ERROR1用户使用错误
BINDER_DEBUG_FAILED_TRANSACTION2transaction失败 
BINDER_DEBUG_DEAD_TRANSACTION4transaction死亡
BINDER_DEBUG_OPEN_CLOSE8open/close/mmap
BINDER_DEBUG_DEAD_BINDER16binder/node死亡
BINDER_DEBUG_DEATH_NOTIFICATION32binder死亡通知
BINDER_DEBUG_READ_WRITE64read/write
BINDER_DEBUG_USER_REFS128binder引用计数 
BINDER_DEBUG_THREADS256binder_thread信息
BINDER_DEBUG_TRANSACTION512transaction信息
BINDER_DEBUG_TRANSACTION_COMPLETE1024transaction完成
BINDER_DEBUG_FREE_BUFFER2048可用buffer
BINDER_DEBUG_INTERNAL_REFS4096binder内部引用计数 
BINDER_DEBUG_BUFFER_ALLOC8192同步内存分配信息
BINDER_DEBUG_PRIORITY_CAP16384调整binder线程的nice值
BINDER_DEBUG_BUFFER_ALLOC_ASYNC32768异步内存分配信息

一般是通过设置节点/sys/module/binder/parameters/debug_mask内的值来动态控制选择开启上表中的debug log,比如要看binder的read/write信息,则直接执行命令:

echo 64 > /sys/module/binder/parameters/debug_mask

如果还需要更多信息,比如BINDER_DEBUG_OPEN_CLOSE的信息,那么就执行命令(这里的72=64+8,实际上就是对应的标识位置1即可)

echo 72 > /sys/module/binder/parameters/debug_mask

总之,只需将各个开关的mask值相加(对应标识位置1)写入debug_mask即可。

2 问题总结

说明:binder如果通信出现问题,那么多半是app或者自定义service写的有问题造成的(比如自己写的 或者 ODM/OEM厂商的某些增值服务。。。),因此binder很多时候主要是用于辅助分析和定位添加内容后导致的问题。

2.1 binder内存泄漏问题总结

binder内存泄漏(也称作binder leak),比如在binder通信中如果传输中使用的parcel没有有效释放(release),会造成buffer空间越来越少,积累到一定程度就会出现binder传输的消息在另外一端无法接收。正常使用IPCThread的API操作binder会自动往driver发送FREE_BUFFER的指令做释放操作,除非是单独对binder做了ioctl相关操作。那么如何确认是该问题呢?

使用cat命令查看transaction下的内容,如下所示:

这里一行buffer代表有一个binder通信在占用buffer的空间。如果该proc进程下的buffer的行数持续增长不减,基本上你就能判断是binder内存泄漏的问题了,接下来就定位到目标进程了,再接下来就要针对对应的进程进行详细分析了。

2.2 binder线程到达上限

如果app向service发起请求的频率过高,service端如果对所有的业务执行都加了锁的话,则会导致service端用于接收处理binder事件的线程全部卡住,当线程池(default 16个线程)耗尽之后,就无法再处理请求。如果这个时候app的主线程如果再调用该serivce提供的方法,就很容易出现ANR的问题。那么如何确认是该问题呢?

使用cat命令查看transaction下的内容,如下所示:

如果线程池耗尽则在transactions文件日志中 会连续显示16句。为解决这种线程池耗尽的问题,应对的方案是 从app端去限制请求频率,不要频繁的请求server。

2.3 oneway相关问题

有的自定义service端的方法 存在 间歇性的卡顿,但一般情况下并不会阻塞,但是在压力测试中有一定几率出现阻塞情况,关键是一旦出现阻塞就很容易导致部分关键功能整体失效。这时在编写aidl时需要添加oneway方法,使得即便发生了错误产生阻塞也不至于卡死。但这样做真的就可以了吗?这里不绕弯子了,事实上还需要调整APP请求server的频率,为什么呢?因为如果客户端你调用的频率过高,就会导致服务端binder线程到达上限的问题了。

这里详细解读下oneway的两个特性:异步调用和串行化处理。具体解读如下:

  1. 异步调用是指应用向 binder 驱动发送数据后不需要挂起线程等待 binder 驱动的回复,而是直接结束。像一些系统服务调用应用进程的时候就会使用 oneway,比如 AMS 调用应用进程启动 Activity,这样就算应用进程中做了耗时的任务,也不会阻塞系统服务的运行。
  2. 串行化处理是指对于一个服务端的 AIDL 接口而言,所有的 oneway 方法不会同时执行,binder 驱动会将他们串行化处理,排队一个一个调用。

@1 对于第1点,要注意的是 所谓异步指的是 binder 驱动发送数据后不需要挂起线程等待 binder 驱动的回复,(注意:是不需要,是协议流程发生变化),对比如下:

binder.c的源码中关键代码为:

static void binder_transaction(struct binder_proc *proc,
			       struct binder_thread *thread,
			       struct binder_transaction_data *tr, int reply)
	//...
	if (reply) {//有reply的情况
		binder_pop_transaction(target_thread, in_reply_to);
	} else if (!(t->flags & TF_ONE_WAY)) {//没有reply且没有oneway标志位的情况
		t->need_reply = 1;
		t->from_parent = thread->transaction_stack;
		thread->transaction_stack = t;
	} else {//没有reply且有oneway标志位的情况
		if (target_node->has_async_transaction) {
			target_list = &target_node->async_todo;
			target_wait = NULL;//这里表示的是线程不用等待,所以在图上会没有后面的交互
		} else
			target_node->has_async_transaction = 1;
	}
	//...
}

@2 对于第2点,只有确切的回答了以下几个问题才算是真的理解。

假设server端进程A中有如下两个Binder服务A1和A2,这两个服务都有两个异步的接口AE1和AE2。如下所示:

interface A1 {
    oneway void AE1();//异步执行2-3秒
    oneway void AE2();//同上
}
interface A2 {
    oneway void AE1();//同上
    oneway void AE2();//同上
}

B进程和C进程均为server进程A的客户端,问题如下:

  1. 如果进程B和进程C同一时刻分别调用A1.AE1()和A2.AE1(),请问进程A能否同时响应这两次Binder调用并执行?
  2. 如果进程B和进程C同一时刻分别调用A1.AE1()和A1.AE1(),请问进程A能否同时响应这两次Binder调用并执行?
  3. 如果进程B和进程C同一时刻分别调用A1.AE1()和A1.AE2(),请问进程A能否同时响应这两次Binder调用并执行?

请思索片刻后再答案哦~,答案在最下方。

。。。

。。。

。。。

1答案为:可以同时执行,2和3 答案均为:需要一个个排队执行。如果你会打错了,说明你需要回头看下代码认真分析了。

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Android Binder 机制Android 系统中的一种进程间通信(IPC)机制,用于在不同的进程之间传递数据和调用方法。它是 Android 系统中最重要的 IPC 机制之一,也是 Android 应用程序与系统服务进行通信的基础。 Binder 机制的工作原理是基于一个抽象的客户端-服务器模型。在 Binder 机制中,有三种角色:客户端、服务器和服务管理器。客户端和服务器在不同的进程中运行,而服务管理器运行在系统服务进程中。 当客户端需要与服务器通信时,它首先通过服务管理器获取服务器的引用。服务管理器通过一个名为 Binder 驱动的内核模块来实现进程间通信。客户端可以通过跨进程访问服务器对象来调用服务器上的方法,并将参数传递给服务器。服务器可以将结果返回给客户端。 Binder 机制的一个重要特性是它支持跨进程的对象引用。这意味着客户端可以获取服务器上的对象引用,并将其传递给其他进程中的客户端。通过这种方式,多个客户端可以共享服务器上的相同对象,并相互协作。 在 Android 应用程序中,开发者可以通过 AIDL(Android 接口定义语言)来定义客户端和服务器之间的接口。AIDL 可以生成一个 Java 接口和一个 C++ 接口,用于在客户端和服务器之间进行通信。 总之,Android Binder 机制Android 系统中用于进程间通信的核心技术之一。它提供了一种高效、灵活和安全的方式来在不同的进程之间传递数据和调用方法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值