虚拟内存管理实例

关于虚拟内存管理的基本内容参看相关内容,在此不再赘述,在理论的基础上,设计出合理的实验可以强化对理论的理解和应用能力

————————————————————————————————–

实验内容:在proc 文件系统下,建立一个文件,每次向这个文件写人字符时,调用相应的虚拟内存处理函数

  1. /*
  2. mtest_dump_vma_list():打印出当前进程的各个VMA,这个功能我们简称”listvma”
  3. mtest_find_vma(): 找出某个虚地址所在的VMA,这个功能我们简称“findvma”
  4. my_follow_page( ):根据页表,求出某个虚地址所在的物理页面,这个功能我们简称”findpage”
  5. mtest_write_val(), 在某个地址写上具体数据,这个功能我们简称“writeval”.
  6. */
  7. #include <linux/module.h>
  8. #include <linux/kernel.h>
  9. #include <linux/proc_fs.h>
  10. #include <linux/string.h>
  11. #include <linux/vmalloc.h>
  12. #include <asm/uaccess.h>
  13. #include <linux/init.h>
  14. #include <linux/slab.h>
  15. #include <linux/mm.h>
  16. #include <linux/vmalloc.h>
  17. MODULE_LICENSE(“GPL”);
  18. /*
  19. @如何编写代码查看自己的进程到底有哪些虚拟区?
  20. */
  21. static void mtest_dump_vma_list(void)
  22. {
  23. struct mm_struct *mm = current->mm;
  24. struct vm_area_struct *vma;
  25. printk(“The current process is %s\n”,current->comm);
  26. printk(“mtest_dump_vma_list\n”);
  27. down_read(&mm->mmap_sem);
  28. for (vma = mm->mmap;vma; vma = vma->vm_next) {
  29. printk(“VMA 0x%lx-0x%lx “,
  30. vma->vm_start, vma->vm_end);
  31. if (vma->vm_flags & VM_WRITE)
  32. printk(“WRITE “);
  33. if (vma->vm_flags & VM_READ)
  34. printk(“READ “);
  35. if (vma->vm_flags & VM_EXEC)
  36. printk(“EXEC “);
  37. printk(“\n”);
  38. }
  39. up_read(&mm->mmap_sem);
  40. }
  41. /*
  42. @如果知道某个虚地址,比如,0x8049000,
  43. 又如何找到这个地址所在VMA是哪个?
  44. */
  45. static void  mtest_find_vma(unsigned long addr)
  46. {
  47. struct vm_area_struct *vma;
  48. struct mm_struct *mm = current->mm;
  49. printk(“mtest_find_vma\n”);
  50. down_read(&mm->mmap_sem);
  51. vma = find_vma(mm, addr);
  52. if (vma && addr >= vma->vm_start) {
  53. printk(“found vma 0x%lx-0x%lx flag %lx for addr 0x%lx\n”,
  54. vma->vm_start, vma->vm_end, vma->vm_flags, addr);
  55. } else {
  56. printk(“no vma found for %lx\n”, addr);
  57. }
  58. up_read(&mm->mmap_sem);
  59. }
  60. /*
  61. @一个物理页在内核中用struct page来描述。
  62. 给定一个虚存区VMA和一个虚地址addr,
  63. 找出这个地址所在的物理页面page.
  64. */
  65. static struct page *
  66. my_follow_page(struct vm_area_struct *vma, unsigned long addr)
  67. {
  68. pud_t *pud;
  69. pmd_t *pmd;
  70. pgd_t *pgd;
  71. pte_t *pte;
  72. spinlock_t *ptl;
  73. struct page *page = NULL;
  74. struct mm_struct *mm = vma->vm_mm;
  75. pgd = pgd_offset(mm, addr);
  76. if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) {
  77. goto out;
  78. }
  79. pud = pud_offset(pgd, addr);
  80. if (pud_none(*pud) || unlikely(pud_bad(*pud)))
  81. goto out;
  82. pmd = pmd_offset(pud, addr);
  83. if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) {
  84. goto out;
  85. }
  86. pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
  87. if (!pte)
  88. goto out;
  89. if (!pte_present(*pte))
  90. goto unlock;
  91. page = pfn_to_page(pte_pfn(*pte));
  92. if (!page)
  93. goto unlock;
  94. get_page(page);
  95. unlock:
  96. pte_unmap_unlock(pte, ptl);
  97. out:
  98. return page;
  99. }
  100. /*
  101. @ 根据页表,求出某个虚地址所在的物理页面,
  102. 这个功能我们简称”findpage”
  103. */
  104. static void   mtest_find_page(unsigned long addr)
  105. {
  106. struct vm_area_struct *vma;
  107. struct mm_struct *mm = current->mm;
  108. unsigned long kernel_addr;
  109. struct page *page;
  110. printk(“mtest_write_val\n”);
  111. down_read(&mm->mmap_sem);
  112. vma = find_vma(mm, addr);
  113. page = my_follow_page(vma, addr);
  114. if (!page)
  115. {
  116. printk(“page not found  for 0x%lx\n”, addr);
  117. goto out;
  118. }
  119. printk(“page  found  for 0x%lx\n”, addr);
  120. kernel_addr = (unsigned long)page_address(page);
  121. kernel_addr += (addr&~PAGE_MASK);
  122. printk(“find  0x%lx to kernel address 0x%lx\n”, addr, kernel_addr);
  123. out:
  124. up_read(&mm->mmap_sem);
  125. }
  126. /*
  127. @你是否有这样的想法,
  128. 给某个地址写入自己所想写的数据?
  129. */
  130. static void
  131. mtest_write_val(unsigned long addr, unsigned long val)
  132. {
  133. struct vm_area_struct *vma;
  134. struct mm_struct *mm = current->mm;
  135. struct page *page;
  136. unsigned long kernel_addr;
  137. printk(“mtest_write_val\n”);
  138. down_read(&mm->mmap_sem);
  139. vma = find_vma(mm, addr);
  140. if (vma && addr >= vma->vm_start && (addr + sizeof(val)) < vma->vm_end) {
  141. if (!(vma->vm_flags & VM_WRITE)) {
  142. printk(“vma is not writable for 0x%lx\n”, addr);
  143. goto out;
  144. }
  145. page = my_follow_page(vma, addr);
  146. if (!page) {
  147. printk(“page not found  for 0x%lx\n”, addr);
  148. goto out;
  149. }
  150. kernel_addr = (unsigned long)page_address(page);
  151. kernel_addr += (addr&~PAGE_MASK);
  152. printk(“write 0x%lx to address 0x%lx\n”, val, kernel_addr);
  153. *(unsigned long *)kernel_addr = val;
  154. put_page(page);
  155. } else {
  156. printk(“no vma found for %lx\n”, addr);
  157. }
  158. out:
  159. up_read(&mm->mmap_sem);
  160. }
  161. static ssize_t
  162. mtest_write(struct file *file, const char __user * buffer,
  163. size_t count, loff_t * data)
  164. {
  165. printk(“mtest_write  ………..  \n”);
  166. char buf[128];
  167. unsigned long val, val2;
  168. if (count > sizeof(buf))
  169. return -EINVAL;
  170. if (copy_from_user(buf, buffer, count))
  171. return -EINVAL;
  172. if (memcmp(buf, “listvma”, 7) == 0)
  173. mtest_dump_vma_list();
  174. else if (memcmp(buf, “findvma”, 7) == 0) {
  175. if (sscanf(buf + 7, “%lx”, &val) == 1) {
  176. mtest_find_vma(val);
  177. }
  178. }
  179. else if (memcmp(buf, “findpage”, 8) == 0) {
  180. if (sscanf(buf + 8, “%lx”, &val) == 1) {
  181. mtest_find_page(val);
  182. //my_follow_page(vma, addr);
  183. }
  184. }
  185. else  if (memcmp(buf, “writeval”, 8) == 0) {
  186. if (sscanf(buf + 8, “%lx %lx”, &val, &val2) == 2) {
  187. mtest_write_val(val, val2);
  188. }
  189. }
  190. return count;
  191. }
  192. static struct
  193. file_operations proc_mtest_operations = {
  194. .write        = mtest_write
  195. };
  196. static struct proc_dir_entry *mtest_proc_entry;
  197. //整个操作我们以模块的形式实现,因此,模块的初始化和退出函数如下:
  198. static int __init
  199. mtest_init(void)
  200. {
  201. mtest_proc_entry = create_proc_entry(“mtest”, 0777, NULL);
  202. if (mtest_proc_entry == NULL) {
  203. printk(“Error creating proc entry\n”);
  204. return -1;
  205. }
  206. printk(“create the filename mtest mtest_init sucess  \n”);
  207. mtest_proc_entry->proc_fops = &proc_mtest_operations;
  208. return 0;
  209. }
  210. static void
  211. __exit mtest_exit(void)
  212. {
  213. printk(“exit the module……mtest_exit \n”);
  214. remove_proc_entry(“mtest”, NULL);
  215. }
  216. MODULE_LICENSE(“GPL”);
  217. MODULE_DESCRIPTION(“mtest”);
  218. MODULE_AUTHOR(“Zou Nan hai”);
  219. module_init(mtest_init);
  220. module_exit(mtest_exit);

