1.进程0创建进程1时,为进程1建立了自己的task_struct、内核栈,第一个页表,分别位于物理内存16MB的顶端倒数第一页、第二页。请问,这个了页究竟占用的是谁的线性地址空间,内核、进程0、进程1、还是没有占用任何线性地址空间?说明理由并给出代码证据。答:两次都是通过调用get_free_page()在物理内存里申请一个物理页,由于在head.s中决定内核的物理地址和线性地址是一一对应的。因此这两个页都在内核的线性地址空间内。setup_paging:movl$1024*5,%ecx/*5pages-pg_dir+4pagetables*/xorl%eax,%eaxxorl%edi,%edi/*pg_dirisat0x000*/cld;rep;stoslmovl$pg0+7,_pg_dir/*setpresentbit/userr/w*/movl$pg1+7,_pg_dir+4/*------------------*/movl$pg2+7,_pg_dir+8/*------------------*/movl$pg3+7,_pg_dir+12/*------------------*/movl$pg3+4092,%edimovl$0xfff007,%eax/*16Mb-4096+7(r/wuser,p)*/std1:stosl/*fillpagesbackwards-moreefficient:-)*/subl$0x1000,%eaxjge1b2.假设:经过一段时间的运行,操作系统中已经有5个进程在运行,且内核分别为进程4、进程5分别创建了第一个页表,这两个页表在谁的线性地址空间?用图表示这两个页表在线性地址空间和物理地址空间的映射关系。答:大师兄!!3.进程0开始创建进程1,调用了fork(),跟踪代码时我们发现,fork代码执行了两次,第一次,跳过init()直接执行了for(;;)pause(),第二次执行fork代码后,执行了init()。奇怪的是,我们在代码中并没有看见向后的goto语句,也没有看到循环语句,是什么原因导致反复执行?请说明理由,并给出代码证据。答:大师兄!!4.copy_process函数的参数最后五项是:longeip,longcs,longeflags,longesp,longss。查看栈结构确实有这五个参数,奇怪的是其他参数的压栈代码都能找得到,确找不到这五个参数的压栈代码,反汇编代码中也查不到,请解释原因。答:在执行intn这条中断指令时(在此即为int0x80),系统会自动把ssesprflagscseip这五个寄存器的值压入栈中。5.用图表示下面的几种情况,并从代码中找到证据:a、当进程获得第一个缓冲块的时候,hash表的状态。b、经过一段时间的运行,已经2000多个buffer_head挂到hash_table上时,hash表(包括所有的buffer_head)的整体状态。c、经过一段时间的运行,有的缓冲块已经没有进程使用了(空闲),这样的空闲缓冲块是否会从hash_table上脱钩?d、经过一段时间的运行,所有的buffer_head都挂到hash_table上了,这时,又有进程申请空闲缓冲块,将会发生什么?解释原因并找到代码证据。答:见书上P100-P01.6.rd_load()执行完之后,虚拟盘已经成为可用的块设备,并成为根设备。在向虚拟盘中copy任何数据之前,虚拟盘中是否有引导块、超级块、i节点位图、逻辑块位图、i节点、逻辑块?请解释其中的道理,并给出代码证据。答:在rd_load()执行之前,虚拟盘经初始化后中存放的全是0。rd_init()中的代码段:---P46cp=rd_start;for(i=0;ilength;i++)*cp++='\0';且拷贝数据也是从虚拟盘开始处进行存放的。rd_load()中的代码:---P121cp=rd_start;while(nblocks){if(nblocks2)bh=breada(ROOT_DEV,block,block+1,block+2,-1);elsebh=bread(ROOT_DEV,block);…}(void)memcpy(cp,bh-b_data,BLOCK_SIZE);…}7.在虚拟盘被设置为根设备之前,操作系统的根设备是软盘(包括软驱),请说明设置软盘为根设备的技术路线,并给出代码证据。(提示:注意bootsect.s的249行,508这个数值。.org508root_dev:.wordROOT_DEV)答:在bootsect.s程序的开始处有一条语句对ROOT_DEV进行赋值。之后执行如下代码,确定所使用的根设备:segcsmovax,root_devcmpax,#0jneroot_definedsegcsmovbx,sectorsmovax,#0x0208!/dev/ps0-1.2Mbcmpbx,#15jeroot_definedmovax,#0x021c!/dev/PS0-1.44Mbcmpbx,#18jeroot_definedundef_root:jmpundef_rootroot_defined:segcsmovroot_dev,ax#1、首先会判断根设备是否已经设置,如果设置(如ROOT_DEV=0x306)则直接跳过。否则执行判定。#2、通过每磁道的扇区数判别是1.2Mb还是1.44Mb的软盘,判定完成后跳到末尾处。通过代码调试,发现该段代码执行完之后,存在地址0x901fc处的ROOT_DEV的值为0x021c,即为1.44Mb的软盘。#3、在main函数开始时,执行====ROOT_DEV=ORIG_ROOT_DEV;======把存放于0x901fc处的值赋给ROOT_DEV。++++#defineORIG_ROOT_DEV(*(unsignedshort*)0x901FC)8.Linux0.11是怎么将根设备从软盘更换为虚拟盘,并加载了根文件系统?用文字、图示表示,并给出代码证据。答:大师兄!!9.内核的线性地址空间是如何分页的?画出从0x000000开始的7个页(包括页目录表、页表所在页)的挂接关系图,就是页目录表的前四个页目录项、第一个个页表的前7个页表项指向什么位置?给出代码证据。答:Head.s中:setup_paging:movl$1024*5,%ecx/*5pages-pg_dir+4pagetables*/xorl%eax,%eaxxorl%edi,%edi/*pg_dirisat0x000*/cld;rep;stoslmovl$pg0+7,pg_dir/*setpresentbit/userr/w*/movl$pg1+7,pg_dir+4/*------------------*/movl$pg2+7,pg_dir+8/*------------------*/movl$pg3+7,pg_dir+12/*------------------*/_pg_dir用于表示内核分页机制完成后的内核起始位置,也就是物理内存的起始位置0x000000,以上四句完成页目录表的前四项与页表1,2,3,4的挂接movl$pg3+4092,%edimovl$0xfff007,%eax/*16Mb-4096+7(r/wuser,p)*/std1:stosl/*fillpagesbackwards-moreefficient:-)*/subl$0x1000,%eaxjge1b完成页表项与页面的挂接,是从高地址向低地址方向完成挂接的,16M内存全部完成挂接10.用文字和图说明中断描述符表是如何初始化的,可以举例说明(比如:set_trap_gate(0,÷_error)),并给出代码证据。答:对中断描述符表的初始化,就是将异常处理一类的中断服务程序与中断描述符表进行挂接。以set_trap_gate(0,÷_error)为例,0表示该中断函数的地址挂接在中断描述符表的第0项位置处,而&devide_error就是该异常处理函数的地址。对set_trap_gate(0,÷_error)进行宏展开后得到#defineset_trap_gate(0,&devide_error)\_set_gate(&idt[0],15,0,&devide_error)之后执行如下代码:#define_set_gate(&idt[0],15,0,&devide_error)(gate_addr,type,dpl,addr)\__asm__(movw%%dx,%%ax\n\t\movw%0,%%dx\n\t\movl%%eax,%1\n\t\movl%%edx,%2\:\:i((short)(0x8000+(013)+(158))),\o(*((char*)(&idt[0]))),\o(*(4+(char*)(&idt[0]))),\d((char*)(&devide_error)),a(0x00080000))%0=0x8f00,%1指向idt[0]的起始地址,%2指向四个字节之后的地址处。#1、将地址&devide_error放在EAX的低两个字节,EAX的高两字节不变。#2、把0x8f00放入EDX的低两字节,高两字节保持不变。#3、把EAX放在%1所指的地址处,占四字节。#4、将EDX放在%2所指的地址处,占四字节。大师兄!!11.为什么计算机启动最开始的时候执行的是BIOS代码而不是操作系统自身的代码?答:因为在计算机上电启动的时候,内存里没有任何代码,需要借助于BIOS代码进行加载引导扇区以及系统代码,在进行一系列的配置(如加载中断向量表和中断服务程序等)后才能够执行操作系统自身的代码。大师兄和P1.12.为什么BIOS只加载了一个扇区,后续扇区却是由bootsect代码加载?为什么BIOS没有把所有需要加载的扇区都加载?答:这是因为BIOS默认在计算机启动的时候通过BIOS中断只从启动扇区(对Linux-0.11而言指软盘的第一个扇区)加载代码。而后续代码的加载则是通过系统中断完成的。因为如果要使用BIOS进行加载,而且加载完成之后再执行,则需要很长的时间;因此Linux采用的是边执行边加载的方法。P613.为什么BIOS把bootsect加载到0x07c00,而不是0x00000?加载后又马上挪到0x90000处,是何道理?为什么不一次加载到位?答:因为BIOS首先会把中断向量表加载到0x00000-0x003ff的1KB的内存空间,在加载bootsect时约定加载到0x07c00处。挪到0x90000处是操作系统自身的代码完成的,是为加载后续代码做准备;而加载到0x07c00是BIOS约定好了的,操作系统也没办法。P914.bootsect、setup、head程序之间是怎么衔接的?给出代码证据。答:bootsect在完成自身复制之后,将setup加载到自己的后面(0x90200处),将system模块加载到0x10000处。在完成加载之后,通过指令jmpi0,SETUPSEG跳转到setup程序处执行。setup首先关中断将system移动到内存地址起始位置0x0处。完成从16位模式向32位模式转变之后,执行jmpi0,8跳到head程序处执行。P14和P2415.setup程序里的cli是为了什么?答:cli是关中断指令。因为此时需要由16位实模式向32位保护模式转变,在新的中断机制未建好之前不允许响应中断(即使有,在操作系统看来跟没有一样),因此使用cli指令关闭所有的中断(当然不包括NMI)。P1616.setup程序的最后是jmpi0,8为什么这个8不能简单的当作阿拉伯数字8看待?答:因为此时已经工作于32位保护模式下,此时“0”是段内偏移地址;而“8”是段选择符,用以选择段描述符表和描述符表项以及所要求的特权级。P2417.打开A20和打开pe究竟是什么关系,保护模式不就是32位的吗?为什么还要打开A20?有必要吗?答: