binder驱动-------之内存映射篇

1:binder内存管理概述

binder一次跨进程通讯,只需要一次拷贝(原因后面会解析),而一般的像socket通讯则需要两次拷贝;参与binder通讯的进程,无论是client还是服务器端,他们都会通过调用ProcessState::self()函数来建立自己的初步映射,为什么说是初步影射呢,因为binder_mmap为我们分配了指定长度的虚拟地址,但却只是建立一个物理页的影射,其他的虚拟地址都还暂未建立虚拟到物理的影射。下面我们的讲述从ProcessState::self()函数开始,该函数使用典型的单列模式:如已创建该实例则直接返回,如果没有创建,则创建返回这个实例,这里需要锁来防止创建两个同类型的实例,该函数还是static类型的,所以可以在系统的任何地方调用。

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState;
    return gProcess;
}

展开构建函数:ProcessState,如下:

ProcessState::ProcessState()
    : mDriverFD(open_driver())//打开/dev/binder设备驱动
    , mVMStart(MAP_FAILED)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
        // XXX Ideally, there should be a specific define for whether we
        // have mmap (or whether we could possibly have the kernel module
        // availabla).
#if !defined(HAVE_WIN32_IPC)
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);//建立影射,直接调用到驱动的binder_mmap函数
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
            close(mDriverFD);
            mDriverFD = -1;
        }
#else
        mDriverFD = -1;
#endif
    }

    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
}

以上函数:打开了/dev/binder设备节点,并且建立了初步的映射,binder driver中的mmap函数具体分析见下一节。

先大概说下,整个函数执行完后,都做了些什么事情:

为进程的用户和内核空间分配了一段虚拟地址,这段虚拟地址将用于binder内存的访问。binder的物理内存页由binder驱动负责分配,然后这些物理页的访问,将分为进程的用户空间和进程内核空间。由于进程的用户空间和内核空间的虚拟地址范围是不一样的,所以这里分配的一段虚拟地址将包括进程的用户空间地址和内核空间地址。

并且在映射建立初始阶段,只是映射了一个物理页,而不是为整个虚拟地址空间都建立相应的物理影射,目的是充分高效的利用内存,只是到需要用到的时候才建立映射,用完后马上释放映射,这样可以充分高效的利用系统的物理内存页。

内核刚开始只是分配了一个物理页,并且分别将这个物理页映射到进程的内核虚拟地址空间V1(修改内核空间的页表映射)和进程的用户虚拟地址空间V2(修改用户空间的页表映射)。在用户空间访问V1和在内核空间访问V2,其实都是访问的是同一个物理内存块,从而实现进程的内核和用户空间共享同一块物理内存的目的。这样binder驱动在内核空间,将一段数据拷贝到这个物理页,则该进程的用户空间则不需要copy_to_user()即可以同步看到内核空间的修改,并能够访问这段物理内存。

 

每个进程都有属于自己的一块binder内存,这块内存的大小就是在进程调用mmap系统调用时分配的,binder_proc->buffer指向这块内存在内核空间的首地址,这块内存块在进程的用户空间的首地址为:ProcessState::mVMStart,他们的差值存储在binder_proc->user_buffer_offset变量中,这样在已知内核地址情况下,在用户空间只需要加上这个user_buffer_offset偏移值,即可以得到在用户空间访问的地址,从而达到访问同一物理内存地址的目的。

 

每个进程的binder内存,组织在以下三个列表中:

  • struct list_head buffers;//这个列表连接所有的内存块,以地址的大小为顺序,各内存块首尾相连
  • struct rb_root free_buffers;//连接所有的已建立映射的虚拟内存块,以内存的大小为index组织在以该节点为根的红黑树下
  • struct rb_root alloca
  • 12
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值