📄 memory.c.bak
字号:
/* * linux/mm/memory.c * * (C) 1991 Linus Torvalds *//* * demand-loading started 01.12.91 - seems it is high on the list of * things wanted, and it should be easy to implement. - Linus *//* * Ok, demand-loading was easy, shared pages a little bit tricker. Shared * pages started 02.12.91, seems to work. - Linus. * * Tested sharing by executing about 30 /bin/sh: under the old kernel it * would have taken more than the 6M I have free, but it worked well as * far as I could see. * * Also corrected some "invalidate()"s - I wasn't doing enough of them. */#include <signal.h>#include <asm/system.h>#include <linux/sched.h>#include <linux/head.h>#include <linux/kernel.h>volatile void do_exit(long code);static inline volatile void oom(void){ printk("out of memory\n\r"); do_exit(SIGSEGV);}#define invalidate() \ /* 重新加载页目录表地址(起到刷新页变换高速缓冲器的作用) */__asm__("movl %%eax,%%cr3"::"a" (0))/* these are not to be changed without changing head.s etc *//* 此处是物理内存分页的状况,非线性地址 */#define LOW_MEM 0x100000 /* 分页内存起始地址 */#define PAGING_MEMORY (15*1024*1024) /* 分页内存大小 */#define PAGING_PAGES (PAGING_MEMORY>>12) /* 分页内存页数 */#define MAP_NR(addr) (((addr)-LOW_MEM)>>12) /* 指定物理内存地址映射到页号 */#define USED 100/* 页面被占用标志 */#define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \ /* 判断给定线性地址是否在当前进程代码断中 */current->start_code + current->end_code)static long HIGH_MEMORY = 0; /* 物理内存最高端地址 */#define copy_page(from,to) \ /* 从from处复制一页内存到to处 */__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")static unsigned char mem_map [ PAGING_PAGES ] = {0,};/* * Get physical address of first (actually last :-) free page, and mark it * used. If no free pages left, return 0. */unsigned long get_free_page(void) /* 从主内存区找空闲物理页面,从mem_map中找到一空闲项,将其所对应物理页清0并返回该页的物理地址 p561*/{ /*仅仅在物理地址空间中对mem_map(物理页)进行操作,未用到页表 */register unsigned long __res asm("ax");__asm__("std ; repne ; scasb\n\t" /* scasb比较eax和[edi]中的值 */ "jne 1f\n\t" "movb $1,1(%%edi)\n\t" "sall $12,%%ecx\n\t" /* sall 左移 */ "addl %2,%%ecx\n\t" "movl %%ecx,%%edx\n\t" "movl $1024,%%ecx\n\t" "leal 4092(%%edx),%%edi\n\t" /* leal 获得有效地址 */ "rep ; stosl\n\t" /* 页清0 */ "movl %%edx,%%eax\n" /*返回页面起始地址 */ "1:" :"=a" (__res) :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), "D" (mem_map+PAGING_PAGES-1) :"di","cx","dx");return __res;}/* * Free a page of memory at physical address 'addr'. Used by * 'free_page_tables()' */void free_page(unsigned long addr) /*释放一页物理内存,addr为该页的物理地址,根据addr算出该页在mem_map中的位置,然后将该项减1*/{ /*仅仅在物理地址空间中对mem_map(物理页)进行操作,未用到页表 */ if (addr < LOW_MEM) return; if (addr >= HIGH_MEMORY) panic("trying to free nonexistent page"); addr -= LOW_MEM; addr >>= 12; if (mem_map[addr]--) return; mem_map[addr]=0; panic("trying to free free page");}/* * This function frees a continuos block of page tables, as needed * by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks. */int free_page_tables(unsigned long from,unsigned long size) /*释放指定位置,指定长度的页及页表(相对于线性地址而言,参数为线性地址) */{ unsigned long *pg_table; unsigned long * dir, nr; if (from & 0x3fffff) panic("free_page_tables called with wrong alignment"); if (!from) panic("Trying to free up swapper memory space"); size = (size + 0x3fffff) >> 22; /* 算出共有几个页表 */ dir = (unsigned long *) ((from>>20) & 0xffc); /*算出起始页目录项地址*/ for ( ; size-->0 ; dir++) { if (!(1 & *dir))/*页目录项中的p位是否为0*/ continue; pg_table = (unsigned long *) (0xfffff000 & *dir);/* 取页表地址 */ for (nr=0 ; nr<1024 ; nr++) { if (1 & *pg_table)/*页表项中的p位是否为0*/ free_page(0xfffff000 & *pg_table);/* 释放页表项对应物理页 */ *pg_table = 0;/* 释放页表项 */ pg_table++; } free_page(0xfffff000 & *dir);/*释放页表 */ *dir = 0;/*页目录项清0*/ } invalidate(); /* 重新加载页目录表地址(起到刷新页变换高速缓冲器的作用) */ return 0;}/* * Well, here is one of the most complicated functions in mm. It * copies a range of linerar addresses by copying only the pages. * Let's hope this is bug-free, 'cause this one I don't want to debug :-) * * Note! We don't copy just any chunks of memory - addresses have to * be divisible by 4Mb (one page-directory entry), as this makes the * function easier. It's used only by fork anyway. * * NOTE 2!! When from==0 we are copying kernel space for the first * fork(). Then we DONT want to copy a full page-directory entry, as * that would lead to some serious memory waste - we just copy the * first 160 pages - 640kB. Even that is more than we need, but it * doesn't take any more memory - we don't copy-on-write in the low * 1 Mb-range, so the pages can be shared with the kernel. Thus the * special case for nr=xxxx. */int copy_page_tables(unsigned long from,unsigned long to,long size) /* 复制指定位置,指定长度的页表(相对于线性地址而言,参数为线性地址)并将页表项设为只读,此后有两套页表映射到同一物理页上 */{ /* 该函数仅被fork使用 */ unsigned long * from_page_table; unsigned long * to_page_table; unsigned long this_page; unsigned long * from_dir, * to_dir; unsigned long nr; if ((from&0x3fffff) || (to&0x3fffff)) panic("copy_page_tables called with wrong alignment"); from_dir = (unsigned long *) ((from>>20) & 0xffc); /*计算原目录项起始地址(物理) */ to_dir = (unsigned long *) ((to>>20) & 0xffc);/*计算目标目录项起始地址(物理) */ size = ((unsigned) (size+0x3fffff)) >> 22;/*计算欲复制的页表数 */ for( ; size-->0 ; from_dir++,to_dir++) { if (1 & *to_dir) /* 若目的页目录项已存在则死机*/ panic("copy_page_tables: already exist"); if (!(1 & *from_dir))/*若原页目录项不存在则跳过 */ continue; from_page_table = (unsigned long *) (0xfffff000 & *from_dir);/*页表地址 */ if (!(to_page_table = (unsigned long *) get_free_page()))/*分配一新物理页给目的页表 */ return -1; /* Out of memory, see freeing */ *to_dir = ((unsigned long) to_page_table) | 7;/*复制目的目录项 */ nr = (from==0)?0xA0:1024;/*若原地址为0,说明是进程0复制进程1,此时仅需复制160项页表项(起到节约内存的作用);否则一律复制1024项 */ for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {/*逐项复制页表项 */ this_page = *from_page_table; if (!(1 & this_page)) continue; this_page &= ~2;/*置新页表项为只读 */ *to_page_table = this_page; if (this_page > LOW_MEM) {/*若该页>1mb则需修改mem_map(mem_map以1mb处为起始点) */ *from_page_table = this_page;/*将原页表项也设为只读 */ this_page -= LOW_MEM; this_page >>= 12; mem_map[this_page]++; } } } invalidate(); return 0;}/* * This function puts a page in memory at the wanted address. * It returns the physical address of the page gotten, 0 if * out of memory (either when trying to access page-table or * page.) */unsigned long put_page(unsigned long page,unsigned long address) /*将一页物理内存映射到进程的线性地址空间中,参数page为页物理地址,aadress为欲映射的线性地址*/{ unsigned long tmp, *page_table;/* NOTE !!! This uses the fact that _pg_dir=0 */ if (page < LOW_MEM || page >= HIGH_MEMORY) printk("Trying to put page %p at %p\n",page,address); if (mem_map[(page-LOW_MEM)>>12] != 1) printk("mem_map disagrees with %p at %p\n",page,address); page_table = (unsigned long *) ((address>>20) & 0xffc);/*根据线性地址获得目录项地址*/ if ((*page_table)&1) /* 获得页表地址(若不存在则新建一页表) */ page_table = (unsigned long *) (0xfffff000 & *page_table); else { if (!(tmp=get_free_page())) return 0; *page_table = tmp|7; page_table = (unsigned long *) tmp; } page_table[(address>>12) & 0x3ff] = page | 7;/*另页表项指向该物理页 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -