日期:2014-05-16  浏览次数:20766 次

Linux 内核解读之内存管理----memory.c

转载请注明原文出处http://blog.csdn.net/lizhiliang06/article/details/8655115

80x86体系结构中,Linux内核的内存管理程序使用分页管理方式。利用页目录和页表结构处理内核中其他部分代码对内存申请和释放操作。Memory.s是linux内存管理的核心,80x86内存管理最大寻址范围是4G的内存空间,在最大1024*1024*4KB的范围内寻址,内存管理分为3级寻址,第一级是目录,第二集是页表,第三级是页内偏移地址,它们在内存的分布关系如下图:


线性地址转换如下图所示:


1,这篇文章里面涉及到了页面出错异常处理。

2,写时复制机制。

3,需求加载机制。

具体代码详解如下:


#include <signal.h>					//信号头文件,涉及到信号的结构和操作函数
#include <asm/system.h>				//系统头文件,设置修改描述符、中断门等
#include <linux/sched.h>			//调度头文件,定义了任务结构,和调度函数
#include <linux/head.h>				//head头文件, 定义了段描述符结构等
#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);
}

//刷新页变换高速缓冲区
//为了提高地址转换效率,CPU将最近使用的页表数据存放在芯片中高速缓冲区,
//修改过信息后,就需要刷新缓冲区,这里是通过加载页目录基址寄存器cr3的方法来进行刷新的,eax = 0为物理地址的0,即页目录的基地址
#define invalidate() \
__asm__("movl %%eax,%%cr3"::"a" (0))

/* these are not to be changed without changing head.s etc */
#define LOW_MEM 0x100000				//定义内存低端(1MB)
#define PAGING_MEMORY (15*1024*1024)	//定义 15MB作为主内存即分页内存
#define PAGING_PAGES (PAGING_MEMORY>>12) //分页后的主内存页数(15MB/4KB)
#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;

//复制1页内存
#define copy_page(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)
{
register unsigned long __res asm("ax");

__asm__("std ; repne ; scasb\n\t"
	"jne 1f\n\t"
	"movb $1,1(%%edi)\n\t"
	"sall $12,%%ecx\n\t"
	"addl %2,%%ecx\n\t"
	"movl %%ecx,%%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)//释放一页内存,从物理地址addr开始的一页面内存开始释放,
{
	if (addr < LOW_MEM) return;	//如果地址小于最低内存低端,则返回
	if (addr >= HIGH_MEMORY)	//如果地址大于高端,着提示错误
		panic("trying to free nonexistent page");
	addr -= LOW_MEM;//减去低端的地址,右移12位即除以4*1024,4KB,得到页面号
	addr >>= 12;
	if (mem_map[addr]--) return;//如果内存页面字节不等于0,则减1,并返回
	mem_map[addr]=0;		//否则设置页面映射字节为0,提示释放重复经释放掉的错误信息
	panic("trying to free free page");
}


int free_page_tables(unsigned long from, unsigned long size)
{
	unsigned long *pg_table;
	unsigned long *dir, nr;
	
	if(from & 0x3fffff)    //0x4