这一周主要看了陈老师的内存管理实例,感觉理解的很有限,实例代码的网址如下:http://home.lupaworld.com/home-space-uid-26540-do-blog-id-230202.html
师姐把这个实例拆分成了四个部分, 第一部分比较简单,根据 vm_area_struct 结构体中的 vma_next 指针来完成当前进程虚存区的遍历,打印当前进程的 vma ,并且打印 vma 的标志,标志对应可读、可写、可执行,我们还可以通过当前进程的 task_struct 结构中的 comm 和 pid 成员来打印出它的名称和 pid 号。第二部分的主要功能是通过调用 find_vma() 函数来查找随意一个虚地址对应的 vma ,这个函数主要是将虚地址与 vma 的起始与结束地址相比较,如果正好在起止地址之间的话就正好找到了对应的 vma ,函数执行过程中用到了 vm_area_struct 结构体中的 rb_node 结点,这是采用了红黑树算法,这是一种自平衡二叉查找树,看了一些介绍但不太懂,如果查找到了对应的 vma 则打印它的起止地址和标志。第三部分的第一个函数主要是根据 vma 和其中的虚地址来查找它对应的物理页,代码采用了四级目录的表示方法,分别对应 pgd_t 页全局目录项、 pud_t 页上级目录项、 pmd_t 页中间目录项、 Pte_t 页表项,采用四级目录主要是为了在 64 位系统中使用, 64 位系统中只用了 48 位 (9 + 9 + 9 + 9 + 12) 来表示,函数中顺序的调用了相应的 offset 宏来获得各级目录的偏移地址,例如: #define pgd_offset(mm, address) ((mm)->pgd + pgd_index((address))) ,其中 (mm)->pgd 是获得页目录的基地址, pgd_index(address) 是从虚地址中获得页全局目录项的偏移量,其定义如下: #define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)) , PGDIR_SHIFT 为 39 ,就是将 address 左移 39 位获得高 9 位。又如: #define pud_offset(pgd, start) (pgd) ,这个产生页上级目录对应的线性地址, #define pmd_offset(dir,addr) ((pmd_t *) pud_page_vaddr(*(dir)) + (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))) ,这个就看不懂了,到页表时调用了 pfn_to_page 宏,这个宏也看不懂。下面一个函数是根据页表,求出某个虚地址所在的页,并可以转换成对应的物理地址,其中主要的两句是 kernel_addr = page_address(page) , kernel_addr += addr & ~PAGE_MASK; 第一句先求得虚地址对应物理页面的起始地址,第二句将起始地址加上页内偏移量,其中 PAGE_MASK 为 4K ,这样就将虚地址转换成物理地址了。第四部分的功能主要是向 vma 中写入数据,先找到虚地址对应的 vma ,并判断它的标志是否可写,然后便可写入相应的数据了。
这四个部分如果要整合成一个完整的模块代码还要加上一些其他的部分,并且对 vma 的操作要加上互斥信号量,这个信号量在 mm_struct 中定义为 mmap_sem ,再加上实现各种功能的操作函数后这个实例的架构基本上就定下来了,主要使用了 memcmp(buf, "", n), 函数来控制对于于哪种功能,用于列出 vma 只要使用 memcmp(buf, "listvma", 7) ,查找 vma 除了使用 memcmp(buf, "findvma", 7) 外还要用使用 sscanf() 函数来输入虚地址,后面的几个功能也要进行相应的处理,再加上模块的注册和许可证后这个模块便基本上完成了。