专栏名称: 鸿洋
你好,欢迎关注鸿洋的公众号,每天为您推送高质量文章,让你每天都能涨知识。点击历史消息,查看所有已推送的文章,喜欢可以置顶本公众号。此外,本公众号支持投稿,如果你有原创的文章,希望通过本公众号发布,欢迎投稿。
目录
相关文章推荐
开发者全社区  ·  币圈大瓜! ·  19 小时前  
开发者全社区  ·  北京CBD最近发生了一件大事 ·  昨天  
鸿洋  ·  再学安卓 - ... ·  昨天  
开发者全社区  ·  又一批空姐倒下了 ·  2 天前  
开发者全社区  ·  北大国发院博士去向 ·  2 天前  
51好读  ›  专栏  ›  鸿洋

再学安卓 - binder之ServiceManager

鸿洋  · 公众号  · android  · 2025-02-19 08:35

正文

本文分析基于Android14(aosp分支android-14.0.0_r28)

系列文章:

再学安卓 - binder之Framework通信流程

再学安卓 - APP进程

再学安卓 - SystemServer
再学安卓 - Zygote

再学安卓 - init进程


前言

上一篇我们对Binder通信的流程有了大致的了解,但始终有一个问题萦绕在我们心里,CountBinder需要AMS和APP建立的已有通道才能传输,那么AMS和APP之间的通道是怎么建立,系统中的第一个Binder通道是怎么建立的?我们可能很自然就想到上一篇提到过的ServiceManager,作为管理模块,它应该就是那个创始者。

1
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件事:

• 创建ProcessState实例。
• 创建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. 1. 通信频率低:ServiceManager仅仅在添加Service和通道建立阶段(Client端一般都会缓存来自Server端的Proxy)有一定的通信量,真正的C/S通信时无需ServiceManager参与,因此它不需要线程池支持,也能完成使命。
  2. 2. 复用需求低:ServiceManager作为一个特殊的Binder Server端,它的生命周期几乎与整个系统相当,但它的复用性需求并不高。反观普通的Binder Server端,通信次数可能很多,但通信完成之后不再需要,为了减少线程创建回收的性能损耗,线程池的高效利用是最合理的方案。

2
获取Service


上一篇我们讲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






请到「今天看啥」查看全文


推荐文章
开发者全社区  ·  币圈大瓜!
19 小时前
开发者全社区  ·  北京CBD最近发生了一件大事
昨天
开发者全社区  ·  又一批空姐倒下了
2 天前
开发者全社区  ·  北大国发院博士去向
2 天前
机器学习研究会  ·  2017年4月历史文章汇总
7 年前