复制页目录项和页表的函数是
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 new_page; 8 unsigned long nr; 9 10 if (( from & 0x3fffff ) || (to& 0x3fffff )) 11 panic( " copy_page_tables called with wrong alignment " ); 12 from_dir = (unsigned long *) (( from >> 20 ) & 0xffc ); /* _pg_dir = 0 */ 13 to_dir = (unsigned long *) ((to>> 20 ) & 0xffc ); 14 size = ((unsigned) (size+ 0x3fffff )) >> 22 ; 15 for ( ; size--> 0 ; from_dir++,to_dir++ ) { 16 if ( 1 & * to_dir) 17 panic( " copy_page_tables: already exist " ); 18 if (!( 1 & * from_dir)) 19 continue ; 20 from_page_table = (unsigned long *) ( 0xfffff000 & * from_dir); 21 if (!(to_page_table = (unsigned long * ) get_free_page())) 22 return - 1 ; /* Out of memory, see freeing */ 23 *to_dir = ((unsigned long ) to_page_table) | 7 ; 24 nr = ( from == 0 )? 0xA0 : 1024 ; 25 for ( ; nr-- > 0 ; from_page_table++,to_page_table++ ) { 26 this_page = * from_page_table; 27 if (! this_page) 28 continue ; 29 if (!( 1 & this_page)) { 30 if (!(new_page = get_free_page())) 31 return - 1 ; 32 read_swap_page(this_page>> 1 , ( char * ) new_page); 33 *to_page_table = this_page; 34 *from_page_table = new_page | (PAGE_DIRTY | 7 ); 35 continue ; 36 } 37 this_page &= ~ 2 ; 38 *to_page_table = this_page; 39 if (this_page > LOW_MEM) { 40 *from_page_table = this_page; 41 this_page -= LOW_MEM; 42 this_page >>= 12 ; 43 mem_map[this_page]++ ; 44 } 45 } 46 } 47 invalidate(); 48 return 0 ; 49 }
调用这个函数是在
1 int copy_mem( int nr, struct task_struct * p) 2 { 3 unsigned long old_data_base,new_data_base,data_limit; 4 unsigned long old_code_base,new_code_base,code_limit; 5 6 code_limit=get_limit( 0x0f ); 7 data_limit=get_limit( 0x17 ); 8 old_code_base = get_base(current->ldt[ 1 ]); 9 old_data_base = get_base(current->ldt[ 2 ]); 10 if (old_data_base != old_code_base) 11 panic( " We don't support separate I&D " ); 12 if (data_limit < code_limit) 13 panic( " Bad data_limit " ); 14 new_data_base = new_code_base = nr * TASK_SIZE; 15 p->start_code = new_code_base; 16 set_base(p->ldt[ 1 ],new_code_base); 17 set_base(p->ldt[ 2 ],new_data_base); 18 if (copy_page_tables(old_data_base,new_data_base,data_limit)) { 19 free_page_tables(new_data_base,data_limit); 20 return - ENOMEM; 21 } 22 return 0 ; 23 }
其中 old_data_base 是父进程ldt的基地址, new_data_base 是子进程ldt的基地址, data_limit 是ldt段的长度
copy_page_tables 的作用是复制由 old_data_base和 data_limit所决定内存空间所占用的页目录项和页表(个数由 data_limit决定 ),其实现是由线性地址找到页目录项,再找到页表,复制父进程页表的每一项到子进程的页表中(申请的)
为什么说是页目录项,因为页目录只有一个,各个进程共用统一个页目录,但是每个进程的页目录项是不同的,详见fork.c
1 int copy_process( int nr, long ebp, long edi, long esi, long gs, long none, 2 long ebx, long ecx, long edx, long orig_eax, 3 long fs, long es, long ds, 4 long eip, long cs, long eflags, long esp, long ss) 5 { 6 struct task_struct * p; 7 int i; 8 struct file * f; 9 10 p = ( struct task_struct * ) get_free_page(); 11 if (! p) 12 return - EAGAIN; 13 task[nr] = p; 14 *p = *current; /* NOTE! this doesn't copy the supervisor stack */ 15 p->state = TASK_UNINTERRUPTIBLE; 16 p->pid = last_pid; 17 p->counter = p-> priority; 18 p->signal = 0 ; 19 p->alarm = 0 ; 20 p->leader = 0 ; /* process leadership doesn't inherit */ 21 p->utime = p->stime = 0 ; 22 p->cutime = p->cstime = 0 ; 23 p->start_time = jiffies; 24 p->tss.back_link = 0 ; 25 p->tss.esp0 = PAGE_SIZE + ( long ) p; 26 p->tss.ss0 = 0x10 ; 27 p->tss.eip = eip; 28 p->tss.eflags = eflags; 29 p->tss.eax = 0 ; 30 p->tss.ecx = ecx; 31 p->tss.edx = edx; 32 p->tss.ebx = ebx; 33 p->tss.esp = esp; 34 p->tss.ebp = ebp; 35 p->tss.esi = esi; 36 p->tss.edi = edi; 37 p->tss.es = es & 0xffff ; 38 p->tss.cs = cs & 0xffff ; 39 p->tss.ss = ss & 0xffff ; 40 p->tss.ds = ds & 0xffff ; 41 p->tss.fs = fs & 0xffff ; 42 p->tss.gs = gs & 0xffff ; 43 p->tss.ldt = _LDT(nr); 44 p->tss.trace_bitmap = 0x80000000 ; 45 if (last_task_used_math == current) 46 __asm__( " clts ; fnsave %0 ; frstor %0 " :: " m " (p-> tss.i387)); 47 if (copy_mem(nr,p)) { 48 task[nr] = NULL; 49 free_page(( long ) p); 50 return - EAGAIN; 51 } 52 for (i= 0 ; i<NR_OPEN;i++ ) 53 if (f=p-> filp[i]) 54 f->f_count++ ; 55 if (current-> pwd) 56 current->pwd->i_count++ ; 57 if (current-> root) 58 current->root->i_count++ ; 59 if (current-> executable) 60 current->executable->i_count++ ; 61 if (current-> library) 62 current->library->i_count++ ; 63 set_tss_desc(gdt+(nr<< 1 )+FIRST_TSS_ENTRY,&(p-> tss)); 64 set_ldt_desc(gdt+(nr<< 1 )+FIRST_LDT_ENTRY,&(p-> ldt)); 65 p->p_pptr = current; 66 p->p_cptr = 0 ; 67 p->p_ysptr = 0 ; 68 p->p_osptr = current-> p_cptr; 69 if (p-> p_osptr) 70 p->p_osptr->p_ysptr = p; 71 current->p_cptr = p; 72 p->state = TASK_RUNNING; /* do this last, just in case */ 73 return last_pid; 74 }
其中复制父进程TSS中的PDBR(CR3)
另外注意哪些地址是物理地址,哪些地址是线性地址。
页目录项中的地址和页表项中的地址都是物理地址