实现多任务的内核Linux0.00分析

来源:互联网 发布:nero软件百度云. 编辑:程序博客网 时间:2024/05/22 07:48

最近终于把实现多任务的微内核调试了一遍,我们阐述了如何在保护模式下切换任务。同时知识包括:gdt,idt,ldt,tss,时钟中断服务,特权级切换,显存编程,boot和loader功能,bios调用等等。详细知识还要在实践中摸索学习,希望大家一起进步。这篇文章仅仅做个记录,如没有亲身调试过代码,可能不大好理解。接下几天重点看看0.12启动程序,多分页需要更加深入了解。

;#Mode=Dos;放在.code前面.386p.model smallLATCH=11930SCRN_SEL=18HTSS0_SEL= 20HLDT0_SEL=28HTSS1_SEL=30HLDT1_SEL=38H.codestart:mov eax,10hmov ds,axlss esp,FWORD ptr [init_stack]call setup_idtcall setup_gdtmov eax,10hmov ds,axmov es,axmov fs,axmov gs,axlss esp,FWORD ptr [init_stack]mov al,36hmov edx,43hout dx,almov eax,LATCH mov edx,40hout dx,almov al,ahout dx,almov eax,80000hmov ecx,timer_interruptmov ax,cxmov dx,8e00hmov ecx,8lea esi,[ecx * 8 + idt]mov [esi],eaxmov [esi + 4],edxmov ecx,system_interruptmov ax,cxmov dx,0ef00hmov ecx,80hlea esi,[ecx * 8 + idt]mov [esi],eaxmov [esi + 4],edx;//增加的代码mov ecx,div_intmov ax,cxmov dx,0ef00hmov ecx,0lea esi,[ecx * 8 + idt]mov [esi],eaxmov [esi + 4],edxpushfdand DWORD ptr [esp],0ffffbfffhpopfdmov eax,TSS0_SELltr axmov eax,LDT0_SELlldt ax;mov DWORD ptr [current],0;问题1:前缀2edb 0c7h, 05hdd currentdd 0sti;///增加代码;///push 17hpush init_stackpushfdpush 0fhpush task0iretdsetup_gdt:lgdt FWORD ptr [lgdt_opcode]retsetup_idt:mov edx, ignore_initmov eax,80000hmov ax,dxmov dx,8e00hmov edi, idtmov ecx,256rp_idt:mov [edi],eaxmov [edi + 4],edxadd edi,8dec ecxjne rp_idtlidt FWORD ptr [lidt_opcode]retwrite_char:push gspush ebxmov ebx,SCRN_SELmov gs,bxmov ebx,DWORD ptr [scr_loc]shl ebx,1mov BYTE ptr gs:[ebx],alshr ebx,1inc ebxcmp ebx,2000jb @fmov ebx,0@@:;mov DWORD ptr [scr_loc],ebxdb 89h, 1dhdd scr_locpop ebxpop gsret;align 4ignore_init:push dspush eaxmov eax,10hmov ds,axmov eax,67call write_charpop eaxpop dsiretd;//增加代码div_int:  iret;align 4timer_interrupt:push dspush eaxmov eax,10hmov ds,axmov al,20hout 20h,almov eax,1cmp DWORD ptr [current],eaxje n1;mov DWORD ptr [current],eaxdb 0a3hdd currentBYTE 0eahWORD 0, 0, TSS1_SELjmp n2n1:;mov DWORD ptr [current],0db 0c7h, 05hdd currentdd 0db 0eahWORD 0,0, TSS0_SELn2:pop eaxpop dsiretd;align 4system_interrupt:push dspush edxpush ecxpush ebxpush eaxmov edx,10hmov ds,dxcall write_charpop eaxpop ebxpop ecxpop edxpop dsiretdcurrent: DWORD  0scr_loc: DWORD  0;align 4lidt_opcode:WORD256*8-1DWORDidtlgdt_opcode:WORD(end_gdt-gdt)-1DWORDgdt;align 4idt:QWORD 256 dup (0)gdt:QWORD 0000000000000000hQWORD 00c09a00000007ffhQWORD 00c09200000007ffhQWORD 00c0920b80000002hWORD 68h, tss0, 0e900h, 0WORD 40h, ldt0, 0e200h, 0WORD 68h, tss1, 0e900h, 0WORD 40h, ldt1, 0e200h, 0end_gdt:DWORD 128 dup (0)init_stack:DWORD  init_stackWORD   10h;align 4ldt0:QWORD 0000000000000000hQWORD 00c0fa00000003ffhQWORD 00c0f200000003ffhtss0:DWORD 0DWORD krn_stk0, 10hDWORD 0, 0, 0, 0, 0DWORD 0, 0, 0, 0, 0DWORD 0, 0, 0, 0, 0DWORD 0, 0, 0, 0, 0, 0DWORD LDT0_SEL, 8000000hDWORD 128 dup (0)krn_stk0:ldt1:QWORD 0000000000000000hQWORD 00c0fa00000003ffhQWORD 00c0f200000003ffhtss1:DWORD 0DWORD krn_stk1, 10hDWORD 0, 0, 0, 0, 0DWORD task1, 200hDWORD 0, 0, 0, 0DWORD usr_stk1, 0, 0, 0DWORD 17h, 0fh, 17h, 17h, 17h, 17hDWORD LDT1_SEL, 8000000hDWORD 128 dup (0)krn_stk1:task0:mov eax,17hmov ds,axmov al,65int 80hmov ecx,0fffht1:loop t1jmp task0task1:mov eax,17hmov ds,axmov al,66int 80hmov ecx,0fffht2:loop t2jmp task1DWORD 128 dup (0)usr_stk1:end start

