IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android-binder通信详解 -> 正文阅读

[移动开发]Android-binder通信详解

1. binder是什么?

binder是安卓系统的进程间通信方式。

2. 为什么安卓要用binder?

Android内核是基于Linux系统,linux本身就有很多种进程间通信方式: 内存共享,消息队列、信号量等,为什么安卓还要用binder呢?

传统ipc(进程间通信):

共享内存,不需要内存拷贝,但是控制繁琐。

管道通信,需要两次内存拷贝。

?

binder通信:

Binder只需要一次拷贝是因为安卓的内存映射方法,也就是mmap。a进程发数据给b进程,a进程把数据拷贝给mmap开辟的内核空间,b进程通过mmap就可以取出来这个数据而不用重新拷贝。

?

?

3. binder的实现:

binder的实现是基于内存映射的,也就是上面说的mmap。

这里我们首先要理解用户空间和内核空间。内核空间可以访问所有的进程页表。也就是内核空间可以访问所有的用户空间。也是基于这个前提,进程间通信才可以实现。

要理解mmap必须先理解物理地址、虚拟地址和页表的概念。

物理地址:

物理地址空间是实在的存在于计算机中的一个实体,在每一台计算机中保持唯一独立性。我们可以称它为物理内存;如在32位的机器上,物理空间的大小理论上可以达到2^32字节(4GB)。

虚拟地址:

虚拟地址并不真实存在于计算机中。每个进程都有4G的虚拟地址空间,其中3G用户空间,1G内核空间(linux),每个进程共享内核空间,独立的用户空间。每个进程都有自己独立的虚拟地址空间。这样每个进程都能访问自己的地址空间,这样做到了有效的隔离。

?虚拟地址里面存放的什么内容?

?mmap的作用是映射文件描述符和指定文件的(off_t off)区域至调用进程的(addr,addr *len)的内存区域,如下图所示:

?binder的整个流程:

?

4. binder的通信流程

?

?

整个安卓的进程间通信都是基于c/s架构的,就是client / server 架构。

系统启动时servicemanager作为0号进程先启动,然后会读取rc文件,把系统中其他的进程依次启动。

server启动的时候就把自己注册到servicemanager里面,servicemanager里面会维护一个services的list。

client端想要和service通信就会先获取servicemanager服务,然后从servicemanager里面通过service name获取对应的service的binder,拿到了这个binder client端就可以通过这个binder和service通信了。

具体过程如下:

?4.1 server注册进程:

此处以mediaplayer为例:

int main(int argc __unused, char **argv __unused)
{
    signal(SIGPIPE, SIG_IGN);
  // 获取service manager的代理供IPC使用。
    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm(defaultServiceManager());
    ALOGI("ServiceManager: %p", sm.get());
    InitializeIcuOrDie();

    MediaPlayerService::instantiate(); // 注册service,这里是注册了一个binder
    ResourceManagerService::instantiate(); // 这里和上面一样

    // 这块是调用binder driver,底层实现是一个thread pool就是一个死循环
    ProcessState::self()->startThreadPool(); 
    IPCThreadState::self()->joinThreadPool();
}

所有的::instantiate()函数都是static型函数,且分别是创建各个子模块的对象实例。由此可看出mediaserver共分2个子模块: MediaPlayerService & ResourceManagerService。

void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService( // 注册server,server name为media.player
            String16("media.player"), new MediaPlayerService());
}

?server注册到servermanager的时候会调用frameworks/native/cmds/servicemanager/service_manager.c中的do_add_service。这个函数中首先检查是否有权限注册service,没权限就会报错返回;然后检查是否已经注册过,注册过的service将不能再次注册。然后构造一个svcinfo对象,并加入一个全局链表中svclist中。最后通知binder设备:有一个service注册进来。

Tips: server一定是一个死循环,一直在等待client的调用,我们自己写的server一般是while死循环之类的。安卓server的死循环在binder driver里面,我们在server代码里面看不到,但是只要server调用了startThreadPool就会有一个死循环在底层跑着。

4.2 service启动流程

首先,每个service都要有对应的rc文件,里面说明了这个service启动需要加载的文件流程:

以mediaserver.rc为例:

service media /system/bin/mediaserver
    class main
    user media
    group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
    ioprio rt 4
    task_profiles ProcessCapacityHigh HighPerformance
    writepid /dev/memcg/camera/media/service/cgroup.procs

