binder系列二-binder内存管理

binder的内存管理

Server端在启动之后,对/dev/binder设备调用mmap,内核中的binder_mmap函数进行对应的处理:申请一块物理内存,然后Server端的用户空间和内核空间同时进行映射;当Client端发送请求时,请求将数据从Client进程的用户空间拷贝到Server端的内核内存中,因为用户空间和内核空间对该块物理内存进行了同步映射,因此Server端用户空间也可以获得数据了,下面对Server端用户空间与内核空间的同步映射关系进行讲解:
下面为用户空间调用binder_open进行注册过程:

struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;
    bs = malloc(sizeof(*bs));
// 调用内核空间binder_open 生成binder_proc
    bs->fd = open(driver, O_RDWR | O_CLOEXEC);
    bs->mapsize = mapsize;
// 调用内核空间的binder_mmap 进行内存映射
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
}

mmap的前半部分的流程都是类似的,使用结构体vm_area_struct 来描述一个虚拟内存区域;在构造完毕vm_area_struct之后,如果有fd传入,就会判断当前文件描述符是否有mmap赋值,然后调用对应的内核mmap函数,大致的流程如下:在这里插入图片描述
对于binder来讲,即接下来调用binder_mmap函数,binder_proc中使用binder_alloc做内存管理,binder_mmap初始化过程主要是建立了vm_area_struct(用户空间)与binder_proc->alloc的关联;

  • struct binder_alloc的成员变量buffer指向vma->vm_start;
  • struct binder_alloc的成员变量vma指向vma;
  • struct binder_alloc的buffer_size 赋值为 min(vma->vm_end-vma->vm_start, 4M),所以一个进程用于binder通信最大是4M;
    在这里插入图片描述
    binder_alloc作为当前Server端的binder内存总管,通过struct binder_buffer和struct binder_lru_page对binder使用的内存进行管理;
struct binder_buffer

struct binder_buffer结构体用于对虚拟地址的管理,通过mmap函数之后,当前的进程分配获得一片虚拟内存地址进行binder操作,所以初始的binder_buffer是下面这个样子的

  • struct binder_buffer 成员变量user_data指向当前管理的虚拟地址的起始地址,每个binder_buffer会管理一块虚拟内存区域,由user_data指向起始地址;
  • struct binder_buffer 成员变量entry 将当前binder_buffer连接到struct binder_alloc的buffers链表上,每一个binder_buffer管理一块区域,根据产生的时间连接;
  • struct binder_buffer 成员变量rb_node用来将自己连接到binder_alloc的free_buffers或者是allocated_buffers的链表,free_buffers的key值为buffer_size的大小,allocated_buffers的key值为user_data,即内存的起始地址,同时由成员变量free表示当前binder_buffer管理的虚拟地址是否有在使用;在这里插入图片描述
    每个struct binder_buffer的不断分配与归还过程中存在着内存的合并等操作,binder_alloc_new_buf用来从当前的free_buffers中找到一个合适大小的binder_buffer分为两部分,一部分为分配的大小挂在allocated_buffers红黑树上,然后将剩下的binder_buffer重新挂载free_buffers红黑树中。
struct binder_lru_page

上面的struct binder_buffer用来管理虚拟地址中的每块内存,struct binder_lru_page用来管理分配的物理页;
初始时根据当前进程mmap的内存大小,可以计算如果是全部用完,占有的struct page数量;然后根据数量分配struct binder_lru_page的数组,每个struct binder_lru_page用来管理当前index对应的虚拟地址对应的struct page页;初始情况下实际物理页并不分配,对alloc成员变量和lru成员变量进行初始化;

alloc->pages = kcalloc(alloc->buffer_size / PAGE_SIZE,sizeof(alloc->pages[0]),GFP_KERNEL)

因为struc binder_alloc的成员pages指向的是一个binder_lru_page数组,并且每个struct binder_lru_page管理的成员变量是按照顺序排列的,当需要获得虚拟地址空间某个addr对应的page状态时,因此可以通过addr通过运算来获得,如下图代码;则当查看vm_start是否有page时,可以查看alloc->pages[0]处的binder_lru_page状态,需要注意内存对齐;

index = (page_addr - alloc->buffer) / PAGE_SIZE;
struct binder_lru_page *page = &alloc->pages[index]

在这里插入图片描述

binder_alloc_new_buf

在描述完毕上面几个结构体的变量之后,就可以对该函数进行解析;

在binder_transaction时,需要将客户端的数据复制的服务端,因此会调用如下函数从服务端的alloc管理的内存中获得一块内存,这块内存依旧由struct binder_buffer来管理;

t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,tr->offsets_size, extra_buffers_size,!reply && (t->flags & TF_ONE_WAY));

在这里插入图片描述
binder_alloc_new_buf_locked做以下操作:

  • 在alloc->free_list上找到一个最小大于分配size的struct binder_buffer A,然后将剩下的部分赋值给new_buffer,其起始位置在当前A->user_data + size处;然后将其挂在alloc->buffer链表上,这个new buffer管理从A->user_data+size开始的区域;

  • new_buffer管理的是没有使用的地址区域,因此insert_free_buffer链表上;

  • 此时struct binder_buffer A->user_data开始到 struct binder_buffer A->user_data + size的区域要被使用了,因此如果其在全局变量binder_freelist的话,需要将对应部分的page拆下来;

  • 将struct binder_buffer A从alloc->free_buffers上删除,然后挂载到allocated_buffer链表上;
    在这里插入图片描述
    在这里插入图片描述
    binder_install_buffer_pages做以下操作:

  • 通过struct binder_buffer的user_data和data_size获得虚拟地址的起始地址以及结束地址;

  • 根据地址获得对应的alloc->pages的索引index以及对应的struct binder_lru_page;

  • 调用binder_install_single_page进行映射物理页操作;

  • binder_install_single_page调用alloc_page分配一块物理页;

  • 调用vm_insert_page建立vma中addr地址与物理页page的关联;

  • 调用binder_set_installed_page使得lru_page->page_ptr指向page;
    在这里插入图片描述
    经过以上步骤,在binder中物理页与用户空间建立了联系;在这里插入图片描述

  • 14
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值