任务0到任务1的切换过程和任务1的运行--基于linux0.11内核

任务0的PCB块是由INIT_TASK结构初始化的,初始化在sched_init()函数中进行,该结构中便已定义了一个ldt代码段描述符和一个ldt数据段描述符 任务1的PCB块是复制自任务0的,然后再修改一部分的值。。其中ldt结构代码段和数据段的基地址在copy_mem()函数中修改,改为nr*0x4000000. 为了TSS结构能够寻得该任务的ldt段。ldt段的全局描述符选择符必须放在tss.ldt中,而任务1的段限长和任务0一样都是640k并不修改,

copy_page_tables函数是一个很复杂的函数,他以nr*0x4000000地址为开头建立了若干个页面,任务0的段限长有多大,即建立多个页面, 但以4MB,即一个页表所指示的大小为最小单位,比如任务0的段长只为640k,但是都要根据nr*0x4000000在一个相应位置建立一个页目录项, 一个页目录项有1024个页面,但由于任务0在内核空间,所以只复制前160个页面,160*4k=640k,任务0所使用的虚拟地址空间是和物理地址空间一一对应的,因为内核一开始就把16M内存空间一一映射到物理空间,  大家在这里要留意一个细节,任务一的task结构中的eip值是和任务0的eip值一样的,但是eip值其实在寻址过程中只是一个偏移值,当任务0切换到 任务1时,cpu在任务一中获取tss,而这个tss结构中登记有任务一的ldt描述符,大家应该记得早在copy_mem中ldt的基地址就已经修改为nr*0x4000000,任务一即为0x4000000,把该值放入到cs中,以后便从cs:eip中执行,切换前是执行到0:eip,切换后即执行0x4000000:eip

然后经过copy_page_tables函数的作用0x4000000和任务0的头640k,都指向了同一个物理空间,(此时0x4000000这个地址就可以叫做用户空间地址,因为它就是用户的,不是和内核空间的物理地址一一对应的,---括号内的这句话是有感而发,未经验证)

但是copy_page_tables将指向这个物理空间的两个虚拟地址都设置为只读,即是只共享读,当要写的时候便会产生一个页面出错异常中断,此时cpu便会执行系统提供的异常处理函数do_wp_page()来试图解决这个异常

do_wp_page()会对这块导致写入异常中断的物理页面进行取消共享操作(使用un_wp_page()函数),为写进程复制一新的物理页面,使父进程A和子进程B 各自拥有一块内容相同的物理页面。这时才真正地进行了复制操作(只复制这一块物理页面)。并且把将要执行写入操作的这块物理页面标记成可以写访问的。最后,从异常处理函数中返回时,cpu就会重新执行刚才导致异常的写入操作指令,使进程能够继续执行下去

现在再说到任务1,任务1调用init()函数中运行,init()函数fork出一个子进程,然后子进程调用execve("/bin/sh",argv_rc,envp_rc)载入sh程序 并运行,execve是系统调用,软中断会调用do_execve() 函数去处理这个中断,如果执行程序是一般执行文件(只讨论这个),便会通过copy_strings(argc,argv,page,p,0)和copy_strings(argc,argv,page,p,0),把环境变量和参数复制进一个内存空间中,该内存空间被分配为128k,在这次调用中 环境变量参数是:static char * argv_rc[] = { "/bin/sh", NULL };// 调用执行程序时参数的字符串数组。static char * envp_rc[] = { "HOME=/", NULL };// 调用执行程序时的环境字符串数组。

copy_strings函数是一个挺复杂的函数,具体功能其实就是把argv_rc和 envp_rc这两个指针数组的内容复制到一个页面指针数组所指向的页面中 该页面指针数组共有32项,而每个页面有4k所以总共有128k;

接着调用change_ldt函数,该函数如名字所示修改局部描述符表中的描述符基址和段限长,并将参数和环境空间页面放置在数据段末端,他根据执行文件头部的a_text代码长度来设置局部代码段的段限长,重新设置数据段长度为64mb,并通过调用put_page把page指针指向的物理页面与数据段末端关联起来,也即是说当put_page函数返回后 64m长度范围内的数据段的末端的线性地址指向了page指针指向的物理地址 接下来do_execve函数继续调用create_tables()来在新用户堆栈中创建环境和参数变量指针表,并返回该堆栈指针 :p = (unsigned long) create_tables((char *)p,argc,envc);

函数do_execve()最后会把eip指针指向ex.a_entry,堆栈指针指向p 当软中断结束返回时cpu便会从a_entry指向的地址开始执行。 但一开始执行肯定会引起缺页异常中断发生。因为代码和数据还未被从 块设备上读入内存。此时缺页异常处理过程会根据引起异常的线性地址在 主内存区为新程序申请内存页面(内存帧)

 

posted @ 2012-12-17 21:30  一维  阅读(776)  评论(1编辑  收藏  举报