Linux Virtual Memory
This is the 3rd assignment of course CS353, in which we are required to write a module to explore Linux virtual memory.
Here is the content of ~/Documents/Makefile:
1 obj-m:=mtest.o 2 all: 3 make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules 4 clean: 5 make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
I wrote the source code in file ~/Documents/mtest.c:
1 #include <linux/kernel.h> 2 #include <linux/module.h> 3 #include <linux/init.h> 4 #include <linux/proc_fs.h> 5 #include <linux/seq_file.h> 6 #include <asm/uaccess.h> 7 #include <linux/sched.h> 8 #include <linux/mm.h> 9 #include <asm/page.h> 10 #include <asm/pgtable.h> 11 #include <linux/highmem.h> 12 #include <linux/mm_types.h> 13 14 #define SIZE 1024 15 #define VMACACHE_BITS 2 16 #define VMACACHE_SIZE (1U << VMACACHE_BITS) 17 18 char BUF[SIZE]; 19 20 /* Translate hex string into unsigned long */ 21 static unsigned long atol(char *str,unsigned long *pos) 22 { 23 unsigned long val = 0L, tmp; 24 for (;*(str+(*pos))==‘ ‘;(*pos)++); 25 for (;*(str+(*pos))!=‘ ‘;(*pos)++) { 26 char ch = *(str+(*pos)); 27 if (ch==‘x‘) { 28 continue; 29 } else if (ch>=‘0‘&&ch<=‘9‘) { 30 tmp = ch-‘0‘; 31 } else if (ch>=‘A‘&&ch<=‘F‘) { 32 tmp = 10+ch-‘A‘; 33 } else if (ch>=‘a‘&&ch<=‘f‘) { 34 tmp = 10+ch-‘a‘; 35 } else { 36 return 0; 37 } 38 val <<= 4; 39 val += tmp; 40 } 41 return val; 42 } 43 44 /* List the virtual memory areas of *current */ 45 static void listvma(void) 46 { 47 struct vm_area_struct *vma; 48 unsigned long start, end; 49 vm_flags_t flags; 50 /* traverse the virtual memory areas 51 in the form of LINKED-LIST (mm->mmap) 52 rather than RB-TREE (mm->mm_rb) */ 53 for (vma=current->mm->mmap;vma;vma=vma->vm_next) { 54 start = vma->vm_start; 55 if (stack_guard_page_start(vma, start)) 56 start += PAGE_SIZE; 57 end = vma->vm_end; 58 if (stack_guard_page_end(vma, end)) 59 end -= PAGE_SIZE; 60 flags = vma->vm_flags; 61 printk(KERN_INFO "%lx-%lx %c%c%c%c\n", 62 start, 63 end, 64 (flags & VM_READ) ? ‘r‘ : ‘-‘, 65 (flags & VM_WRITE) ? ‘w‘ : ‘-‘, 66 (flags & VM_EXEC) ? ‘x‘ : ‘-‘, 67 (flags & VM_MAYSHARE) ? ‘s‘ : ‘p‘); 68 } 69 70 } 71 72 /* Given a virtual address, return the page */ 73 static struct page *findpage(unsigned long va,unsigned char wrt) 74 { 75 struct page *pg = NULL ; 76 struct vm_area_struct *vma = NULL; 77 pgd_t *pgd = NULL; 78 pud_t *pud = NULL; 79 pmd_t *pmd = NULL; 80 pte_t *ptep = NULL; 81 spinlock_t *ptlp = NULL; 82 printk(KERN_INFO "va = %lx\n",va); 83 vma = find_vma(current->mm,va); 84 if (!vma || va < vma->vm_start) { 85 // the virtual address is not available 86 goto OUT; 87 } else if (wrt && !(vma->vm_flags&VM_WRITE)) { 88 // the virtual memory area is not writable 89 goto OUT; 90 } 91 printk(KERN_INFO "vma -> %p\n",vma); 92 /* Page Global Directory */ 93 pgd = pgd_offset(current->mm,va); 94 if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) { 95 goto OUT; 96 } 97 printk(KERN_INFO "pgd = %lx\n",(long)pgd_val(*pgd)); 98 /* Page Upper Directory */ 99 pud = pud_offset(pgd,va); 100 if (pud_none(*pud) || unlikely(pud_bad(*pud))) { 101 goto OUT; 102 } 103 printk(KERN_INFO "pud = %lx\n",(long)pud_val(*pud)); 104 /* Page Middle Directory */ 105 pmd = pmd_offset(pud,va); 106 if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) { 107 goto OUT; 108 } 109 printk(KERN_INFO "pmd = %lx\n",(long)pmd_val(*pmd)); 110 /* Page Table Entry */ 111 ptep = pte_offset_map_lock(current->mm,pmd,va,&ptlp); 112 printk(KERN_INFO "ptep -> %p\n",ptep); 113 if (!ptep) { 114 goto OUT; 115 } 116 printk(KERN_INFO "pte = %lx\n",(long)pte_val(*ptep)); 117 if (!pte_present(*ptep)) { 118 goto UNLOCK; 119 } 120 //pg = vm_normal_page(vma,va,*ptep); 121 pg = pfn_to_page(pte_pfn(*ptep)); 122 if (pg) { 123 get_page(pg); 124 } 125 UNLOCK: 126 pte_unmap_unlock(ptep,ptlp); 127 OUT: 128 return pg; 129 } 130 131 132 static void mtest_operate(void) 133 { 134 BUF[strlen(BUF)-1] = ‘ ‘; 135 down_read(¤t->mm->mmap_sem); 136 if (!strncmp(BUF,"listvma ",8)) { 137 listvma(); 138 } else if (!strncmp(BUF,"findpage ",9)) { 139 unsigned long pos = 9, va; 140 struct page *pg = NULL; 141 va = atol(BUF,&pos); 142 if ((pg = findpage(va,0))) { 143 unsigned long pa; 144 pa = ((long)page_address(pg))|(va&~PAGE_MASK); 145 printk(KERN_INFO "%lx\n",pa); 146 } else { 147 printk(KERN_INFO "translation not found\n"); 148 } 149 } else if (!strncmp(BUF,"writeval ",9)) { 150 unsigned long pos = 9, va; 151 struct page *pg = NULL; 152 va = atol(BUF,&pos); 153 if ((pg = findpage(va,1))) { 154 *(unsigned long *)va = atol(BUF,&pos); 155 put_page(pg); 156 printk(KERN_INFO "successful\n"); 157 } else { 158 printk(KERN_INFO "the page is not writable\n"); 159 } 160 } 161 up_read(¤t->mm->mmap_sem); 162 } 163 164 165 /* 166 * Basic functions of the proc fs entry /proc/mtest 167 * mtest_open and mtest_show supports command "cat" 168 * mtest_write supports command "echo" 169 */ 170 171 static int mtest_show(struct seq_file *m,void *v) 172 { 173 seq_printf(m,"%s\n",BUF); 174 return 0; 175 } 176 177 static int mtest_open(struct inode *inode,struct file *file) 178 { 179 return single_open(file,mtest_show,NULL); 180 } 181 182 static ssize_t mtest_write(struct file *filp,const char __user *buf,size_t len,loff_t *pos) 183 { 184 memset(BUF,0,SIZE); 185 if (copy_from_user(BUF,buf,len)){ 186 return -EFAULT; 187 } 188 mtest_operate(); 189 return len; 190 } 191 192 static const struct file_operations mtest_fops = { 193 .owner = THIS_MODULE, 194 .read = seq_read, 195 .write = mtest_write, 196 .llseek = seq_lseek, 197 .open = mtest_open, 198 .release = single_release, 199 }; 200 201 static int __init mtest_init(void) 202 { 203 struct proc_dir_entry *entry = NULL; 204 entry = proc_create("mtest",0666,NULL,&mtest_fops); 205 if (!entry) { 206 printk(KERN_INFO "Error: create_proc_entry failed\n"); 207 return -1; 208 } else { 209 printk(KERN_INFO "Kernel: mtest init\n"); 210 return 0; 211 } 212 } 213 214 static void __exit mtest_exit(void) 215 { 216 remove_proc_entry("mtest",NULL); 217 printk(KERN_INFO "Kernel: mtest exit\n"); 218 } 219 220 module_init(mtest_init); 221 module_exit(mtest_exit); 222 223 MODULE_LICENSE("GPL"); 224 MODULE_AUTHOR("ZUO NAN");
We are also required to design a user program to write data to a virtual address (./test/c):
1 #include <stdio.h> 2 3 #define SIZE 128 4 5 unsigned char BUF[SIZE]; 6 7 int main() 8 { 9 unsigned long val = 0; 10 11 while (1) { 12 printf("Command: "); 13 memset(BUF,0,SIZE); 14 scanf("%s",&BUF); 15 if (!strcmp(BUF,"write")) { 16 memset(BUF,0,SIZE); 17 scanf("%s",&BUF); 18 printf("writeval %p %s\n",&val,BUF); 19 if (!freopen("/proc/mtest","w",stdout)){ 20 printf("proc entry not found\n"); 21 exit(1); 22 } 23 printf("writeval %p %s\n",&val,BUF); 24 if (!freopen("/dev/tty","w",stdout)){ 25 printf("stdout not recovered\n"); 26 exit(2); 27 } 28 } else if (!strcmp(BUF,"print")) { 29 printf("%lx\n",val); 30 } else if (!strcmp(BUF,"exit")) { 31 break; 32 } 33 } 34 return 0; 35 }
Here are some testing results of my programs.
(1) To test the compilation and insertion of the module:
(2) To test LISTVMA instruction:
(3) To test FINDPAGE and WRITEVAL:
(4) To test the user program:
(5) To test the removal of the module:
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。