有了这个rc文件,系统在启动的时候就会调用system/bin/mediaserver这个可执行文件。这个文件里面的实现就是上面的main函数,然后就会把MediaPlayerServiceResourceManagerService注册进去。

4.3 client调用流程

下面以MediaCodecList为例:

// frameworks/av/media/libstagefright/MediaCodecList.cpp
sp<IMediaCodecList> MediaCodecList::getInstance() {
    Mutex::Autolock _l(sRemoteInitMutex);
    if (sRemoteList == nullptr) {
       // 此处就是通过servicemanager获取name为"media.player"的server的binder
        sp<IBinder> binder =
            defaultServiceManager()->getService(String16("media.player")); 
        // 拿到了binder就可以访问service的接口了
        sp<IMediaPlayerService> service =
            interface_cast<IMediaPlayerService>(binder);
        if (service.get() != nullptr) {
            sRemoteList = service->getCodecList();
            if (sRemoteList != nullptr) {
                sBinderDeathObserver = new BinderDeathObserver();
                binder->linkToDeath(sBinderDeathObserver.get());
            }
        }
        if (sRemoteList == nullptr) {
            // if failed to get remote list, create local list
            sRemoteList = getLocalInstance();
        }
    }
    return sRemoteList;
}

client端调用的就是IMediaPlayerService中的函数,这个IMediaPlayerService就是接口类,bn和bp都会继承这个类。client端调用的是bp的代码,然后bp通过binder调用到bn。

class BpMediaPlayerService: public BpInterface<IMediaPlayerService> {
    virtual sp<IMediaCodecList> getCodecList() const {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
        remote()->transact(GET_CODEC_LIST, data, &reply); 
        // remote这里就是通过binder调用从client端调用server端
        return interface_cast<IMediaCodecList>(reply.readStrongBinder());
    }
}
status_t BnMediaPlayerService::onTransact(...) {
     case GET_CODEC_LIST: {
          sp<IMediaCodecList> mcl = getCodecList();
          reply->writeStrongBinder(IInterface::asBinder(mcl));
          // 这里就会通过binder调用把server端的调用结果返回给client端
     }
}

sp<IMediaCodecList> MediaPlayerService::getCodecList() const {
    return MediaCodecList::getLocalInstance();
}

4.4 一个问题: MediaPlayerService和ResourceManagerService属于一个进程吗?

这里首先理解一个概念: 代码中的service究竟是什么?

class MediaPlayerService : public BnMediaPlayerService
class BnMediaPlayerService: public BnInterface<IMediaPlayerService>
class IMediaPlayerService: public IInterface

service是一个继承了interface的具体实现,用于和client通信。server端和client端同时继承interface类,一个是bp一个是bn。

所以呢?service和binder有啥关系? service和进程有啥关系?

service调用了instantiate函数就是把自己注册到了servicemanager里面,这个时候service就是一个binder接口。

注意:直到这里都没有进程的概念。service 不等于 进程。

那么进程是怎么来的?

看看service的启动流程, 在servicemanager加载rc文件的时候,这个rc文件里面调用的是/system/bin/mediaserver,一个可执行文件,可执行文件在系统中是什么?是一个单独的进程。到这里就破案了。

MediaPlayerService和ResourceManagerService都在/system/bin/mediaserver中。所以他们肯定属于同一个进程,但是他们分别注册到了service manager中,所以他们是两个binder。

5. 安卓的treble机制

在Android 8.0开始,Android引入了Treble的机制,为了方便Android系统的快速移植、升级,提升系统稳定性,Binder机制被拓展成了"/dev/binder", "/dev/hwbinder","/dev/vndbinder"。

三种binder的具体使用如下:

?

此处需要注意VNDK的两种概念:

在代码中,当一个so既需要在system分区使用又需要在vendr分区使用则声明该so为VNDK,然后so生成的时候就会产生两个, 一个在system目录,一个在vendor目录。

此处的vndk是两个vendor分区的进程通信用的。

?

6. 总结

? ?本文讲述了安卓binder通信。首先从linux已有进程通信存在的问题讲起,讲述了binder存在的必要,然后讲述了binder的实现流程,以及通过media player service为例讲述了在c/s架构中binder的具体使用过程。最后讲述了安卓中的treble机制,就是安卓对已有binder的几种扩展。下篇文章将通过audio finger和audio hal的通信再次讲述hwbinder的实现过程。敬请期待~

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-23 16:48:38  更:2021-08-23 16:50:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:50:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码