/*
* 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
*/
/*
* Ǵ01.12.91 ʼд - ڳƱǺҪĳ
* ӦǺױƵ - 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.
*/
/*
* OKǱȽױдģҳȴҪе㼼ɡҳ
* 02.12.91 ʼдģܹ - Linus
*
* ִͨдԼ30 /bin/sh Թ˲ԣں˵Ҫռö
* 6M ڴ棬Ŀǰȴáڿúܺá
*
* "invalidate()"Ҳ - ⷽһĲ
*/

#include <signal.h>		// źͷļźŷųźŽṹԼźŲԭ͡

#include <asm/system.h>		// ϵͳͷļû޸/жŵȵǶʽꡣ

#include <linux/sched.h>	// ȳͷļṹtask_structʼ0 ݣ
// һЩйúͻȡǶʽຯ䡣
#include <linux/head.h>		// head ͷļ˶ļ򵥽ṹͼѡ
#include <linux/kernel.h>	// ںͷļһЩں˳úԭζ塣

volatile void do_exit (long code);	// ˳kernel/exit.c102 С

//// ʾڴϢ˳
static inline volatile void
oom (void)
{
  printk ("out of memory\n\r");
  do_exit (SIGSEGV);		// do_exit()Ӧʹ˳룬źֵSIGSEGV(11)
}				// ֵͬĳ뺬ǡԴʱáͬ塣

// ˢҳ任ٻ꺯
// ΪߵַתЧʣCPU ʹõҳݴоƬиٻС޸Ĺҳ
// Ϣ֮󣬾Ҫˢ¸ûʹ¼ҳĿ¼ַĴcr3 ķˢ¡
// eax = 0ҳĿ¼Ļַ
#define invalidate() \
__asm__( "movl %%eax,%%cr3":: "a" (0))

/* these are not to be changed without changing head.s etc */
/* 涨ҪĶҪhead.s ļеϢһı */
// linux 0.11 ںĬֵ֧ڴ16M޸Щʺϸڴ档
#define LOW_MEM 0x100000	// ڴͶˣ1MB
#define PAGING_MEMORY (15*1024*1024)	// ҳڴ15MBڴ15M
#define PAGING_PAGES (PAGING_MEMORY>>12)	// ҳڴҳ
#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)	// ָڴַӳΪҳš
#define USED 100		// ҳ汻ռñ־μ405 С

// CODE_SPACE(addr) ((((addr)+0xfff)&~0xfff) < current->start_code + current->end_code)
// úжϸַǷλڵǰ̵ĴУμ252 С
#define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \
current->start_code + current->end_code)

static long HIGH_MEMORY = 0;	// ȫֱʵڴ߶˵ַ

// 1 ҳڴ棨4K ֽڣ
#define copy_page(from,to) \
__asm__( "cld ; rep ; movsl":: "S" (from), "D" (to), "c" (1024): "cx", "di", "si")

// ڴӳֽͼ(1 ֽڴ1 ҳڴ)ÿҳӦֽڱ־ҳ浱ǰãռã
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.
*/
/*
* ȡ׸(ʵ1 :-)ҳ棬Ϊʹáûпҳ棬
* ͷ0
*/
//// ȡҳ档Ѿûпڴˣ򷵻0
// 룺%1(ax=0) - 0%2(LOW_MEM)%3(cx=PAGING PAGES)%4(edi=mem_map+PAGING_PAGES-1)
// %0(ax=ҳʼַ)
// %4 Ĵʵָmem_map[]ڴֽͼһֽڡֽͼĩ˿ʼǰɨ
// ҳ־ҳΪPAGING_PAGESҳУڴӳֽΪ0򷵻ҳַ
// ע⣡ָֻڴһҳҳ棬ûӳ䵽ĳ̵Եַȥ
// put_page()ӳġ
unsigned long
get_free_page (void)
{
  register unsigned long __res asm ("ax");

  __asm__ ("std ; repne ; scasb\n\t"	// λλal(0)Ӧÿҳ(di)ݱȽϣ
	   "jne 1f\n\t"		// ûе0 ֽڣת0
	   "movb $1,1(%%edi)\n\t"	// Ӧҳڴӳλ1
	   "sall $12,%%ecx\n\t"	// ҳ*4K = ҳʼַ
	   "addl %2,%%ecx\n\t"	// ټϵͶڴַҳʵʼַ
	   "movl %%ecx,%%edx\n\t"	// ҳʵʼַ??edx Ĵ
	   "movl $1024,%%ecx\n\t"	// Ĵecx üֵ1024
	   "leal 4092(%%edx),%%edi\n\t"	// 4092+edx λ??edi(ҳĩ)
	   "rep ; stosl\n\t"	// edi ָڴ㣨Ҳҳ㣩
	   "movl %%edx,%%eax\n"	// ҳʼַ??eaxֵ
"1:": "=a" (__res): "" (0), "i" (LOW_MEM), "c" (PAGING_PAGES), "D" (mem_map + PAGING_PAGES - 1):"di", "cx",
	   "dx");
  return __res;			// ؿҳַ޿Ҳ򷵻0
}

