系列文章:
再学安卓 - binder之Framework通信流程
再学安卓 - APP进程
再学安卓 - init进程
前言
上一篇我们对Binder通信的流程有了大致的了解,但始终有一个问题萦绕在我们心里,CountBinder需要AMS和APP建立的已有通道才能传输,那么AMS和APP之间的通道是怎么建立,系统中的第一个Binder通道是怎么建立的?我们可能很自然就想到上一篇提到过的ServiceManager,作为管理模块,它应该就是那个创始者。
通过第5篇init进程启动我们知道很多系统服务进程都是通过rc脚本启动的,ServiceManager也不例外。来看看
init.rc
:
on init
... ...
start servicemanager
在init阶段,启动了servicemanager。在第6篇zygote中我们提到过,除了系统初始化rc脚本,其他模块脚本基本都和模块代码在相同目录下,ServiceManager也不例外。
// frameworks/native/cmds/servicemanager/servicemanager.rc
service servicemanager /system/bin/servicemanager
class core animation
user system
group system readproc
critical
file /dev/kmsg w
onrestart setprop servicemanager.ready false
onrestart restart --only-if-running apexd
onrestart restart audioserver
onrestart restart gatekeeperd
onrestart class_restart --only-enabled main
onrestart class_restart --only-enabled hal
onrestart class_restart --only-enabled early_hal
task_profiles ServiceCapacityLow
shutdown critical
由rc脚本得知,ServiceManager对应的二进制可执行文件为
/system/bin/servicemanager
。
我们故技重施,和第6篇zygote一样,在Android.bp中找到了ServiceManager模块的定义。
// frameworks/native/cmds/servicemanager/Android.bp
cc_binary {
name: "servicemanager",
defaults: ["servicemanager_defaults"],
init_rc: ["servicemanager.rc"],
srcs: ["main.cpp"],
bootstrap: true,
}
入口理所当然就是
main.cpp
的main函数。
// frameworks/native/cmds/servicemanager/main.cpp
int main(int argc, char** argv) {
... ...
// 从启动参数中获取binder设备文件位置,默认为/dev/binder
const char* driver = argc == 2 ? argv[1] : "/dev/binder";
// ① 创建ProcessState实例,并将其指针赋给static sp gProcess
// ProcessState实例是进程单例,主要负责Binder相关的初始化配置
sp ps = ProcessState::initWithDriver(driver);
// 通过系统调用将Binder线程池最大线程数设置为0,即不使用线程池
ps->setThreadPoolMaxThreadCount(0);
... ...
// ② 创建ServiceManager实例,并将自己添加为Service
sp manager = sp::make(std::make_unique());
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
LOG(ERROR) << "Could not self register servicemanager";
}
// ③ 将ServiceManager与进程、线程管理类关联起来
IPCThreadState::self()->setTheContextObject(manager);
ps->becomeContextManager();
// ④ 启动循环
sp looper = Looper::prepare(false /*allowNonCallbacks*/);
BinderCallback::setupTo(looper);
ClientCallbackCallback::setupTo(looper, manager);
... ...
while(true) {
looper->pollAll(-1);
}
// should not be reached
return
EXIT_FAILURE;
}
总体来看,main函数干了4件事:
• 创建ServiceManager实例,并将自己添加为Service。
• 将ServiceManager与进程、线程管理类关联起来。
① 我们先看看ProcessState的构造函数做了些什么(我们跳过了单例创建ProcessState的模板代码)。
// frameworks/native/libs/binder/ProcessState.cpp
ProcessState::ProcessState(const char* driver)
: mDriverName(String8(driver)),
mDriverFD(-1),
mVMStart(MAP_FAILED),
... ...
// 线程池最大线程数
mMaxThreads(DEFAULT_MAX_BINDER_THREADS),
mCurrentThreads(0),
mKernelStartedThreads(0),
... ...
mThreadPoolStarted(false),
mThreadPoolSeq(1),
mCallRestriction(CallRestriction::NONE) {
// 打开Binder设备文件
base::Result<int> opened = open_driver(driver);
if (opened.ok()) {
// 通过系统调用mmap将用户虚拟空间和内核虚拟空间映射到同一块物理内存上
// 该内存区域大小为BINDER_VM_SIZE(1M - (内存页大小 * 2)),即Binder通信传输内容的大小限制
// Android 14及之前内存页大小都为4K
mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE,
opened.value(), 0);
... ...
}
... ....
if (opened.ok()) {
mDriverFD = opened.value();
}
}
② 再来看看ServiceManager,它的类结构如下:
可以看出,ServiceManager也是一个BBinder,这与我们上一篇的结论吻合:BBinder就是Binder接口本身在native层的形态。因为ServiceManager是管理Service的类,所以它作为这些Service的Server端,提供注册、查询的功能,非常符合逻辑。此外,ServiceManager本身的业务接口由IServiceManager定义,比如:注册函数、查询函数等。
接着,ServiceManager调用了
addService
函数将自己添加到Service集合中。
// frameworks/native/cmds/servicemanager/ServiceManager.cpp
Status ServiceManager::addService(const std::string& name, const sp& binder, bool allowIsolated, int32_t dumpPriority) {
... ...
// 将Service添加到map<:string>中,其中Service是代表服务的结构体,binder字段存放IBinder接口指针
mNameToService[name] = Service{
.binder = binder,
.allowIsolated = allowIsolated,
.dumpPriority = dumpPriority,
.ctx = ctx,
};
... ...
return Status::ok();
}
③ 将ServiceManager作为BBinder保存到IPCThreadState的字段
the_context_object
中。IPCThreadState是线程单例,它与ProcessState类作用类似,但它保存Binder线程相关的属性且负责与内核的数据交互。接着调用
ProcessState#becomeContextManager
将其在Binder驱动中注册为上下文管理者。讲人话就是告诉Binder驱动当前进程是ServiceManager进程,以后在用户空间中这个进程就是Binder大总管。
// frameworks/native/libs/binder/ProcessState.cpp
bool ProcessState::becomeContextManager() {
... ...
flat_binder_object obj {
.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
};
// 通过系统调用注册Binder上下文管理者
int result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);
... ...
return result == 0;
}
④
Looper#prepare
会通过系统调用
epoll_create1()
创建一个eventpoll实例。
BinderCallback#setupTo
向eventpoll注册Bidner设备文件描述符,这样当设备文件发生变化时,就会调用
BinderCallback#handleEvent() -> IPCThreadState#handlePolledCommands() -> getAndExecuteCommand()
。
getAndExecuteCommand
在上一篇中我们提到过,它读取驱动发送的消息,根据通信指令进行处理。
// frameworks/native/cmds/servicemanager/main.cpp
class BinderCallback : public LooperCallback {
public:
static sp setupTo(const sp& looper) {
// 创建回调
sp cb = sp::make();
int binder_fd = -1;
// 将Binder设备文件描述符赋给binder_fd
IPCThreadState::self()->setupPolling(&binder_fd);
LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd);
// 将Binder设备文件描述符注册到Looper监听,并传入callback
// 当有Binder驱动的消息来临时,handleEvent函数就会被回调
int ret = looper->addFd(binder_fd,
Looper::POLL_CALLBACK,
Looper::EVENT_INPUT,
cb,
nullptr /*data*/);
LOG_ALWAYS_FATAL_IF(ret != 1, "Failed to add binder FD to Looper");
return cb;
}
int
handleEvent(int /* fd */, int /* events */, void* /* data */) override {
IPCThreadState::self()->handlePolledCommands();
// 返回1表示继续接收回调,返回0表示结束接收回调
return 1;
}
};
ClientCallbackCallback#setupTo
向eventpoll注册一个定时文件描述符,该文件每5秒发生一次变化,变化时会回调
ClientCallbackCallback#handleEvent -> ServiceManager#handleClientCallbacks
。
handleClientCallbacks
会通知Server端当前是否有Client在使用它。当然前提是Server端需要主动调用
registerClientCallback
接口注册回调。
// frameworks/native/cmds/servicemanager/main.cpp
class ClientCallbackCallback : public LooperCallback {
public:
static sp setupTo(const sp& looper, const sp& manager) {
// 创建回调
sp cb = sp::make(manager);
// 创建一个定时文件描述符
int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);
itimerspec timespec {
.it_interval = {
.tv_sec = 5,
.tv_nsec = 0,
},
.it_value = {
.tv_sec = 5,
.tv_nsec = 0,
},
};
// 设置interval为5秒
int timeRes = timerfd_settime(fdTimer, 0 /*flags*/, ×pec, nullptr);
LOG_ALWAYS_FATAL_IF(timeRes < 0, "Failed to timerfd_settime: res: %d err: %d", timeRes, errno);
// 将文件描述符注册到Looper监听,并传入callback
// 当文件描述符变化时也就是5秒间隔到来时,handleEvent函数就会被回调
int addRes = looper->addFd(fdTimer,
Looper::POLL_CALLBACK,
Looper::EVENT_INPUT,
cb,
nullptr);
LOG_ALWAYS_FATAL_IF(addRes != 1, "Failed to add client callback FD to Looper");
return cb;
}
int handleEvent(int fd, int /*events*/, void* /*data*/) override {
uint64_t expirations;
int ret = read(fd, &expirations, sizeof(expirations));
if (ret != sizeof(expirations)) {
ALOGE("Read failed to callback FD: ret: %d err: %d", ret, errno);
}
mManager->handleClientCallbacks();
return 1;
}
... ...
};
最后,调用
Looper#pollAll
启动消息循环。
ServiceManager的启动并不复杂,主要是通过ioctl系统调用跟Binder驱动交互,完成一系列的初始化工作,最后启动循环等待处理消息。但其中有一个很大的疑点:为什么要将线程池数目设置为0?ServiceManager作为Binder通信的Server端之一,为什么没有像上一篇讲的那样采用线程池的方案来处理消息?
-
1. 通信频率低:ServiceManager仅仅在添加Service和通道建立阶段(Client端一般都会缓存来自Server端的Proxy)有一定的通信量,真正的C/S通信时无需ServiceManager参与,因此它不需要线程池支持,也能完成使命。
-
2. 复用需求低:ServiceManager作为一个特殊的Binder Server端,它的生命周期几乎与整个系统相当,但它的复用性需求并不高。反观普通的Binder Server端,通信次数可能很多,但通信完成之后不再需要,为了减少线程创建回收的性能损耗,线程池的高效利用是最合理的方案。
上一篇我们讲Binder接口的序列化时就提到通过
ActivityManager.getService()
可以获得IActivityManager接口,这是AMS对外开放的远程接口。那么我们就以IActivityManager的获取为例,看看获取Service接口的流程。
// frameworks/base/core/java/android/app/ActivityManager.java
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton IActivityManagerSingleton =
new Singleton() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
// frameworks/base/core/java/android/os/ServiceManager.java
public static IBinder getService(String name) {
try {
// 尝试从缓存中获取IActivityManager,若没有,则通过rawGetService函数获取
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(rawGetService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
private static IBinder rawGetService(String name) throws RemoteException {
... ...
// 首先通过getIServiceManager获取ServiceManager的BpBinder,之后再调用getService获取IActivityManager
final IBinder binder = getIServiceManager().getService(name);
... ...
return binder;
}
具体看看ServiceManager的Proxy类是怎么获取的。
// frameworks/base/core/java/android/os/ServiceManager.java
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative
.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}
// frameworks/base/core/jni/android_util_Binder.cpp
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz) {
// 获取BpBinder指针
sp b = ProcessState::self()->getContextObject(NULL);
// javaObjectForIBinder我们在上一篇中已经分析过
// 它的主要作用就是根据传入的IBinder创建Java层的BinderProxy并返回
return javaObjectForIBinder(env, b);
}
// frameworks/native/libs/binder/ProcessState.cpp
sp ProcessState::getContextObject(const sp& /*caller*/) {
// getStrongProxyForHandle也是上一篇分析过的函数,作用是创建BpBinder实例并将传入的handle保存起来
// handle可以简单理解为S端接口的编号,binder驱动将其与真正的远端接口映射起来,实现关联
sp context = getStrongProxyForHandle(0);
... ...
return context;
}
可以看到这里的handle直接硬编码为0,这就是从无到有跟ServiceManager取得联系的关键。Binder机制默认ServiceManager的handle为0,是驱动在内核中创建的第一个Binder节点。
到这里,我们已经获得了ServiceManager的Proxy,回到
rawGetService
函数中,接下来就是调用
getService
函数获取IActivityManager接口。
// frameworks/base/core/java/android/os/ServiceManager.java
private static IBinder rawGetService(String name) throws RemoteException {
... ...
// getIServiceManager()最终返回经过包装的ServiceManagerProxy,调用它的getService
final IBinder binder = getIServiceManager().getService(name);
... ...
return binder;
}
// frameworks/base/core/java/android/os/ServiceManagerNative.java
public IBinder getService(String name) throws RemoteException {
// mServierManger为远端接口,因此ServiceManager.cpp中必定有checkService的函数实现
return mServiceManager.checkService(name);
}
调用过程我们已经很熟悉了,这里就不跟踪了,直接看
checkService
的函数实现。
// frameworks/native/cmds/servicemanager/ServiceManager.cpp