Binder - 6、Binder的一次拷贝
一、前言
众所周知,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_buffer
,binder_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
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01