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

📄 memory.c

📁 带中文注释的 linux 0.11 源代码0.11,很好的
💻 C
📖 第 1 页 / 共 3 页
字号:
* 这些页面可以与内核共享。因此这是nr=xxxx 的特殊情况(nr 在程序中指页面数)。*///// 复制指定线性地址和长度(页表个数)内存对应的页目录项和页表,从而被复制的页目录和//// 页表对应的原物理内存区被共享使用。// 复制指定地址和长度的内存对应的页目录项和页表项。需申请页面来存放新页表,原内存区被共享;// 此后两个进程将共享内存区,直到有一个进程执行写操作时,才分配新的内存页(写时复制机制)。intcopy_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 位是0,则R/W 就没有作用。如果U/S 是1,而R/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 longput_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    {// 否则,申请空闲页面给页表使用,并在对应目录项中置相应标志7(User, 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。]voidun_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 是页面线性地址。// 写共享页面时,需复制页面(写时复制)。voiddo_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 行被调用。voidwrite_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(),将物理页面映射到指定的线性地址// 处。voidget_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 */

⌨️ 快捷键说明

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