linux 内核态 mmap,Linux Kernel 及 binder mmap实现

2)保存kernel态虚拟地址空间的起始地址,以便后面使用:

proc->buffer = area->addr;

3)  计算并保存进程用户态虚拟地址空间起始地址与kernel态虚拟地址空间的起始地址的差值, 以便后面使用。

proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;

4)分配物理页表项(struct page)

proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);

5)binder_update_page_range

它的工作为:

a)分配物理页

b)分别对vma用户空间建立页表、对vmalloc区域建立页表映射关系。

前面有了用户态和Kernel态的虚拟地址空间,但是还不能访问,因为还没有对应的物理内存。

补充知识:

a)struct page用于跟踪描述一个物理页面是否正在被使用。所有的page结构将都被存入一个叫做mem_map的全局数组中.

b)在每个进程的task_struct中包含一个指向mm_struct结构的指针.进程的mm_struct中则包含了进程可执行影像的页目录指针pgd.还包含了指向vm_area_struct的几个指针,每个vm_area_struct包含一个进程的虚拟地址区域.

binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)

proc->buffer指向内核的vmalloc 区域的起始地址,前面已经有了vma(vm_area_struct)和 area(vm_struct)。binder_update_page_range实现代码如下:

staticintbinder_update_page_range(structbinder_proc *proc,intallocate,

void*start,void*end,

structvm_area_struct *vma)

{

void*page_addr;

unsignedlonguser_page_addr;

structvm_struct tmp_area;

structpage **page;

structmm_struct *mm;

binder_debug(BINDER_DEBUG_BUFFER_ALLOC,

"binder: %d: %s pages %p-%p\n", proc->pid,

allocate ?"allocate":"free", start, end);

if(end <= start)

return0;

if(vma)

mm = NULL;

else

mm = get_task_mm(proc->tsk);

if(mm) {

down_write(&mm->mmap_sem);

vma = proc->vma;

}

if(allocate == 0)

gotofree_range;

if(vma == NULL) {

printk(KERN_ERR"binder: %d: binder_alloc_buf failed to "

"map pages in userspace, no vma\n", proc->pid);

gotoerr_no_vma;

}

for(page_addr = start; page_addr 

intret;

structpage **page_array_ptr;

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

BUG_ON(*page);

//分配一个物理页

*page = alloc_page(GFP_KERNEL | __GFP_ZERO);

if(*page == NULL) {

printk(KERN_ERR"binder: %d: binder_alloc_buf failed "

"for page at %p\n", proc->pid, page_addr);

gotoerr_alloc_page_failed;

}

tmp_area.addr = page_addr;

tmp_area.size = PAGE_SIZE + PAGE_SIZE/* guard page? */;

page_array_ptr = page;

//根据kernel态的虚拟地址,分配对应的pud, pmd和pte并填充对应的值

//以使根据虚拟地址,可以通过pgd, pud, pmd和pte寻址到对应的物理存储单元

ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);

if(ret) {

printk(KERN_ERR"binder: %d: binder_alloc_buf failed "

"to map page at %p in kernel\n",

proc->pid, page_addr);

gotoerr_map_kernel_failed;

}

user_page_addr =

(uintptr_t)page_addr + proc->user_buffer_offset;

//根据用户态的虚拟地址,插入一页到用户空间的vma,

//从而用户空间访问从user_page_addr开始的一页内存时,

//从而可以访问到与page对应的物理页中对应的存储单元

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

if(ret) {

printk(KERN_ERR"binder: %d: binder_alloc_buf failed "

"to map page at %lx in userspace\n",

proc->pid, user_page_addr);

gotoerr_vm_insert_page_failed;

}

/* vm_insert_page does not seem to increment the refcount */

}

if(mm) {

up_write(&mm->mmap_sem);

mmput(mm);

}

return0;

free_range:

for(page_addr = end - PAGE_SIZE; page_addr >= start;

page_addr -= PAGE_SIZE) {

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

if(vma)

zap_page_range(vma, (uintptr_t)page_addr +

proc->user_buffer_offset, PAGE_SIZE, NULL);

err_vm_insert_page_failed:

unmap_kernel_range((unsignedlong)page_addr, PAGE_SIZE);

err_map_kernel_failed:

__free_page(*page);

*page = NULL;

err_alloc_page_failed:

;

}

err_no_vma:

if(mm) {

up_write(&mm->mmap_sem);

mmput(mm);

}

return-ENOMEM;

}

a)map_vm_area: 映射Kernel虚拟地址到物理内存,为vmalloc 区域的连续地址空间进行页表映射,当然需要vm_struct (提供虚拟地址)参数和 page参数(用来make pte的),这就完成了内核区的映射

b) vm_insert_page: 更新vma对应的页表,这样就是实现了mmap功能

c)binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)调用的时候只分配了1页,这个是为了节约空间,按需分配。而进程虚拟空间和vmalloc内核空间按需要分配,反正它不占用实际物理内存,所以开始就占用了所需的全部空间,而实际的物理页按需获取;

proc->vma为调用进程的一段用户空间;

proc->files为调用进程的files_struct结构;

proc->buffer_size为需要映射的长度(小于4m)-sizeof(structbinder_buffer);

proc->pages为分配的物理页page的指针数组,开始只有一项,即1页,但是长度还是预留好了;

proc->buffer为内核连续映射区首地址;

proc->user_buffer_offset 为用户空间映射区首地址-内核空间连续映射的首地址。0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值