Binder通过open调用打开后,需要用户态进程需调用mmap进行内存映射。mmap系统调用,经过VFS最终会调用到binder驱动注册的binder_mmap函数。这里我们将揭开Binder通信高效的本质原因,:)
一 内存映射函数的实现 binder_mmap(kernel/drivers/android/binder.c)
static int binder_mmap(struct file *filp, struct vm_area_struct *vma/*用户态虚拟地址空间描述,地址空间在0~3G*/)
{
int ret;
/* 一块连续的由vmalloc分配内核虚拟地址空间描述,从 VMALLOC_START到VMALLOC_END*/
struct vm_struct *area;
struct binder_proc *proc = filp->private_data;
const char *failure_string;
struct binder_buffer *buffer;
if (proc->tsk != current)
return -EINVAL;
//申请空间不能大于4M,如果大于4M就改为4M大小。app默认是1M左右
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;
//检查vma是否被forbidden,vma是一块连续的用户态虚拟内存地址空间的描述
if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
ret = -EPERM;
failure_string = "bad vm_flags";
goto err_bad_arg;
}
//打开VM_DONTCOPY,关闭VM_MAYWRITE
vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
//加上binder_mmap_lock互斥锁,因为接下来要操作proc结构体,可能发生多线程竞争
mutex_lock(&binder_mmap_lock);
//一个进程已经有一次mmap,如要执行新的map,需先将之前的unmap。
if (proc->buffer) {
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
/* 获取一块与用户态空间大小一致的内核的连续虚拟地址空间,
* 注意虚拟地址空间是在此一次性分配的,物理页面却是需要时才去申请和映射
*/
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
//将内核虚拟地址记录在proc的buffer中
proc->buffer = area->addr;
/* 记录用户态虚拟地址空间与内核态虚拟地址空间的偏移量,
* 这样通过buffer和user_buffer_offset就可以计算出用户态的虚拟地址。
*/
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
/*释放互斥锁*/
mutex_unlock(&binder_mmap_lock);
#ifdef CONFIG_CPU_CACHE_VIPT
/* CPU的缓存方式是否为: VIPT(Virtual Index Physical Tag):使用虚拟地址的索引域和物理地址的标记域。
* 这里先不管,有兴趣的可参考:https://blog.csdn.net/Q_AN1314/article/details/78980191
*/
if (c

本文详细探讨了Android Binder驱动中的内存映射过程,包括如何通过系统调用进行内存映射、物理内存的分配与释放。重点介绍了Binder如何在内核态和用户态之间建立地址映射,使得数据只需拷贝一次,从而提高通信效率。同时总结了Binder通信内存映射的限制和管理策略。
最低0.47元/天 解锁文章
2733

被折叠的 条评论
为什么被折叠?