下面为Makefile

  1. obj-m := mm.o
  2. # KDIR is the location of the kernel source.  The current standard is
  3. # to link to the associated source tree from the directory containing
  4. # the compiled modules.
  5. KDIR  := /lib/modules/$(shell uname -r)/build
  6. # PWD is the current working directory and the location of our module
  7. # source files.
  8. PWD   := $(shell pwd)
  9. # default is the default make target.  The rule here says to run make
  10. # with a working directory of the directory containing the kernel
  11. # source and compile only the modules in the PWD (local) directory.
  12. default:
  13. $(MAKE) -C $(KDIR) M=$(PWD) modules
  14. clean:
  15. rm -rf *.o *.ko *.mod.c

下面为测试用例

[root@HBIDS proc]# echo “listvma” > mtest
[root@HBIDS proc]# echo “listvma” > mtest
[root@HBIDS proc]# echo “findvma0xb7f2b001” > mtest
[root@HBIDS proc]# echo “findpage0xb7f2b001” > mtest
[root@HBIDS proc]# echo “writeval0xb7f2b001 123456” > mtest

打印结果为

The current process is bash
mtest_dump_vma_list
VMA 0x8048000-0x80dc000 READ EXEC
VMA 0x80dc000-0x80e2000 WRITE READ EXEC
VMA 0x80e2000-0x811e000 WRITE READ EXEC
VMA 0x42000000-0x4212e000 READ EXEC
VMA 0x4212e000-0x42131000 WRITE READ EXEC
VMA 0x42131000-0x42133000 WRITE READ EXEC
VMA 0xb7d00000-0xb7f00000 READ EXEC
VMA 0xb7f00000-0xb7f0b000 READ EXEC
VMA 0xb7f0b000-0xb7f0c000 WRITE READ EXEC
VMA 0xb7f0c000-0xb7f0d000 WRITE READ EXEC
VMA 0xb7f0d000-0xb7f0f000 READ EXEC
VMA 0xb7f0f000-0xb7f10000 WRITE READ EXEC
VMA 0xb7f10000-0xb7f13000 READ EXEC
VMA 0xb7f13000-0xb7f14000 WRITE READ EXEC
VMA 0xb7f2b000-0xb7f31000 READ EXEC
VMA 0xb7f31000-0xb7f32000 WRITE READ EXEC
VMA 0xb7f32000-0xb7f47000 READ EXEC
VMA 0xb7f47000-0xb7f48000 WRITE READ EXEC
VMA 0xbfd31000-0xbfd47000 WRITE READ EXEC
mtest_write  ………..
mtest_find_vma
found vma 0xb7f47000-0xb7f48000 flag 100877 for addr 0xb7f47001
mtest_write  ………..
mtest_write_val
page  found  for 0xb7f47001
find  0xb7f47001 to kernel address 0xc8c4e001
mtest_write  ………..
mtest_write_val
write 0x1234 to address 0xc8c4e001

后记:这个程序是去年八月份发表在我的博客上的,当时分为三篇发表。也许因为新博客搬家的缘故找不到了。最近的教学中需要这个例子,于是在网上去搜,幸好有网友收藏并进行了整理,在此贴出,以让大家对抽象的虚存管理有代码级的理解。

.