Linux内核设计的艺术-前三章总结

特权级变化的本质是,cs,ds,es,fs,gs,ss的不同,特权级0从GDT中取得描述符,前面这些寄存器后3位为000,描述符特权级为00,特权级3从LDT中取得描述符,前面这些寄存器后3位为111,描述符特权级为11。中断int 0x80从特权级3进入特权级0,并把信息保存在特权级0的堆栈中,iret从特权级0返回特权级3。

进程切换的本质是在进程内核把当前寄存器的值(内核态数据)放入当前进程的TSS,把另一个进程的TSS数据恢复到寄存器,开始执行另一个进程的内核态(已经被切换过)或者用户态(初始化)。

中断的本质是把当前cs,ds,es,fs,gs,ss压入堆栈中(每个特权级有不同的堆栈),切换到内核态执行代码,执行完毕后返回到对应的状态,参考特权级变化。int 0x80中断和硬盘中断区别是int 0x80是用户态的中断(DPL=11),压入堆栈是用户态的寄存器值;硬盘中断(DPL=00),随机的,压入堆栈的有可能是用户态的寄存器值,也可能是内核态的寄存器值。

刚开始内核态是纯粹的内核态,后来有了进程,都说成是进程几的内核态。

get_free_page:目前用于进程内核tast_struct,还有页表,还有进程执行代码(例如shell进程)。

1、通过bootsect.s,setup.s,head.s把软盘共1+4+240=245个扇区移入主内存区,最后形成如下图:


       页目录表和4个页表总共管理16MB的内存,中断描述符表:目前全是0(共256个中断描述符),全局描述符表:基地址为0,段界限为16MB,DPL=00,DT=1,第一个描述符为0,第二个描述符为代码段描述符,第三个描述符为数据段描述符,其余全0。

    目前寄存器的状态如下图及说明:


    

                         图1 

     cs中0x08,其余为0x10,cs高速缓冲寄存器中存放的是第二个描述符,代码段基地址和段界限,其余存放的是第三个描述符基地址和段界限

     GDTR中存放的是GDT的基地址和段界限,IDTR中存放的是LDT的基地址和段界限

     所以现在处于内核态


2、开始在内核态执行main函数,设置如下图的分区,初始化一些结构体





     设置了中断描述符(位于0x54b8~0x5cb8),并开启中断,具体内容如下:

     selector为0x0008,Offset为中断函数的偏移。set_trap_gate,P为1,DPL为00,TYPE为F(陷阱门)。set_intr_gate,P为1,DPL为00,TYPE为E(中断门)。set_system_gate,P为1,DPL为11,TYPE为F(陷阱门)。只有int 0x80是DPL为11,用户态,其余存是内核态。还要注意此时的selector。


     设置了TSS,LDT

    

     进程0的TSS内容如下:

     esp0为内核栈,ss0为0x10,cr3指向页目录表,其余的变量没有什么用,就不介绍了。

     进程0的LDT内容如下:

     基地址为0,段界限为640KB,DPL=11,DT=1,第一个描述符为0,第二个描述符为代码段描述符,第三个描述符为数据段描述符,其余全0。

     

     TSS0:基地址为进程0的TSS的首地址,界限为104个字节,DPL=00,DT=0,TYPE=9,可用386TSS。

     LDT0:基地址为进程0的LDT的首地址,界限为104个字节,DPL=00,DT=0,TYPE=2,LDT。


     跳转到0进程的3特权级,只有跳转到3特权级才能说是进程。

 

   

     cs为0xf(1111),ss为0x17(10111)其余都是。从局部描述符取,特权级为用户态

     LDTR选择器为4<<3=0x20,TI=0 RPL=00 

     TR选择器为5<<3=0x28,TI=0 RPL=00

     LDTR高速缓存存放的是LDT0的基地址和界限

     TR高速缓存存放的是TSS0的基地址和界限

     所以此时处于进程0的3特权级


3、fork中int 0x80中断

      所以由进程0的3特权级,变成进程0的0特权级。因为int 0x80中断,中断描述符中的Selector会把cs置成0x8,ds,es设置为0x10,fs设置为0x17。

      设置进程1的task_struct,中断发生前的所有寄存器的值都保存在tss中了,ldt的基地址变为64MB,界限为640KB,所以要重新设置的页目录表和页表,又设置了TSS1,LDT1

      完毕后,pop指令,让fs,es,ds恢复原来的值,iret把ss、esp、eflags、cs、eip恢复成原值,从进程0的0特权级又转换到进程0的3特权级

      __res为1,所以fork返回1,那么会执行for(;;) pause();又从进程0的3特权级又转换到进程0的0特权级,在sys_pause中使进程0设置为TASK_INTERRUPTIBLE,开始进程调

度,切换到进程1的3特权级,此时变化的寄存器TR选择器,LDTR选择器(从TSS中获取),还有其他所有的寄存器(从TSS中获取)。目前的寄存器信息保存在进程0的TSS中。


4、开始执行init函数

      执行setup函数,从进程1的3特权级切换到进程1的0特权级,开始读硬盘0x300,0号盘(共1024个字节)的数据,等待读盘的时候把进程1设置了TASK_UNINTERRUPTIBLE

此时调度,已经没有进程处于就绪态了,所以又切换到进程0的0特权级,就是上面sys_pause中的某个位置继续执行,可能执行到上面循环pause( )、sys_pause( )、schedule( )、

switch_to (n) 的任何一句,硬盘已经把一个扇区读入到了硬盘缓冲区,发出中断,把当前的代码段描述符和数据段描述符放入堆栈中(不同的特权级有不同的堆栈),切换到了内

核态,开始执行中断操作,把硬盘缓冲区的数据读到内存中来。然后中断返回(有可能处于进程0的0特权级,也可能处于进程0的1特权级)。两次类似的过程后已经读入到了内

存,然后解锁,唤醒进程1,设置为就绪态,又进行进程调度,由进程0内核态切换到进程1的0特权(当前寄存器的值保存在进程0的TSS中),继续执行bread函数,之后执行rd_load,从软盘256块后读取虚拟盘数据到内存中

的虚拟盘区域,然后执行mount_root(),挂在根文件系统,执行完sys_setup后又从进程1的0特权,变成了进程1的3特权级

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值