⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 memory.c

📁 linux1.0的源码
💻 C
字号:
#include <signal.h>#include <linux/config.h>#include <linux/head.h>#include <linux/kernel.h>#include <asm/system.h>int do_exit(long code);#define invalidate() \//下面的内嵌汇编的意思是://	movl	0	%eax//	movl	%eax	%cr3		//将页目录的物理基地址设为0__asm__("movl %%eax,%%cr3"::"a" (0))#if (BUFFER_END < 0x100000)		//0x200000=2M#define LOW_MEM 0x100000#else#define LOW_MEM BUFFER_END#endif/* these are not to be changed - thay are calculated from the above */#define PAGING_MEMORY (HIGH_MEMORY - LOW_MEM)	//分页内存有6M#define PAGING_PAGES (PAGING_MEMORY/4096)	//有多少个页=1536个页#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)		//页号#if (PAGING_PAGES < 10)#error "Won't work"#endif#define copy_page(from,to) \//下面的内嵌汇编的意思是://	movl	1024	%ecx//	movl	from	%esi//	movl	to	%edi//	cld//	rep	movsl__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")static unsigned short mem_map [ PAGING_PAGES ] = {0,};	//如果某页号置1就说明此页已在用/* * 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){    register unsigned long __res asm("ax");    //下面的内嵌汇编的意思是:    //	movl	0	%eax    //	movl	LOW_MEM	%ebx    //	movl	PAGING_PAGES	%ecx    //	movl	mem_map+PAGING_PAGES-1	%edi    //	std							//地址减量方向    //	repne						//ZF=0时循环    //	scasw						//eax---->es:di    //	jne	1f						//每页都已用,直接返回    //	movw	$1	%edi+2			//将mem_map[X]=1,其中的X是寻找到的第几个页    //	sall	$12	%ecx				//PAGING_PAGES乘4096,计数器以字节为单位,寻找页的基址    //	movl	%ecx	%edx		//保存基址    //	addl	%ebx	%edx			//基址加BUFFER_END成为实际地址    //	movl	$1024	%ecx    //	leal	%edx+4092	%edi		//%edx+4092是因为前面是std,所以要减掉4    //	rep    //	stosl							//eax--->es:edi,主要是将该页初始化为0    //	movl	%edx	%eax		//将此基址保存入eax    //	movl	%eax	_res    __asm__("std ; repne ; scasw\n\t"            "jne 1f\n\t"            "movw $1,2(%%edi)\n\t"            "sall $12,%%ecx\n\t"            "movl %%ecx,%%edx\n\t"            "addl %2,%%edx\n\t"            "movl $1024,%%ecx\n\t"            "leal 4092(%%edx),%%edi\n\t"            "rep ; stosl\n\t"            "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){    if (addr<LOW_MEM) return;			//不能低于2M    if (addr>HIGH_MEMORY)				//不能高于8M        panic("trying to free nonexistent page");    //下面2行还原此基址在mem_map[]的地址    addr -= LOW_MEM;    addr >>= 12;    if (mem_map[addr]--) return;			//如果是共享内存,不FREE    mem_map[addr]=0;					//FREE此页    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)			//必须能被4M整除,最小数应为0x400000        panic("free_page_tables called with wrong alignment");    if (!from)					//不能为0,不能FREE内核区        panic("Trying to free up swapper memory space");    size = (size + 0x3fffff) >> 22;	//1代表4M,2代表8M,依此类推    dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */	//找出目录地址,该地址是由copy_page_tables设置,其中from>>20是只移动20位,而不是22位,是因为以后要乘4寻址,其中0xffc是取32位地址的最高10位并乘4    for ( ; size-->0 ; dir++) {        if (!(1 & *dir))            continue;        pg_table = (unsigned long *) (0xfffff000 & *dir);	//从目录地址中取得页表地址        for (nr=0 ; nr<1024 ; nr++) {            if (1 & *pg_table)		//如果该页表在内存中,就FREE它                free_page(0xfffff000 & *pg_table);            *pg_table = 0;			//将此页表项的指针指的内容设为0            pg_table++;			//指向下一个页表项        }        free_page(0xfffff000 & *dir);	//FREE此目录地址内容所指的内存地址        *dir = 0;					//标记此目录空闲    }    invalidate();					//将cr3设为0    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){    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))		//from和to必须被4M整除,4M边界对齐        panic("copy_page_tables called with wrong alignment");    from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */	 //取from高10位,然后乘4,形成目录项    to_dir = (unsigned long *) ((to>>20) & 0xffc);    size = ((unsigned) (size+0x3fffff)) >> 22;    for( ; size-->0 ; from_dir++,to_dir++) {        if (1 & *to_dir)		//看看to_dir指针指定的内存地址的值的最低位是否为1,如为1,说明该值所表示的地址已分配            panic("copy_page_tables: already exist");        if (!(1 & *from_dir))	//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;		//在to_dir所指的内存中填写刚取得的自由内存地址,并设该值的最低位为1,表示已分配        nr = (from==0)?0xA0:1024;		//160X4K=640K        for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {            this_page = *from_page_table;	  //将源页表指针所指的值赋给this_page            if (!(1 & this_page))                continue;            this_page &= ~2;		     	//将该值表示的地址的最低第2位置为0,只允许级别3的进程读            *to_page_table = this_page;			//将源页表中的页表项值放入to_page_table指针所指的内存地址中            if (this_page > LOW_MEM) {		//是否是内核区域                *from_page_table = this_page;	//将源页表项的第2位也置为0,原因是共享了                this_page -= LOW_MEM;		//将该页映射置1,表示在用                this_page >>= 12;                mem_map[this_page]++;            }        }    }    invalidate();								//将cr3代表的基址设为0    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){    unsigned long tmp, *page_table;    /* NOTE !!! This uses the fact that _pg_dir=0 */    if (page < LOW_MEM || page > HIGH_MEMORY) 	//不低于2M,不高于8M        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;	//该地址相对于page_table首地址的相对位置,在此处放入page|7    return page;}void un_wp_page(unsigned long * table_entry)	//此函数的作用是保护内核区页或共享内存页{    unsigned long old_page,new_page;    old_page = 0xfffff000 & *table_entry;    if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {	//如果老页不在内核区并且页已分配在用        *table_entry |= 2;				  //只将此页的第2位置1,使该页可读可写        return;					    //直接返回    }    if (!(new_page=get_free_page()))        do_exit(SIGSEGV);    if (old_page >= LOW_MEM)        mem_map[MAP_NR(old_page)]--;	//将共享页的计数减1    *table_entry = new_page | 7;			  //入口重新定向为新页的地址    copy_page(old_page,new_page);			//将老页拷贝入新页}/* * This routine handles present pages, when users try to write * to a shared page. It is done by copying the page to a new address * and decrementing the shared-page counter for the old page. */void do_wp_page(unsigned long error_code,unsigned long address){    un_wp_page((unsigned long *)               (((address>>10) & 0xffc) + (0xfffff000 &*((unsigned long *) ((address>>20) &0xffc)))));		                //其中((address>>10) & 0xffc)是相对首地址的偏移地址,是取address的12到21位共10位,(0xfffff000 &*((unsigned long *) ((address>>20) &0xffc))))是计算出页表首地址}void write_verify(unsigned long address){    unsigned long page;    if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))        return;					  //如果页目录指向的页表不在内存,直接返回    page &= 0xfffff000;					//取页表首地址    page += ((address>>10) & 0xffc);		//加上address中的中间10位,并乘4,就形成了页表项地址    if ((3 & *(unsigned long *) page) == 1)  /* non-writeable, present */	//对共享内存的处理        un_wp_page((unsigned long *) page);    return;}void do_no_page(unsigned long error_code,unsigned long address)	//如果没有页,取一个自由页,并放入指定的页表地址(address)中{    unsigned long tmp;    if (tmp=get_free_page())        if (put_page(tmp,address))            return;    do_exit(SIGSEGV);}void calc_mem(void){    int i,j,k,free=0;    long * pg_tbl;    for(i=0 ; i<PAGING_PAGES ; i++)        if (!mem_map[i]) free++;		         	//计算有多少自由页    printk("%d pages free (of %d)\n\r",free,PAGING_PAGES);    for(i=2 ; i<1024 ; i++) {			    	//计算各页目录中的页表用掉了多少页        if (1&pg_dir[i]) {            pg_tbl=(long *) (0xfffff000 & pg_dir[i]);            for(j=k=0 ; j<1024 ; j++)                if (pg_tbl[j]&1)                    k++;            printk("Pg-dir[%d] uses %d pages\n",i,k);        }    }}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -