2020-02-17-Android的Binder通信原理

2020-04-04  本文已影响0人  耿望

Binder通信是一个比较大的架构,从内核跟native源码角度分析的文章很多,今天准备写一下大概原理,适合跟我一样的新手看。

C/S架构

Binder通信整体架构是我们很熟悉的客户端+服务端模式,客户端进程得到一个BpBinder,也就是binder的代理对象,通过代理对象向内核缓冲区读写数据。服务端得到一个BBinder,也就是binder的本地对象,通过本地对象向内核缓冲区读写数据,这样就实现了跨进程通信。
下面画了一张图:


Binder原理.jpg

内存映射

我们理解一般的跨进程通信需要两次数据拷贝,而Binder只需要一次,这是为什么呢?
在Linux系统中,每个进程之间是独立的,各自拥有自己的内存空间,分为用户空间和内核空间。
如果需要实现通信,就需要把进程A的数据拷贝到内核空间,然后再从内核空间拷贝到进程B。
而binder建立了一个映射关系,把内核缓冲区的数据映射到进程A和进程B,它们的物理地址是一样的,从而省去了一次数据拷贝的过程。
下面画了一张图


IPC数据拷贝.jpg

Binder服务的注册与获取

Binder通信还有一个比较特殊的地方,就是所有的服务都需要通过ServiceManager向Binder驱动注册。
ServiceManager本身也是一个Binder服务,它的本地实现代码都在service_manager.c文件中,同时向外提供了ISeriviceManager接口,开发者可以通过IServiceManager接口获取到一个BpServiceManager对象,通过它来addService或者getService。
下面画了一张图,写了ServiceManager启动,注册跟获取服务的过程:


ServiceManager.jpg

ServiceManager启动过程

我们看下ServiceManager的main方法,初始化的过程做了以下几件事:
1.打开binder驱动,并调用mmap()方法分配内存映射空间:binder_open();
2.通知binder驱动使其成为守护进程:binder_become_context_manager();
3.验证selinux权限,判断进程是否有权注册或查看指定服务;
4.进入循环状态,等待Client端的请求:binder_loop()。
/frameworks/native/cmds/servicemanager/service_manager.c

int main(int argc, char** argv)
{
    struct binder_state *bs;
    union selinux_callback cb;
    char *driver;

    if (argc > 1) {
        driver = argv[1];
    } else {
        driver = "/dev/binder";
    }

    bs = binder_open(driver, 128*1024);//1
    if (!bs) {
#ifdef VENDORSERVICEMANAGER
        ALOGW("failed to open binder driver %s\n", driver);
        while (true) {
            sleep(UINT_MAX);
        }
#else
        ALOGE("failed to open binder driver %s\n", driver);
#endif
        return -1;
    }

    if (binder_become_context_manager(bs)) {//2
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
#ifdef VENDORSERVICEMANAGER
    cb.func_log = selinux_vendor_log_callback;
#else
    cb.func_log = selinux_log_callback;
#endif
    selinux_set_callback(SELINUX_CB_LOG, cb);
//3
#ifdef VENDORSERVICEMANAGER
    sehandle = selinux_android_vendor_service_context_handle();
#else
    sehandle = selinux_android_service_context_handle();
#endif
    selinux_status_open(true);

    if (sehandle == NULL) {
        ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
        abort();
    }

    if (getcon(&service_manager_context) != 0) {
        ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
        abort();
    }

    binder_loop(bs, svcmgr_handler);//4

    return 0;
}

获取ServiceManager的代理对象BpServiceManager

接着开发者可以通过IServiceManager接口,获取到ServiceManager的代理对象。
/frameworks/native/libs/binder/IServiceManager.cpp

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != nullptr) return gDefaultServiceManager;

    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == nullptr) {//1
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(nullptr));//2
            if (gDefaultServiceManager == nullptr)
                sleep(1);//3
        }
    }

    return gDefaultServiceManager;
}

这里是一个单例模式,如果gDefaultServiceManager存在就直接返回,否则创建新的gDefaultServiceManager,它就是BpServiceManager。
注释1 处用了一个while循环,保证gDefaultServiceManager创建成功;
注释2 处获取了三个重要的对象:
(1)通过self函数获取一个ProcessState对象;
(2)通过getContextObject获取了一个BpBinder对象;
(3)通过interface_cast获取了一个BpServiceManager对象;
注释3 处如果创建不成功,sleep1秒后再次尝试创建。

向Binder驱动注册服务

通过上面拿到的BpServiceManager,我们可以向Binder驱动注册自己的服务了。这里通过Parcel将数据序列化之后写入内存。
/frameworks/native/libs/binder/IServiceManager.cpp

    virtual status_t addService(const String16& name, const sp<IBinder>& service,
                                bool allowIsolated, int dumpsysPriority) {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        data.writeInt32(dumpsysPriority);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }

客户端操作是通过BpBinder代理对象,而实际通信操作都是在IPCThreadState类中完成的。
/frameworks/native/libs/binder/BpBinder.cpp

// NOLINTNEXTLINE(google-default-arguments)
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

通过ServiceManager获取服务

获取服务的过程类似上面,也是通过代理对象获取的,这里就不介绍源码了,画了一下上层java接口的流程。


Binder java.jpg

服务端异常死亡处理

可以通过实现IBinder.DeathRecipient接口,来监听服务端死亡通知。

private class BinderDeathRecipient implements DeathRecipient {
    @Override
    public void onBinderDied() {
        //
    }
}

然后将获取到的IBinder对象,通过IBinder.linkToDeath()方法链接到DeathRecipient。

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "onServiceConnected ComponentName=" + name);
            mReporterBinder = service;
            try {
                sercie.linkToDeath(new BinderDeathRecipient(), 0);
            } catch (RemoteException e) {
                 //
             }
        }

参考:

Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析
彻底理解Android Binder通信架构
Binder系列—开篇
Android深入浅出之Binder机制
Binder死亡通知机制之linkToDeath
Android多进程之Binder的意外死亡及权限校验

上一篇 下一篇

猜你喜欢

热点阅读