copy_page_tables函数
2021/5/18 10:55:45
本文主要是介绍copy_page_tables函数,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1 int copy_page_tables(unsigned long from,unsigned long to,long size) 2 { 3 unsigned long * from_page_table; 4 unsigned long * to_page_table; 5 unsigned long this_page; 6 unsigned long * from_dir, * to_dir; 7 unsigned long nr; 8 9 // 首先检测参数给出的原地址from和目的地址to的有效性。原地址和目的地址都需要 10 // 在4Mb内存边界地址上。否则出错死机,限制这样的要求是因为一个页表的1024项可 11 // 管理4Mb内存。源地址from和目的地址to只有满足这个要求才能保证从一个页表的 12 // 第一项开始复制页表项,并且新页表的最初所有项都是有效的。然后取得源地址和 13 // 目的地址的其实目录项指针(from_dir 和 to_dir).再根据参数给出的长度size计 14 // 算要复制的内存块占用的页表数(即目录项数)。 15 if ((from&0x3fffff) || (to&0x3fffff)) 16 panic("copy_page_tables called with wrong alignment");
// 计算给出的线性基地址对应的真实目录项。对应的目录项号=from>>22.因为每
// 项占4字节,并且由于页目录表从物理地址0开始存放,因此实际目录项指针=目录
// 项号<<2,也即(from>>20)。& 0xffc确保目录项指针范围有效,即用于屏蔽目录项
// 指针最后2位。因为只移动了20位,因此最后2位是页表项索引的内容,应屏蔽掉。
17 from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */ 18 to_dir = (unsigned long *) ((to>>20) & 0xffc); //计算参数size给出的长度所占的页目录项数(4MB的进位整数倍),也即所占页表数 //因为1个页表可管理4MB物理内存,所以这里用右移22位的方式把需要复制的内存长度值 //除以4MB.其中加上0x3fffff(即4MB-1)用于得到进位整数倍结果,即操作若有余数则进1. //例如,如果原size=4.01Mb,那么可得到结果sieze=2 19 size = ((unsigned) (size+0x3fffff)) >> 22; // 在得到了源起始目录项指针from_dir和目的起始目录项指针to_dir以及需要复制的 21 // 页表个数size后,下面开始对每个页目录项依次申请1页内存来保存对应的页表,并 22 // 且开始页表项复制操作。如果目的目录指定的页表已经存在(P=1),则出错死机。 23 // 如果源目录项无效,即指定的页表不存在(P=1),则继续循环处理下一个页目录项。 24 for( ; size-->0 ; from_dir++,to_dir++) { 25 if (1 & *to_dir) 26 panic("copy_page_tables: already exist"); 27 if (!(1 & *from_dir)) 28 continue; 29 // 在验证了当前源目录项和目的目录项正常之后,我们取源目录项中页表地址 30 // from_page_table。为了保存目的目录项对应的页表,需要在主内存区中申请1 31 // 页空闲内存页。如果取空闲页面函数get_free_page()返回0,则说明没有申请 32 // 到空闲内存页面,可能是内存不够。于是返回-1值退出。 33 from_page_table = (unsigned long *) (0xfffff000 & *from_dir); if (!(to_page_table = (unsigned long *) get_free_page())) 35 return -1; /* Out of memory, see freeing */ 36 // 否则我们设置目的目录项信息,把最后3位置位,即当前目录的目录项 | 7, 37 // 表示对应页表映射的内存页面是用户级的,并且可读写、存在(Usr,R/W,Present). 38 // (如果U/S位是0,则R/W就没有作用。如果U/S位是1,而R/W是0,那么运行在用 39 // 户层的代码就只能读页面。如果U/S和R/W都置位,则就有读写的权限)。然后 40 // 针对当前处理的页目录项对应的页表,设置需要复制的页面项数。如果是在内 41 // 核空间,则仅需复制头160页对应的页表项(nr=160),对应于开始640KB物理内存 42 // 否则需要复制一个页表中的所有1024个页表项(nr=1024),可映射4MB物理内存。 43 *to_dir = ((unsigned long) to_page_table) | 7; 44 nr = (from==0)?0xA0:1024; 45 // 此时对于当前页表,开始循环复制指定的nr个内存页面表项。先取出源页表的 46 // 内容,如果当前源页表没有使用,则不用复制该表项,继续处理下一项。否则 47 // 复位表项中R/W标志(位1置0),即让页表对应的内存页面只读。然后将页表项复制 48 // 到目录页表中。 49 for ( ; nr-- > 0 ; from_page_table++,to_page_table++) { 50 this_page = *from_page_table; 51 if (!(1 & this_page)) 52 continue; 53 this_page &= ~2; 54 *to_page_table = this_page; 55 // 如果该页表所指物理页面的地址在1MB以上,则需要设置内存页面映射数 56 // 组mem_map[],于是计算页面号,并以它为索引在页面映射数组相应项中 57 // 增加引用次数。而对于位于1MB以下的页面,说明是内核页面,因此不需 58 // 要对mem_map[]进行设置。因为mem_map[]仅用于管理主内存区中的页面使 59 // 用情况。因此对于内核移动到任务0中并且调用fork()创建任务1时(用于 60 // 运行init()),由于此时复制的页面还仍然都在内核代码区域,因此以下 61 // 判断中的语句不会执行,任务0的页面仍然可以随时读写。只有当调用fork() 62 // 的父进程代码处于主内存区(页面位置大于1MB)时才会执行。这种情况需要 63 // 在进程调用execve(),并装载执行了新程序代码时才会出现。 64 // *from_page_table = this_page; 这句是令源页表项所指内存页也为只读。 65 // 因为现在开始有两个进程公用内存区了。若其中1个进程需要进行写操作, 66 // 则可以通过页异常写保护处理为执行写操作的进程匹配1页新空闲页面,也 67 // 即进行写时复制(copy on write)操作。 68 if (this_page > LOW_MEM) { 69 *from_page_table = this_page; 70 this_page -= LOW_MEM; 71 this_page >>= 12; 72 mem_map[this_page]++; 73 } 74 } 75 } 76 invalidate(); 77 return 0; 78 }
这篇关于copy_page_tables函数的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-27Rocket消息队列学习入门指南
- 2024-11-26Rocket消息中间件教程:新手入门详解
- 2024-11-26RocketMQ项目开发教程:新手入门指南
- 2024-11-26MQ源码教程:轻松入门Apache MQ源码解析
- 2024-11-26Rocket消息队列教程:新手入门必读
- 2024-11-26Rocket消息队列教程:新手入门指南
- 2024-11-26RocketMQ底层原理教程:新手入门指南
- 2024-11-26RocketMQ底层原理教程:入门级详解
- 2024-11-26如何获取 OpenAI API Key 用于ChatGPT AI大模型开发?
- 2024-11-26MATLAB 中 A(7)=[];什么意思?-icode9专业技术文章分享