• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Binder - 6、Binder的一次拷贝

武飞扬头像
welsonx
帮助1

一、前言

众所周知,Binder之所以高效,是因为它只发生了一次内存拷贝,那么它的“一次拷贝”到底是怎么实现的呢?

我们在之前在分析binder_transaction的时候,提到了一个方法,这个方法是一次拷贝的核心,我们在这里来仔细分析一下

二、源码分析

2.1 入口-binder_transaction

Kernel\drivers\android\binder.c


static void binder_transaction(struct binder_proc *proc,

                   struct binder_thread *thread,

                   struct binder_transaction_data *tr, int reply,

                   binder_size_t extra_buffers_size)

{

    //申请内存

    t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,

        tr->offsets_size, extra_buffers_size,

        !reply && (t->flags & TF_ONE_WAY), current->tgid);

    //拷贝数据

    if (binder_alloc_copy_user_to_buffer(

                &target_proc->alloc,

                t->buffer, 0,

                (const void __user *)

                    (uintptr_t)tr->data.ptr.buffer,

                tr->data_size)) {

        //error

    }

}

在这个方法中,有两个函数对拷贝数据非常重要,一个是binder_alloc_new_buf,另一个是binder_alloc_copy_user_to_bufferbinder_alloc_new_buf是用来申请内存用的,而binder_alloc_copy_user_to_buffer是执行具体的数据拷贝。

可以看到这里两个方法传递的参数都是target_proc的,代表我们此时在操纵接收端的内存。

我们分开来看一下,先看下binder_alloc_new_buf

2.2 buffer申请-binder_alloc_new_buf


struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,

                       size_t data_size,

                       size_t offsets_size,

                       size_t extra_buffers_size,

                       int is_async,

                       int pid)

{

    struct binder_buffer *buffer;

  


    mutex_lock(&alloc->mutex);

    buffer = binder_alloc_new_buf_locked(alloc, data_size, offsets_size,

                         extra_buffers_size, is_async, pid);

    mutex_unlock(&alloc->mutex);

    return buffer;

}

直接调用了binder_alloc_new_buf_locked方法:


static struct binder_buffer *binder_alloc_new_buf_locked(

                struct binder_alloc *alloc,

                size_t data_size,

                size_t offsets_size,

                size_t extra_buffers_size,

                int is_async,

                int pid)

{

    struct rb_node *n = alloc->free_buffers.rb_node;

    struct binder_buffer *buffer;

    size_t buffer_size;

    struct rb_node *best_fit = NULL;

    void __user *has_page_addr;

    void __user *end_page_addr;

    size_t size, data_offsets_size;

    int ret;

  


    //数据对齐

    data_offsets_size = ALIGN(data_size, sizeof(void *))  

        ALIGN(offsets_size, sizeof(void *));

    size = data_offsets_size   ALIGN(extra_buffers_size, sizeof(void *));

    /* Pad 0-size buffers so they get assigned unique addresses */

    size = max(size, sizeof(void *));

    //寻找有没有空闲的buffer

    while (n) {

        buffer = rb_entry(n, struct binder_buffer, rb_node);

        buffer_size = binder_alloc_buffer_size(alloc, buffer);

  


        if (size < buffer_size) {

            best_fit = n;

            n = n->rb_left;

        } else if (size > buffer_size)

            n = n->rb_right;

        else {

            best_fit = n;

            break;

        }

    }

    //包含该数据的页面

    has_page_addr = (void __user *)

        (((uintptr_t)buffer->user_data   buffer_size) & PAGE_MASK);

    //user_data结尾对应的页面

    end_page_addr =

        (void __user *)PAGE_ALIGN((uintptr_t)buffer->user_data   size);

    if (end_page_addr > has_page_addr)

        end_page_addr = has_page_addr;

    ret = binder_update_page_range(alloc, 1, (void __user *)

        PAGE_ALIGN((uintptr_t)buffer->user_data), end_page_addr);

    buffer->free = 0;

    buffer->allow_user_free = 0;

    //插入allocated_buffers红黑树中

    binder_insert_allocated_buffer_locked(alloc, buffer);

    buffer->data_size = data_size;

    buffer->offsets_size = offsets_size;

    return buffer;

}

这个方法还是很长的,总体来说,干了这么几件事:

  • 1、数据对齐

  • 2、寻找binder_alloc中有没有空闲的节点,如果有并且足够大,那么就可以用这个

  • 3、计算我们内存对应的页面

  • 4、执行binder_update_page_range方法

  • 5、将使用的Buffer插入到allocated_buffers

中间的这个binder_update_page_range方法,看起来比较可疑,我们跳进去看下:

2.2.1 binder_update_page_range


static int binder_update_page_range(struct binder_alloc *alloc, int allocate,

                    void __user *start, void __user *end)

{

    void __user *page_addr;

    unsigned long user_page_addr;

    struct binder_lru_page *page;

    struct vm_area_struct *vma = NULL;

    struct mm_struct *mm = NULL;

    bool need_mm = false;

  


    //是否需要申请页面,我们这里肯定是需要的,所以need_mm为true

    for (page_addr = start; page_addr < end; page_addr  = PAGE_SIZE) {

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

        if (!page->page_ptr) {

            need_mm = true;

            break;

        }

    }

  


    if (mm) {

        mmap_read_lock(mm);

        vma = alloc->vma;

    }

  


    //判断物理Page是否存在

    for (page_addr = start; page_addr < end; page_addr  = PAGE_SIZE) {

        int ret;

        bool on_lru;

        size_t index;

  


        index = (page_addr - alloc->buffer) / PAGE_SIZE;

        page = &alloc->pages[index];

  


        if (page->page_ptr) {

            //页面存在

            continue;

        }

        //申请物理Page

        page->page_ptr = alloc_page(GFP_KERNEL |

                        __GFP_HIGHMEM |

                        __GFP_ZERO);

        page->alloc = alloc;

        INIT_LIST_HEAD(&page->lru);

  


        user_page_addr = (uintptr_t)page_addr;

        //将page插入到vma中

        ret = vm_insert_page(vma, user_page_addr, page[0].page_ptr);

    }

    if (mm) {

        mmap_read_unlock(mm);

        mmput(mm);

    }

    return 0;

}

哦,原来binder_update_page_range是用来申请物理页面的,按需申请Page并将其插入vma中,且被binder_alloc所持有,那么binder_alloc是何许人也?

2.2.2 binder_alloc是什么?

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgecgfj
系列文章
更多 icon
同类精品
更多 icon
继续加载