在调试完达芬奇的视屏输入后记录一下调试心得。重点还是再次放在系统调用mmap后的内存分配上。做了一个mmap系统调用的笔记以及记录以备后用。大致介绍了linux中对于mmap的应用以及后续产生缺页异常处理的一个流程。
由于之前的调试中一直对于vma的区域选取产生疑问,不知道vma到底是何许人也,这次有机会就直接mmap调试了。一下内容都通过log信息验证。
系统调用 mmap后
SYSCALL_DEFINE6(mmap_pgoff,unsigned long, addr, unsigned long, len,
unsignedlong, prot, unsigned long, flags,
unsignedlong, fd, unsigned long, pgoff)
| - >
unsigned long do_mmap_pgoff(structfile *file, unsigned long addr,
unsignedlong len, unsigned long prot,
unsignedlong flags, unsigned long pgoff)
| - >
unsigned long mmap_region(struct file*file, unsigned long addr,
unsignedlong len, unsigned long flags,
unsignedint vm_flags, unsigned long pgoff)
通过mmap_region最后调用驱动中填写的mmap函数,
static struct v4l2_file_operations omap34xxcam_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = video_ioctl2,
.poll =omap34xxcam_poll,
.mmap = omap34xxcam_mmap,
.open = omap34xxcam_open,
.release = omap34xxcam_release,
};
mmap函数最终是要解决什么?解决物理以及虚拟内存的分配,和对应关系。在函数mmap_region在vm_area_cachep 中分配一个vma。
vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
这个vma就是将来在驱动中使用的空间。既然是空间,那么有起点和终点。起点就是addr,终点就是addr+len。
vma->vm_start = addr;
vma->vm_end = addr + len;
关于这个addr的确定查看函数:get_unmapped_area(file, addr, len, pgoff,flags);
这个地址范围落在用户空间 0G-3G [ 0x 0000 0000 – 0xC000 0000]。
现在有了vma,有了地址以及空间那么怎么落实最后的内存上,这是在函数
static int __kprobes
do_page_fault(unsigned long addr, unsigned int fsr, structpt_regs *regs)中完成。
| ->
static int __kprobes
__do_page_fault(structmm_struct *mm, unsigned long addr, unsigned int fsr,
structtask_struct *tsk)
在函数__do_page_fault中直接使用要访问的虚拟地址作为参数在mm_struct中去寻找相应的vma。这个vma就是系统调用mmap中分配以及初始化的。现在真的要用到vma的起始地址了。但是mmap系统调用中没有把vma所描述的内存区域和物理内存联系起来。所谓联系就是建立相关的页表及页表项。当程序真正去访问一个已经存在但是没有建立物理内存的地址时,缺页异常发生。
__do_page_fault函数中vma = find_vma(mm,addr);找到包含有该地址的vma。然后调用handle_mm_fault以及handle_pte_fault分配建立页表。最终当缺页异常处理完成后,以上mmap中申请的内存区域就变得有效并且可以访问了。
在这两个建立页表的函数中遇到了一些page到物理地址以及虚拟地址的转换。在另外一片文章中介绍。
谢谢。