Binder

Binder 作为Android系统IPC的通信方式,整体是比较复杂的,涉及到智能指针,Parcel,JNI等知识点; 另外,更是有很多细节,熟悉这些细节可以更好的了解Binder,但是一头埋进细节里面,可能会很迷茫; 所以本篇尽量剥离细节,试图从整体角度理解Binder,希望能给大家带来帮助。

1. Android系统的IPC(internal process communication)

进程是操作系统层面的概念,进程的好处有很多,显著的一点就是进程有了自己私有的内存空间,从而保证了进程内数据的安全,但是却为进程间的通信带来了不便。为了解决这个问题,不同的操作系统采用了很多不同的方案,Binder便是Android特有的方案,当然Android是以Linux为内核的操作系统,所有UNIX类型的进程间通信方式( file-system, local sockets或者signals)依然可以使用。
下面是Android常用的IPC方式,其中Services的实现也是基于binder:

  • Binder
  • Services(基于binder)
  • Intents
  • ContentProviders

从Android O开始,Android framework 和HALs也要通过binder来通信,这样binder的负荷就增加不少,Android O为了保持binder通信的效率,引入了很多改进,其中就包括增加binder节点,各个节点用于处理不同的需求,这样就分担了每个节点的负担。

IPC Domainservice managerDescription
/dev/binderservicemanagerIPC between framework/app processes with AIDL interfaces
/dev/hwbinderhwservicemanagerIPC between framework/vendor processes with HIDL interfaces IPC between vendor processes with HIDL interfaces
/dev/vndbindervndservicemanagerIPC between vendor/vendor processes with AIDL Interfaces

“/dev/binder"等节点,并不对应物理设备。”

下面是两个进程使用Binder通信的抽象图,直观上就是两个进程在直接通信,看不到Binder驱动的存在。
这里写图片描述

2.Binder如何实现进程间通信

Binder通过共享内存的方式实现进程间的通信。
这里写图片描述

新进程启动时,会调用binder_open打开Binder驱动,并调用mmap()函数将binder驱动内存映射到进程内存中。Binder驱动内部会生成一份该进程相关的记录,记录使用的便是”drivers/android/binder.c“中定义的binder_proc结构体,有了这样的记录,Binder驱动就能找到每个进程。
进程B启动时,调用mmap()函数将binder驱动内存映射到进程B内存中,mmap函数返回的虚拟地址,经过逻辑地址转换后会指向物理内存的某个位置; 对于binder驱动,它也有一个指针(binder_proc->alloc.buff)指向某个虚拟内存地址,经过逻辑地址转换后,它和进程B指向物理内存的同一位置。同理,进程A也是一样。进程A通过Binder和进程B通信,首先Binder驱动会找到进程B对应的记录(binder_proc),然后调用copy_from_user将进程A中的数据复制到binder_proc->alloc.buff所指向的内存中,由于进程B也可以访问binder_proc->alloc.buff虚拟地址所对应的物理内存块,所以就可以获取到相关数据,实现了数据的共享。

3.Binder框架

Binder涉及到了Java、JNI、Native和kernel; 下图展现了层次结构以及相关主要文件:
这里写图片描述

  • Java层,IBinder.java定义了IBinder接口; Binder.java定义了Binder类和BinderProxy类,两者都实现了IBinder接口。Binder类用作server;BinderProxy类用作Client,Java层拿到的IBinder类型的对象其实都是BinderProxy对象,发送的请求也会经BinderProxy的transact方法进一步向下传递。
  • JNI层,android_util_Binder.cpp实现了Binder.java中的native方法,是连接Java层和Native层的桥梁。
  • Native层,应用进程被创建的时候ProcessState.self()会被调用,新的ProcessState对象会被创建,同时open_driver(char * )和mmap函数会依次被调用,分别用于打开/dev/binder节点和做内存映射。open_driver函数会向Binder驱动中设置最大线程数量(15)。
    一个进程里面有很多线程,而Binder IPC又是阻塞式的,所以每个线程都要可以和Binder驱动通信,这样才不至于进程被block,IPCThreadState.cpp就是用来和Binder驱动进行通信的。
    BpBinder和BinderProxy对应,客户端的请求会通过transact@BinderProxy发送,JNI层会根据BinderProxy对象,找到对应的BpBinder对象,然后调用transact@BpBinder。
    Binder.cpp里面定义了BBinder类,JavaBBinder继承了BBinder类;android.os.Binder对象被跨进程传递时,JavaBBinder对象会被创建,也会被保存在Binder驱动中,这个对象可以用于查找android.os.Binder对象,即Server。
  • kernel层就是Binder驱动。
