copy_mem、copy_page_tables函数

在这里插入图片描述
copy_mem函数在这里插入图片描述

在这里插入图片描述
创建中新进程数据段与代码段在线性地址空间中的基地址等于 64MB * 其任务号

被copy_mem调用的copy_page_tables函数

  复制指定线性地址和长度(页表个数)内存对应的页目录项和页表,从而被复制的页目录和 
 页表对应的原物理内存区被共享使用。 
 // 复制指定地址和长度的内存对应的页目录项和页表项。需申请页面来存放新页表,原内存区被共享; 
 // 此后两个进程将共享内存区,直到有一个进程执行写操作时,才分配新的内存页(写时复制机制)。 
150 int copy_page_tables(unsigned long from,unsigned long to,long size) 
151 { 
152 unsigned long * from_page_table; 
153 unsigned long * to_page_table; 
154 unsigned long this_page; 
155 unsigned long * from_dir, * to_dir; 
156 unsigned long nr; 
157 
 // 源地址和目的地址都需要是在 4Mb 的内存边界地址上(页表对齐)。否则出错,死机。 
158 if ((from&0x3fffff) || (to&0x3fffff)) 
159 panic("copy_page_tables called with wrong alignment"); 
 // 取得源地址和目的地址的目录项指针(from_dir 和 to_dir)。
160 from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
161 to_dir = (unsigned long *) ((to>>20) & 0xffc); 
 // 计算要复制的内存块占用的页表数(也即目录项数)。 
162 size = ((unsigned) (size+0x3fffff)) >> 22; 
 // 下面开始对每个占用的页表依次进行复制操作。 
163 for( ; size-->0 ; from_dir++,to_dir++) { 
 // 如果目的目录项指定的页表已经存在(P=1),则出错,死机。 
164 if (1 & *to_dir) 
165 panic("copy_page_tables: already exist"); 
 // 如果此源目录项不存在(P=0),则不用复制对应页表,跳过。 
166 if (!(1 & *from_dir)) 
167 continue; 
 // 取当前源目录项中页表的地址Îfrom_page_table。 
168 from_page_table = (unsigned long *) (0xfffff000 & *from_dir); 
 // 为目的页表取一页空闲内存,如果返回是 0 则说明没有申请到空闲内存页面。返回值=-1,退出。 
169 if (!(to_page_table = (unsigned long *) get_free_page())) 
170 return -1; /* Out of memory, see freeing */
 // 设置目的目录项信息。7 是标志信息,表示(Usr, R/W, Present)。 
171 *to_dir = ((unsigned long) to_page_table) | 7;
 // 针对当前处理的页表,设置需复制的页面数。如果是在内核空间,则仅需复制头 160 页(640KB), 
 // 否则需要复制一个页表中的所有 1024 页面。 
172 nr = (from==0)?0xA0:1024; 
 // 对于当前页表,开始复制指定数目 nr 个内存页面。 
173 for ( ; nr-- > 0 ; from_page_table++,to_page_table++) { 
174 this_page = *from_page_table; // 取源页表项内容。 
175 if (!(1 & this_page)) // 如果当前源页面没有使用,则不用复制。 
176 continue; 
 // 复位页表项中 R/W 标志(置 0)。(如果 U/S 位是 0,则 R/W 就没有作用。如果 U/S 是 1,而 R/W 是 0, 
 // 那么运行在用户层的代码就只能读页面。如果 U/S 和 R/W 都置位,则就有写的权限。) 
177 this_page &= ~2; 
178 *to_page_table = this_page; // 将该页表项复制到目的页表中。 
 // 如果该页表项所指页面的地址在 1MB 以上,则需要设置内存页面映射数组 mem_map[],于是
 //计算页面号,并以它为索引在页面映射数组相应项中增加引用次数。而对于位于 1MB 以下的页面,说明 
 // 是内核页面,因此不需要对 mem_map[]进行设置。因为 mem_map[]仅用于管理主内存区中的页面使用 
 // 情况。因此,对于内核移动到任务 0 中并且调用 fork()创建任务 1 时(用于运行 init()),由于此时 
 // 复制的页面还仍然都在内核代码区域,因此以下判断中的语句不会执行。只有当调用 fork()的父进程 
 // 代码处于主内存区(页面位置大于 1MB)时才会执行。这种情况需要在进程调用了 execve(),装载并 
 // 执行了新程序代码时才会出现。 
179 if (this_page > LOW_MEM) { 
 // 下面这句的含义是令源页表项所指内存页也为只读。因为现在开始有两个进程共用内存区了。 
 // 若其中一个内存需要进行写操作,则可以通过页异常的写保护处理,为执行写操作的进程分配 
 // 一页新的空闲页面,也即进行写时复制的操作。 
180 *from_page_table = this_page; // 令源页表项也只读。 
181 this_page -= LOW_MEM; 
182 this_page >>= 12; 
183 mem_map[this_page]++;
184 } 
185 } 
186 } 
187 invalidate(); // 重置CR3为0,刷新页变换高速缓冲。 
188 return 0; 
189 }

对于内核移动到进程0 中并且调用 fork()创建任务 1 时(用于运行 init()),由于此时被复制的页面还仍然都在内核代码区域,因此以下判断中的语句不会执行。只有当调用 fork()的父进程 代码处于主内存区(页面位置大于 1MB)时才会执行。这种情况需要在进程调用了 execve(),装载并执行了新程序代码时才会出现。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值