要感谢一个热心网友提供的内核镜像,学到不少东西。下面是我调试的记录:

代码分析:
57:pushl $0x17
pushl $init_stack
pushfl
pushl $0x0f
pushl $task0
iret



57:入栈任务0的ss,(更改了特权级为3)
入栈任务0的堆栈指针esp
入栈标志寄存器
入栈任务0的代码段选择符(cs)
入栈如任务的指令指针(eip)
iret退栈,分别赋值了ss,esp,eflags,cs,eip。
效果:
1.CPU特权级由0->3,因为cs寄存器的内容由0x10(RPL位为0)转为0x0f(RPL为3)
2.跳转到任务0开始执行。cs得到LDT中代码段描述符基址:0x00,偏移量eip=task0,这样就跳转到任务0处开始执行
总结:任务0开始在特权级3上面开始执行。
iret指令之前各个寄存器的值:
r:
ebx: 0x00000000 0
esp: 0x00000bf1 3057
ebp: 0x00000000 0
esi: 0x000001c5 453
edi: 0x000009c5 2501
eip: 0x000000d8
sreg:
es:0x0010, dh=0x00c09300, dl=0x000007ff, valid=1
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
cs:0x0008, dh=0x00c09b00, dl=0x000007ff, valid=3
Code segment, base=0x00000000, limit=0x007fffff, Execute/Read, Accessed, 32-bit
ss:0x0010, dh=0x00c09300, dl=0x000007ff, valid=7
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
ds:0x0010, dh=0x00c09300, dl=0x000007ff, valid=7
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
fs:0x0010, dh=0x00c09300, dl=0x000007ff, valid=1
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
gs:0x0010, dh=0x00c09300, dl=0x000007ff, valid=1
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
ldtr:0x0028, dh=0x0000e200, dl=0x0c0b0040, valid=1
tr:0x0020, dh=0x0000eb00, dl=0x0c230068, valid=1
gdtr:base=0x000009c5, limit=0x3f
idtr:base=0x000001c5, limit=0x7ff


执行完iret指令后:
r:
ebx: 0x00000000 0
esp: 0x00000c05 3077
ebp: 0x00000000 0
esi: 0x000001c5 453
edi: 0x000009c5 2501
eip: 0x0000110b


ss:0x17
cs:0x0f


-------------------------------------------------------------------------


224:int $0x80
在bochs中输入:s就会进入到中断程序:system_interrupt
在IDT表中有三种门描述符:中断门、陷阱门、任务门。
他们靠什么区别呢?描述符中有个TYPE字段。中断门:13、陷阱门:14、任务门:5。
int 0x80。执行完指令之后,系统检测到中断,然后根据中断号80,找到相应处理程序。这是一个陷阱门描述符。
根据完全剖析:121分析
(1)处理过程将在高特权级上执行时就会发生堆栈的切换。堆栈切换的过程如下:
处理器从当前任务的TSS段中取得中断处理过程使用的堆栈的段选择符和栈指针(例如tss.ss0、tss.esp0)。然后处理器会把被中断程序的栈选择符和栈指针压入新栈中。


(2)一旦执行int 80,就会执行堆栈切换,切换到任务0的内核堆栈--krn_stk0(esp)、0x10(ss),在哪里找要切换到的内核栈呢?在当前任务的TSS中有内核栈地址。