/*
* Free a page of memory at physical address 'addr'. Used by
* 'free_page_tables()'
*/
/*
* ͷַ'addr'ʼһҳڴ档ں'free_page_tables()'
*/
//// ͷַaddr ʼһҳڴ档
// 1MB µڴռں˳ͻ壬Ϊҳڴռ䡣
void
free_page (unsigned long addr)
{
  if (addr < LOW_MEM)
    return;			// ַaddr СڴͶˣ1MB򷵻ء
  if (addr >= HIGH_MEMORY)	// ַaddr>=ڴ߶ˣʾϢ
    panic ("trying to free nonexistent page");
  addr -= LOW_MEM;		// ַȥͶڴλãٳ4KBҳš
  addr >>= 12;
  if (mem_map[addr]--)
    return;			// Ӧڴҳӳֽڲ01 ء
  mem_map[addr] = 0;		// öӦҳӳֽΪ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.
*/
/*
* 溯ͷҳڴ飬'exit()'Ҫúcopy_page_tables()
* ƣú4Mb ڴ顣
*/
//// ָԵַ޳ҳͷŶӦڴҳָڴ鲢ñС
// ҳĿ¼λַ0 ʼ1024 ռ4K ֽڡÿĿ¼ָһҳ
// ҳַ0x1000 ʼĿ¼ռ䣩ÿҳ1024 Ҳռ4K ڴ档
// ÿҳӦһҳڴ棨4KĿ¼ҳĴСΪ4 ֽڡ
// from - ʼַsize - ͷŵĳȡ
int
free_page_tables (unsigned long from, unsigned long size)
{
  unsigned long *pg_table;
  unsigned long *dir, nr;

  if (from & 0x3fffff)		// Ҫͷڴĵַ4M Ϊ߽硣
    panic ("free_page_tables called with wrong alignment");
  if (!from)			// ͼͷں˺ͻռռ䡣
    panic ("Trying to free up swapper memory space");
// ռҳĿ¼(4M Ľλ)Ҳռҳ
  size = (size + 0x3fffff) >> 22;
// һʼĿ¼ӦĿ¼=from>>22ÿռ4 ֽڣҳĿ¼Ǵ
// ַ0 ʼʵʵĿ¼ָ=Ŀ¼<<2Ҳ(from>>20)0xffc ȷ
// Ŀ¼ָ뷶ΧЧ
  dir = (unsigned long *) ((from >> 20) & 0xffc);	/* _pg_dir = 0 */
  for (; size-- > 0; dir++)
    {				// size ҪͷڴĿ¼
      if (!(1 & *dir))		// Ŀ¼Ч(P λ=0)
	continue;		// Ŀ¼λ0(P λ)ʾӦҳǷڡ
      pg_table = (unsigned long *) (0xfffff000 & *dir);	// ȡĿ¼ҳַ
      for (nr = 0; nr < 1024; nr++)
	{			// ÿҳ1024 ҳ
	  if (1 & *pg_table)	// ҳЧ(P λ=1)ͷŶӦڴҳ
	    free_page (0xfffff000 & *pg_table);
	  *pg_table = 0;	// ҳ㡣
	  pg_table++;		// ָҳһ
	}
      free_page (0xfffff000 & *dir);	// ͷŸҳռڴҳ档ҳ
// ַ1M ڣʲô
      *dir = 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.
*/
/*
* ˣڴmm Ϊӵĳ֮һֻͨڴҳ
* һΧԵַеݡϣûдΪҲ
* ٵ?
*
* ע⣡ǲǽκڴ - ڴĵַҪ4Mb ı
* һҳĿ¼ӦڴСΪʹܼ򵥡
* fork()ʹãfork.c 56 У
*
* ע2from==0 ʱΪһfork()øں˿ռ䡣ʱ
* 븴ҳĿ¼Ӧڴ棬Ϊᵼڴص˷ - 
* ֻͷ160 ҳ - Ӧ640kBʹǸЩҳҲѾǵ
* ⲻռøڴ - ڵ1Mb ڴ淶ΧǲִдʱƲ
* Щҳں˹nr=xxxx nr ڳָҳ
*/
//// ָԵַͳȣҳڴӦҳĿ¼ҳӶƵҳĿ¼
//// ҳӦԭڴʹá
// ַָͳȵڴӦҳĿ¼ҳҳҳԭڴ
// ˺̽ڴֱһִдʱŷµڴҳдʱƻƣ
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;

// ԴַĿĵַҪ4Mb ڴַ߽ϡ
  if ((from & 0x3fffff) || (to & 0x3fffff))
    panic ("copy_page_tables called with wrong alignment");
// ȡԴַĿĵַĿ¼(from_dir to_dir)μ115 ע͡
  from_dir = (unsigned long *) ((from >> 20) & 0xffc);	/* _pg_dir = 0 */
  to_dir = (unsigned long *) ((to >> 20) & 0xffc);
// ҪƵڴռõҳҲĿ¼
  size = ((unsigned) (size + 0x3fffff)) >> 22;
// 濪ʼÿռõҳνиƲ
  for (; size-- > 0; from_dir++, to_dir++)
    {
// ĿĿ¼ָҳѾ(P=1)
      if (1 & *to_dir)
	panic ("copy_page_tables: already exist");
// ԴĿ¼δʹãøƶӦҳ
      if (!(1 & *from_dir))
	continue;
// ȡǰԴĿ¼ҳĵַ??from_page_table
      from_page_table = (unsigned long *) (0xfffff000 & *from_dir);
// ΪĿҳȡһҳڴ棬0 ˵û뵽ڴҳ档ֵ=-1˳
      if (!(to_page_table = (unsigned long *) get_free_page ()))
	return -1;		/* Out of memory, see freeing */
// ĿĿ¼Ϣ7 Ǳ־Ϣʾ(Usr, R/W, Present)
      *to_dir = ((unsigned long) to_page_table) | 7;
// Եǰҳ踴Ƶҳں˿ռ䣬踴ͷ160 ҳҪ
// 1 ҳе1024 ҳ档
      nr = (from == 0) ? 0xA0 : 1024;
// ڵǰҳʼָĿnr ڴҳ档
      for (; nr-- > 0; from_page_table++, to_page_table++)
	{
	  this_page = *from_page_table;	// ȡԴҳݡ
	  if (!(1 & this_page))	// ǰԴҳûʹãøơ
	    continue;
// λҳR/W ־(0)(U/S λ0R/W ûáU/S 1R/W 0
// ôûĴֻܶҳ档U/S R/W λдȨޡ)
	  this_page &= ~2;
	  *to_page_table = this_page;	// ҳƵĿҳС
// ҳָҳĵַ1M ϣҪڴҳӳmem_map[]Ǽ
// ҳţΪҳӳӦô
	  if (this_page > LOW_MEM)
	    {
// ĺԴҳָڴҳҲΪֻΪڿʼ̹ڴˡ
// һڴҪдͨҳ쳣дΪִдĽ̷
// һҳµĿҳ棬ҲдʱƵĲ
	      *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.)
*/
/*
* 溯һڴҳַָҳַ
* ڴ治(ڷҳҳʱ)򷵻0
*/
//// һڴҳӳ䵽ָԵַ
// ҪҳĿ¼ҳָҳϢɹ򷵻ҳַ
unsigned long
put_page (unsigned long page, unsigned long address)
{
  unsigned long tmp, *page_table;

/* NOTE !!! This uses the fact that _pg_dir=0 */
/* ע!!!ʹҳĿ¼ַ_pg_dir=0  */

// ҳλõLOW_MEM(1Mb)򳬳ϵͳʵʺڴ߶HIGH_MEMORY򷢳档
  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);
// Ŀ¼Ч(P=1)(Ҳָҳڴ)ȡָҳĵַ??page_table
  if ((*page_table) & 1)
    page_table = (unsigned long *) (0xfffff000 & *page_table);
  else
    {
// ҳҳʹãڶӦĿ¼Ӧ־7User, U/S, R/WȻ
// ҳĵַ??page_table
      if (!(tmp = get_free_page ()))
	return 0;
      *page_table = tmp | 7;
      page_table = (unsigned long *) tmp;
    }
// ҳַָڴҳҳݡÿҳ1024 (0x3ff)
  page_table[(address >> 12) & 0x3ff] = page | 7;
/* no need for invalidate */
/* Ҫˢҳ任ٻ */
  return page;			// ҳַ
}

//// ȡдҳ溯ҳ쳣жϹд쳣Ĵдʱƣ
// Ϊҳָ롣
// [ un_wp_page ˼ȡҳдUn-Write Protected]
void
un_wp_page (unsigned long *table_entry)
{
  unsigned long old_page, new_page;

  old_page = 0xfffff000 & *table_entry;	// ȡԭҳӦĿ¼š
// ԭҳַڴͶLOW_MEM(1Mb)ҳӳֽͼֵΪ1ʾ
// 1 ΣҳûбڸҳҳR/W ־дˢҳ任
// ٻ壬Ȼ󷵻ء
  if (old_page >= LOW_MEM && mem_map[MAP_NR (old_page)] == 1)
    {
      *table_entry |= 2;
      invalidate ();
      return;
    }
// ڴһҳҳ档
  if (!(new_page = get_free_page ()))
    oom ();			// Out of Memoryڴ治
// ԭҳڴͶˣζmem_map[]>1ҳǹģԭҳҳӳ
// ֵݼ1ȻָҳݸΪҳĵַÿɶдȱ־(U/S, R/W, P)
// ˢҳ任ٻ塣ԭҳݸƵҳ档
  if (old_page >= LOW_MEM)
    mem_map[MAP_NR (old_page)]--;
  *table_entry = new_page | 7;
  invalidate ();
  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.
*
* If it's in code space we exit with a segment error.
*/
/*
* ûͼһҳдʱúѴڵڴҳ棬дʱƣ
* ͨҳ渴ƵһµַϲݼԭҳĹҳֵʵֵġ
*
* ڴռ䣬ǾԶδϢ˳
*/
//// ҳ쳣жϴõC дҳ洦page.s бá
// error_code CPU Զaddress ҳԵַ
// дҳʱ踴ҳ棨дʱƣ
void
do_wp_page (unsigned long error_code, unsigned long address)
{
#if 0
/* we cannot do this yet: the estdio library writes to code space */
/* stupid, stupid. I really want the libc.a from GNU */
/* ڻΪestdio ڴռִд */
/* ̫޴ˡGNU õlibc.a ⡣*/
  if (CODE_SPACE (address))	// ַλڴռ䣬ִֹг
    do_exit (SIGSEGV);
#endif
// ȡҳ汣ָҳҳеҳָ룬㷽ǣ
// ((address>>10) & 0xffc)ַָҳҳеƫƵַ
// (0xfffff000 &((address>>20) &0xffc))ȡĿ¼ҳĵֵַ
// ((address>>20) &0xffc)ҳҳĿ¼ָ룻
// ӼַָӦҳҳָ롣Թҳиơ
  un_wp_page ((unsigned long *)
	      (((address >> 10) & 0xffc) + (0xfffff000 &
					    *((unsigned long
					       *) ((address >> 20) &
						   0xffc)))));

}

//// дҳ֤
// ҳ治дҳ档fork.c 34 бá
void
write_verify (unsigned long address)
{
  unsigned long page;

// жַָӦҳĿ¼ҳǷ(P)(P=0)򷵻ء
  if (!((page = *((unsigned long *) ((address >> 20) & 0xffc))) & 1))
    return;
// ȡҳĵַַָҳҳеҳƫֵöӦҳҳָ롣
  page &= 0xfffff000;
  page += ((address >> 10) & 0xffc);
// ҳ治д(־R/W ûλ)ִй͸ҳдʱƣ
  if ((3 & *(unsigned long *) page) == 1)	/* non-writeable, present */
    un_wp_page ((unsigned long *) page);
  return;
}

//// ȡһҳڴ沢ӳ䵽ָԵַ
// get_free_page()ͬget_free_page()ȡڴһҳڴ档ú
// ǻȡһҳڴҳ棬һput_page()ҳӳ䵽ָԵַ
// 
void
get_empty_page (unsigned long address)
{
  unsigned long tmp;

// ȡһҳ棬߲ܽҳõַָʾڴ治Ϣ
// 279 Ӣע͵ĺǣʹִget_free_page()0 ҲνΪput_page()
// лԴٴҳģ210 С
  if (!(tmp = get_free_page ()) || !put_page (tmp, address))
    {
      free_page (tmp);		/* 0 is ok - ignored */
      oom ();
    }
}

/*
* try_to_share() checks the page at address "address" in the task "p",
* to see if it exists, and if it is clean. If so, share it with the current
* task.
*
* NOTE! This assumes we have checked that p != current, and that they
* share the same executable.
*/
/*
* try_to_share()"p"мλڵַ"address"ҳ棬ҳǷڣǷɾ
* ǸɾĻ뵱ǰ
*
* ע⣡Ѽٶp !=ǰ񣬲ǹͬһִг
*/
//// ԶԽַָҳй
// ͬʱָ֤ĵַǷѾҳ棬
// 1-ɹ0-ʧܡ
static int
try_to_share (unsigned long address, struct task_struct *p)
{
  unsigned long from;
  unsigned long to;
  unsigned long from_page;
  unsigned long to_page;
  unsigned long phys_addr;

// ָڴַҳĿ¼
  from_page = to_page = ((address >> 20) & 0xffc);
// p ĴʼַӦҳĿ¼
  from_page += ((p->start_code >> 20) & 0xffc);
// 㵱ǰдʼַӦҳĿ¼
  to_page += ((current->start_code >> 20) & 0xffc);
/* is there a page-directory at from? */
/* from ǷҳĿ¼*/
// *** p ҳв
// ȡҳĿ¼ݡĿ¼Ч(P=0)򷵻ءȡĿ¼Ӧҳַ??from
  from = *(unsigned long *) from_page;
  if (!(from & 1))
    return 0;
  from &= 0xfffff000;
// ַӦҳֵָȡҳ??phys_addr
  from_page = from + ((address >> 10) & 0xffc);
  phys_addr = *(unsigned long *) from_page;
/* is the page clean and present? */
/* ҳɾҴ*/
// 0x41 ӦҳеDirty Present ־ҳ治ɾЧ򷵻ء
  if ((phys_addr & 0x41) != 0x01)
    return 0;
// ȡҳĵַ??phys_addrҳַڻСڴͶ(1M)Ҳ˳
  phys_addr &= 0xfffff000;
  if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM)
    return 0;
// *** Եǰҳв
// ȡҳĿ¼??toĿ¼Ч(P=0)ȡҳ棬to_page ָĿ¼
  to = *(unsigned long *) to_page;
  if (!(to & 1))
    if (to = get_free_page ())
      *(unsigned long *) to_page = to | 7;
    else
      oom ();
// ȡӦҳַ??toҳַ??to_pageӦҳѾڣ
  to &= 0xfffff000;
  to_page = to + ((address >> 10) & 0xffc);
  if (1 & *(unsigned long *) to_page)
    panic ("try_to_share: to_page already exists");
/* share them: write-protect */
/* ǽйд */
// p ҳд־(R/W=0 ֻ)ҵǰеĶӦҳָ
  *(unsigned long *) from_page &= ~2;
  *(unsigned long *) to_page = *(unsigned long *) from_page;
// ˢҳ任ٻ塣
  invalidate ();
// ҳҳţӦҳӳеõ1
  phys_addr -= LOW_MEM;
  phys_addr >>= 12;
  mem_map[phys_addr]++;
  return 1;
}

/*
* share_page() tries to find a process that could share a page with
* the current one. Address is the address of the wanted page relative
* to the current data space.
*
* We first check if it is at all feasible by checking executable->i_count.
* It should be >1 if there are other tasks sharing this inode.
*/
/*
* share_page()ͼҵһ̣뵱ǰ̹ҳ档address 
* ǰݿռĳҳַ
*
* ͨexecutable->i_count ֤ǷСѹ
* inodeӦô1
*/
//// ҳ档ȱҳʱܷҳ档
// 1 - ɹ0 - ʧܡ
static int
share_page (unsigned long address)
{
  struct task_struct **p;

// ǲִеģ򷵻ءexcutable ִн̵ڴi ڵṹ
  if (!current->executable)
    return 0;
// ִֻܵ(executable->i_count=1)Ҳ˳
  if (current->executable->i_count < 2)
    return 0;
// Ѱ뵱ǰ̿ɹҳḶ̌Զַָҳй
  for (p = &LAST_TASK; p > &FIRST_TASK; --p)
    {
      if (!*p)			// УѰҡ
	continue;
      if (current == *p)	// ǵǰҲѰҡ
	continue;
      if ((*p)->executable != current->executable)	// executable ȣҲ
	continue;
      if (try_to_share (address, *p))	// Թҳ档
	return 1;
    }
  return 0;
}

//// ҳ쳣жϴõĺȱҳ쳣page.s бá
// error_code CPU Զaddress ҳԵַ
void
do_no_page (unsigned long error_code, unsigned long address)
{
  int nr[4];
  unsigned long tmp;
  unsigned long page;
  int block, i;

  address &= 0xfffff000;	// ҳַ
// ָԵַڽ̿ռڽַ̻ƫƳֵ
  tmp = address - current->start_code;
// ǰ̵executable գַָ+ݳȣһҳڴ棬ӳ
// Ӱ䵽ָԵַexecutable ǽ̵i ڵṹֵΪ0̸տʼã
// Ҫڴ棻ָԵַݳȣµڴռ䣬ҲҪ衣
// ˾ֱӵget_empty_page()һҳڴ沢ӳ䵽ָԵַɡ
// start_code ǽ̴εַend_data Ǵݳȡlinux ںˣĴκ
// ݶʼַͬġ
  if (!current->executable || tmp >= current->end_data)
    {
      get_empty_page (address);
      return;
    }
// Թҳɹ˳
  if (share_page (tmp))
    return;
// ȡҳ棬ڴ治ˣʾڴ治ֹ̡
  if (!(page = get_free_page ()))
    oom ();
/* remember that 1 block is used for header */
/* סͷҪʹ1 ݿ */
// ȼȱҳڵݿBLOCK_SIZE = 1024 ֽڣһҳڴҪ4 ݿ顣
  block = 1 + tmp / BLOCK_SIZE;
// i ڵϢȡݿ豸ϵĶӦ߼š
  for (i = 0; i < 4; block++, i++)
    nr[i] = bmap (current->executable, block);
// 豸һҳݣ4 ߼飩ַָpage 
  bread_page (page, current->executable->i_dev, nr);
// һҳڴ󣬸ҳڴĲֿܻᳬ̵end_data λáѭǶ
// ҳ泬Ĳֽ㴦
  i = tmp + 4096 - current->end_data;
  tmp = page + 4096;
  while (i-- > 0)
    {
      tmp--;
      *(char *) tmp = 0;
    }
// ҳӳ䵽ָԵַĲɹͷءͷڴҳʾڴ治
  if (put_page (page, address))
    return;
  free_page (page);
  oom ();
}

//// ڴʼ
// start_mem - ҳڴʼλãȥRAMDISK ռڴռȣ
// end_mem - ʵڴַ
// ڸðlinux ںУʹ16Mb ڴ棬16Mb ڴ潫ڿǣòá
// 0 - 1Mb ڴռںϵͳʵ0-640Kb
void
mem_init (long start_mem, long end_mem)
{
  int i;

  HIGH_MEMORY = end_mem;	// ڴ߶ˡ
  for (i = 0; i < PAGING_PAGES; i++)	// ҳΪռ(USED=100)״̬
    mem_map[i] = USED;		// ҳӳȫóUSED
  i = MAP_NR (start_mem);	// Ȼʹʼڴҳš
  end_mem -= start_mem;		// ټɷҳڴС
  end_mem >>= 12;		// Ӷڷҳҳ
  while (end_mem-- > 0)		// ЩҳӦҳӳ㡣
    mem_map[i++] = 0;
}

// ڴҳʾ
void
calc_mem (void)
{
  int i, j, k, free = 0;
  long *pg_tbl;

// ɨڴҳӳmem_map[]ȡҳʾ
  for (i = 0; i < PAGING_PAGES; i++)
    if (!mem_map[i])
      free++;
  printk ("%d pages free (of %d)\n\r", free, PAGING_PAGES);
// ɨҳĿ¼01 ҳĿ¼ЧͳƶӦҳЧҳʾ
  for (i = 2; i < 1024; i++)
    {
      if (1 & pg_dir[i])
	{
	  pg_tbl = (long *) (0xfffff000 & pg_dir[i]);
	  10.5 page.s  for (j = k = 0; j < 1024; j++)
	    if (pg_tbl[j] & 1)
	      k++;
	  printk ("Pg-dir[%d] uses %d pages\n", i, k);
	}
    }
}
