问题:
system_server进程中有很多的系统服务,这些服务之间是如何进行通信的?是否也会存在线程争夺的问题?或者多线程的问题?
-
system_server 里的系统服务之间主要通过两类方式通信:
- 同进程直接方法调用(Java 对象引用) ;
- Binder 接口调用(包括“进程内 Binder”) ,再辅以 Handler 消息、回调监听等模式。
-
是的,system_server 内部大量使用多线程,也一定会有“线程争夺 / 资源竞争”问题,只是通过严格的锁设计、线程模型和 Watchdog 来降风险。
一、system_server 中系统服务之间是怎么通信的?
system_server 自身就是一个 Java 进程,里面的系统服务本质上就是一些 单例 Java 对象(如 AMS、PMS、WMS 等),所以通信方式可以分为几层:
同进程直接方法调用(最常见、最高效)
服务之间最常见的是“直接持引用 + 调方法”。
class ActivityManagerService {
final WindowManagerService mWindowManager;
final PackageManagerService mPackageManager;
void startSomeActivity(...) {
// 直接调 WMS 的方法
mWindowManager.addAppToken(...);
// 直接调 PMS 查询包/权限
mPackageManager.checkPermission(...);
}
}
特征:
- 不经过 Binder 内核层,只是普通 Java 调用;
- 延迟、开销都很小;
- 常见于那些紧耦合、频繁协作的服务: AMS ↔ WMS ↔ PMS ↔ PowerManagerService 等。
这也是 system_server 内部通信的主力方式。
Binder 接口调用(包括进程内 Binder)
很多系统服务对外都暴露了一个 AIDL/IBinder 接口,例如:
IActivityManagerIPackageManagerIWindowManager
服务之间如果走的是这些接口,也可能还是通过 Binder 调用。
但有两种情况:
-
跨进程 Binder:
- app → AMS、WMS、PMS 等,必然是 Binder IPC。
-
进程内 Binder(in-process Binder) :
- system_server 里的某个服务拿到另一个服务的 AIDL 接口:
IActivityManager am = IActivityManager.Stub.asInterface(
ServiceManager.getService("activity")
);
am.someRemoteCall(...);
- 在同一个进程中,Binder 实现会做 “短路优化” : 不走真正的内核 IPC,但仍然会切到对应的 Binder 线程执行 Stub 方法。
特点:
- 逻辑层面对外仍然是“Binder 接口”,方便统一管理、权限检查;
- 在同进程下开销比真正 IPC 小,但 仍然涉及线程切换(Binder 线程池) ,并不是“同线程直接调用”。
一些厂商或系统代码中,为了保持接口统一,也会让服务之间通过 AIDL 接口来调用,这时就会出现这种“进程内 Binder”。
Handler 消息 / 回调 / 订阅通知
在多线程配合上,服务之间还经常使用:
- Handler + Message 模式
某个服务在自己的 HandlerThread 上工作:
mHandler.post(() -> doHeavyWorkFromOtherService(...));
- 其他服务调用它的“入口函数”,入口函数只是
post消息到内部线程,真正的逻辑在那个线程跑。
这其实是一种 线程间通信,但也是服务间协作的常见方式。
- 监听 / 订阅模式
- 如:某服务注册为另一个服务的 listener / callback
mPowerManagerInternal.registerLowPowerModeObserver(observer);
- 当被观察的服务状态变化时,会在合适的线程回调这个 observer。
- 广播 / ContentObserver 等“通知型”机制
- 虽然广播/ContentObserver 多用于 app ↔ system_server,但 system_server 内部服务也会利用这些机制协调状态。
二、system_server 里会不会有线程争夺 / 多线程问题?
肯定会有,而且非常关键。system_server 是一个高度多线程的进程。
-
system_server 的线程构成(简化)
大概有几类核心线程:
-
主线程(system_server main looper)
- SystemServer.run() 所在线程;
- 启动系统服务、处理一些关键调度消息。
-
Binder 线程池
- 处理 来自应用和其他进程的 Binder 调用;
- 这些线程执行到 AMS/PMS/WMS 等服务的方法时,就会去争抢它们的锁/资源。
- 各服务自己的 HandlerThread / 专用线程
-
比如:
- AMS 业务线程;
- WMS / Display 相关线程;
- Sensor、Power、Alarm 等的一些内部线程;
-
用来处理本服务的耗时逻辑、复杂状态机,不阻塞 main 或 Binder 线程。
- 其他线程池 / 后台线程
- 一些服务会用
ExecutorService/线程池来做异步任务。
所以:system_server 内部是一个多线程“巨无霸”,多服务共享同一个进程地址空间,自然会有锁竞争、线程争用等问题。
-
典型的“争夺 / 多线程”问题有哪些?
-
锁竞争 / 临界区竞争
-
各服务内部有大量
synchronized或ReentrantLock控制共享数据:- 如 AMS 全局进程/Activity 列表;
- WMS 窗口/显示设备状态;
-
不同线程(main、Binder 线程、各 HandlerThread)进入这些代码时会争夺锁,若锁粒度过粗,会造成严重阻塞。
-
死锁风险
-
场景类似:
- 线程 A:持有 AMS 的锁 → 再去调用 WMS;
- 线程 B:持有 WMS 的锁 → 再去调用 AMS;
-
如果锁顺序不一致,很容易产生相互等待死锁;
-
为此 Framework 里有严格的 锁获取顺序规范(并在注释中写得很清楚),不允许随意改。
-
Binder 线程池耗尽 / 队列堵塞
- 如果很多请求都卡在 system_server 的某个耗时代码里,Binder 线程可能会被占满;
- 新来的 Binder 请求得不到处理,表现为系统层面“卡顿”甚至 ANR;
- Watchdog 主要就是盯着这类“长时间不响应”的情况。
-
主线程消息队列堆积
-
如果某服务不小心在 main 线程做了大量耗时操作(比如 I/O、大循环),会导致:
- 主线程 MessageQueue 处理不过来;
- 整个系统表现为“系统界面卡死”,最后被 Watchdog 认为 system_server 卡死。
-
Framework 是如何控制这些多线程问题的?
为了控制 system_server 内部复杂的多线程和锁竞争,Android 做了很多设计和规范:
- 统一/约束锁模型
-
引入共享“全局锁”或一组核心锁,并明确锁获取顺序;
-
文档和代码注释中严格强调:
- “持有哪个锁时禁止再调用谁的接口” ;
- “不要在持锁状态下做 Binder 调用 / IPC” ,防止“锁 + IPC”组合导致跨进程死锁。
- 大量使用 Handler 切线程
- 耗时逻辑、复杂状态机尽量放到服务内部的 HandlerThread;
- 对外暴露的 Binder 接口通常只做参数校验、轻量操作,然后
post到内部线程执行。
- Watchdog 监控
-
Watchdog 定期检查:
- system_server 主线程;
- 一些标记为“关键”的 HandlerThread;
-
如果发现它们长时间卡在某处不动(比如锁等待),就打印堆栈并最终杀死 system_server 重启 framework。
- StrictMode / 开发阶段检测
- 开启 StrictMode 后,主线程上的磁盘 IO / 网络访问等会被严格检测;
- 内部开发和 CTS 测试会帮助发现不合理的耗时逻辑。
三、总结:你可以怎么理解这件事?
-
通信方式
- system_server 内的系统服务之间,主要是同进程直接方法调用,辅以 进程内 Binder 调用、Handler 消息、回调监听 等。
- 对外(app、native 服务)则几乎都是 Binder IPC。
-
多线程与争夺
- 是的,system_server 本质上就是一个高度多线程系统:主线程、Binder 线程池、各种 HandlerThread 等共同工作;
- 服务之间共享数据结构、锁,自然会有线程争夺、锁竞争、死锁风险;
- Android 通过 锁顺序规范、线程模型设计、Handler 切线程、Watchdog 监控 等手段,把这套系统“勉强维持在可控范围”。