还有个问题需要考虑,执行流程怎么跳转到系统调用程序?当然是通过陷阱门的们描述符,里面指定了处理程序的地址,然后更改eip就ok了。
(3)进入内核态,后通过iret退栈(原来cs又弹出来),进入用户态
执行完int 0x80,此时堆栈确实切换了,并且把缘任务的东西入栈
 | STACK 0x00000e77 [0x00001117]---栈顶---[EIP]
 | STACK 0x00000e7b [0x0000000f]----------[cs]
 | STACK 0x00000e7f [0x00000246]----------[eflags]
 | STACK 0x00000e83 [0x00000c05]----------[原esp]
 | STACK 0x00000e87 [0x00000017]----------[原ss]
此时寄存器信息:
cs:0x0008, dh=0x00c09b00, dl=0x000007ff, valid=1
Code segment, base=0x00000000, limit=0x007fffff, Execute/Read, Accessed, 32-bit
ss:0x0010, dh=0x00c09300, dl=0x000007ff, valid=1
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
ds:0x0017, dh=0x00c0f300, dl=0x000003ff, valid=1
Data segment, base=0x00000000, limit=0x003fffff, Read/Write, Accessed
fs:0x0000, dh=0x00c09300, dl=0x000007ff, valid=0
gs:0x0000, dh=0x00c09300, dl=0x000007ff, valid=0
ldtr:0x0028, dh=0x0000e200, dl=0x0c0b0040, valid=1
tr:0x0020, dh=0x0000eb00, dl=0x0c230068, valid=1
gdtr:base=0x000009c5, limit=0x3f
idtr:base=0x000001c5, limit=0x7ff


esp: 0x00000e77 3703
eip: 0x00000199


这个时候cs=0x10,说CPU的特权级由3升到0。


异常和中断过程中的保护:CPL必须小于等于门的DPL。这里是满足的,因为任务0的CPL是3,系统调用陷阱门的DPL也是3。


在system_interrupt执行最后一句iret指令之前,寄存器状态和上面一样。
执行完iret之后:
esp: 0x00000c05 3077
ebp: 0x00000000 0
esi: 0x000001c5 453
edi: 0x000009c5 2501
eip: 0x00001117(对应int 0x10后面那条指令)


cs:0x000f, dh=0x00c0fb00, dl=0x000003ff, valid=1
Code segment, base=0x00000000, limit=0x003fffff, Execute/Read, Accessed, 32-bit
ss:0x0017, dh=0x00c0f300, dl=0x000003ff, valid=1
Data segment, base=0x00000000, limit=0x003fffff, Read/Write, Accessed
ds:0x0017, dh=0x00c0f300, dl=0x000003ff, valid=1
Data segment, base=0x00000000, limit=0x003fffff, Read/Write, Accessed
fs:0x0000, dh=0x00c09300, dl=0x000007ff, valid=0
gs:0x0000, dh=0x00001000, dl=0x00000000, valid=0
ldtr:0x0028, dh=0x0000e200, dl=0x0c0b0040, valid=1
tr:0x0020, dh=0x0000eb00, dl=0x0c230068, valid=1
gdtr:base=0x000009c5, limit=0x3f
idtr:base=0x000001c5, limit=0x7ff
iret指令切换到原来的栈中(陷阱门那节书中有说到这点)


调用ljmp $TSS1_SEL,$0之前寄存器信息:
eax: 0x00000001 1
ecx: 0x0000097c 2428
edx: 0x0000ef00 61184
ebx: 0x00000000 0
esp: 0x00000e6f 3695
ebp: 0x00000000 0
esi: 0x000001c5 453
edi: 0x000009c5 2501
eip: 0x0000017c


sreg:
es:0x0000, dh=0x00c09300, dl=0x000007ff, valid=0
cs:0x0008, dh=0x00c09b00, dl=0x000007ff, valid=3
Code segment, base=0x00000000, limit=0x007fffff, Execute/Read, Accessed, 32-bit
ss:0x0010, dh=0x00c09300, dl=0x000007ff, valid=7
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
ds:0x0010, dh=0x00c09300, dl=0x000007ff, valid=7
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
fs:0x0000, dh=0x00c09300, dl=0x000007ff, valid=0
gs:0x0000, dh=0x00001000, dl=0x00000000, valid=0
ldtr:0x0028, dh=0x0000e200, dl=0x0c0b0040, valid=1
tr:0x0020, dh=0x0000eb00, dl=0x0c230068, valid=1
gdtr:base=0x000009c5, limit=0x3f
idtr:base=0x000001c5, limit=0x7ff


执行之后:
切换到任务0或者1,从TSS取得寄存器信息。特权级由0->3。


---------------------------------------
任务0一直在运行,然后收到时钟中断,这个时候会从特权级3跃升到特权级0,并且也会实现切换到当前任务的内核栈。

0 0