4.servicemanager

我们常用的Binder通信方式有下面两种:

  • 1.编写AIDL,实现服务端(server); 然后借助bindService获取到服务端代理,使用代理访问服务端(server)。

  • 2.通过"ServiceManager.get(“XXX”)"获取到服务端代理,然后使用代理访问服务端(server)。
    下面我们说说第二种方式,下图是使用ServiceManager的一个简单流程图(绝对够糙):
    这里写图片描述
    图中,Client process是一个进程,"Target binder server"运行在一个单独的进程中,
    "ServiceManager"是指"system/bin/servicemanager"进程。

  • 1. 如红线所示,Client process通过/dev/binder向ServiceManager查询Binder server,ServiceManager通过/dev/binder将查询到的结果返回给Client process。

  • 2. 如蓝线所示,经过第一步Client process会拿到一个BinderProxy对象,Client process通过这个BinderProxy对象通过/dev/binder可以访问到Binder server。

ServiceManger扮演Service管家的角色,同时它也是一个特殊的Service(Handle为0)。
其实每个binder节点都有一个manager(context),把上面的表格搬过来看看:

IPC Domainservice managerDescription
/dev/binderservicemanagerIPC between framework/app processes with AIDL interfaces
/dev/hwbinderhwservicemanagerIPC between framework/vendor processes with HIDL interfaces IPC between vendor processes with HIDL interfaces
/dev/vndbindervndservicemanagerIPC between vendor/vendor processes with AIDL Interfaces

"/dev/binder"的manager(context)就是servicemanager,那么servicemanager是怎么工作的呢?

servicemanager对应的代码路径是”frameworks/native/cmds/servicemanager/“
这里写图片描述
Android.bp会将service_manager.c和binder.c两个源文件编译到servicemanager可执行文件,对应的rc文件是servicemanager.rc。开机时Init进程会根据servicemanager.rc启动servicemanager可执行文件,入口函数即是service_manager.c中main函数,这就是"system/bin/servicemanager"进程。
另外,在这个目录中还有vndservicemanager.rc, 仔细查看Android.bp, 可以发现vndservicemanager可以执行文件也是用同样的源码编译生成的,只不过在vndservicemanager.rc中配置了启动时的参数"/dev/vndbinder", 内容如下:

service vndservicemanager /vendor/bin/vndservicemanager /dev/vndbinder
    class core
    user system
    group system readproc
    writepid /dev/cpuset/system-background/tasks
    shutdown critical

service_main.c的main函数如下:

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);
    if (!bs) {
        ...
    }

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

    return 0;
}

为了节省篇幅,上面的代码省略了一些代码.
main函数主要完成了三个工作:

  • 1.调用binder_open打开/dev/binder节点,并将binder驱动内存映射到进程内存空间中。进程调用binder_open打开/dev/binder节点后,Binder驱动内部会生成一份该进程相关的记录。
  • 2.调用binder_become_context_manager,将自己注册为Binder管家。
  • 3.调用binder_loop开启循环,接收客户端的请求(getService, addService等)。

"hwbinder ServiceManager"的代码路径是system/hwservicemanager/


结束
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值