关于Linux0.00中的时钟中断代码的一点理解
在读Linux0.00 head.s的代码时,时钟中断这段代码折腾了我半天才弄懂,先贴上代码
1 /* 2 * head.s contains the 32-bit startup code. 3 * Two L3 task multitasking. The code of tasks are in kernel area, 4 * just like the Linux. The kernel code is located at 0x10000. 5 */ 6 KRN_BASE = 0x10000 7 TSS0_SEL = 0x20 8 LDT0_SEL = 0x28 9 TSS1_SEL = 0X30 10 LDT1_SEL = 0x38 11 12 .text 13 startup_32: 14 movl $0x10,%eax 15 mov %ax,%ds 16 mov %ax,%es 17 mov %ax,%fs 18 mov %ax,%gs 19 lss stack_ptr,%esp 20 21 # setup base fields of descriptors. 22 movl $KRN_BASE, %ebx 23 movl $gdt, %ecx 24 lea tss0, %eax 25 movl $TSS0_SEL, %edi 26 call set_base 27 lea ldt0, %eax 28 movl $LDT0_SEL, %edi 29 call set_base 30 lea tss1, %eax 31 movl $TSS1_SEL, %edi 32 call set_base 33 lea ldt1, %eax 34 movl $LDT1_SEL, %edi 35 call set_base 36 37 call setup_idt 38 call setup_gdt 39 movl $0x10,%eax # reload all the segment registers 40 mov %ax,%ds # after changing gdt. 41 mov %ax,%es 42 mov %ax,%fs 43 mov %ax,%gs 44 lss stack_ptr,%esp 45 46 # setup up timer 8253 chip. 47 movb $0x36, %al 48 movl $0x43, %edx 49 outb %al, %dx 50 movl $11930, %eax # timer frequency 100 HZ 51 movl $0x40, %edx 52 outb %al, %dx 53 movb %ah, %al 54 outb %al, %dx 55 56 # setup timer & system call interrupt descriptors. 57 movl $0x00080000, %eax 58 movw $timer_interrupt, %ax 59 movw $0x8E00, %dx 60 movl $0x20, %ecx 61 lea idt(,%ecx,8), %esi 62 movl %eax,(%esi) 63 movl %edx,4(%esi) 64 movw $system_interrupt, %ax 65 movw $0xef00, %dx 66 movl $0x80, %ecx 67 lea idt(,%ecx,8), %esi 68 movl %eax,(%esi) 69 movl %edx,4(%esi) 70 71 # unmask the timer interrupt. 72 movl $0x21, %edx 73 inb %dx, %al 74 andb $0xfe, %al 75 outb %al, %dx 76 77 # Move to user mode (task 0) 78 pushfl 79 andl $0xffffbfff, (%esp) 80 popfl 81 movl $TSS0_SEL, %eax 82 ltr %ax 83 movl $LDT0_SEL, %eax 84 lldt %ax 85 movl $0, current 86 sti 87 pushl $0x17 88 pushl $stack0_ptr 89 pushfl /* 手动设置返回环境*/ 90 pushl $0x0f /**/ 91 pushl $task0 /**/ 92 iret 93 94 /****************************************/ 95 setup_gdt: 96 lgdt lgdt_opcode 97 ret 98 99 setup_idt: 100 lea ignore_int,%edx 101 movl $0x00080000,%eax 102 movw %dx,%ax /* selector = 0x0008 = cs */ 103 movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ 104 lea idt,%edi 105 mov $256,%ecx 106 rp_sidt: 107 movl %eax,(%edi) 108 movl %edx,4(%edi) 109 addl $8,%edi 110 dec %ecx 111 jne rp_sidt 112 lidt lidt_opcode 113 ret 114 115 # in: %eax - logic addr; %ebx = base addr ; 116 # %ecx - table addr; %edi - descriptors offset. 117 set_base: 118 addl %ebx, %eax 119 addl %ecx, %edi 120 movw %ax, 2(%edi) 121 rorl $16, %eax 122 movb %al, 4(%edi) 123 movb %ah, 7(%edi) 124 rorl $16, %eax 125 ret 126 127 write_char: 128 push %gs 129 pushl %ebx 130 pushl %eax 131 mov $0x18, %ebx 132 mov %bx, %gs 133 movl scr_loc, %bx 134 shl $1, %ebx 135 movb %al, %gs:(%ebx) 136 shr $1, %ebx 137 incl %ebx 138 cmpl $2000, %ebx 139 jb 1f 140 movl $0, %ebx 141 1: movl %ebx, scr_loc 142 popl %eax 143 popl %ebx 144 pop %gs 145 ret 146 147 /***********************************************/ 148 /* This is the default interrupt "handler" :-) */ 149 .align 2 150 ignore_int: 151 push %ds 152 pushl %eax 153 movl $0x10, %eax 154 mov %ax, %ds 155 movl $67, %eax /* print 'C' */ 156 call write_char 157 popl %eax 158 pop %ds 159 iret 160 161 /* Timer interrupt handler */ 162 .align 2 163 timer_interrupt: 164 push %ds 165 pushl %edx 166 pushl %ecx 167 pushl %ebx 168 pushl %eax 169 movl $0x10, %eax 170 mov %ax, %ds 171 movb $0x20, %al 172 outb %al, $0x20 173 movl $1, %eax 174 cmpl %eax, current 175 je 1f 176 movl %eax, current 177 ljmp $TSS1_SEL, $0 178 jmp 2f 179 1: movl $0, current 180 ljmp $TSS0_SEL, $0 181 2: popl %eax 182 popl %ebx 183 popl %ecx 184 popl %edx 185 pop %ds 186 iret 187 188 /* system call handler */ 189 .align 2 190 system_interrupt: 191 push %ds 192 pushl %edx 193 pushl %ecx 194 pushl %ebx 195 pushl %eax 196 movl $0x10, %edx 197 mov %dx, %ds 198 call write_char 199 popl %eax 200 popl %ebx 201 popl %ecx 202 popl %edx 203 pop %ds 204 iret 205 206 /*********************************************/ 207 current:.long 0 208 scr_loc:.long 0 209 210 .align 2 211 .word 0 212 lidt_opcode: 213 .word 256*8-1 # idt contains 256 entries 214 .long idt + KRN_BASE # This will be rewrite by code. 215 .align 2 216 .word 0 217 lgdt_opcode: 218 .word (end_gdt-gdt)-1 # so does gdt 219 .long gdt + KRN_BASE # This will be rewrite by code. 220 221 .align 3 222 idt: .fill 256,8,0 # idt is uninitialized 223 224 gdt: .quad 0x0000000000000000 /* NULL descriptor */ 225 .quad 0x00c09a01000007ff /* 8Mb 0x08, base = 0x10000 */ 226 .quad 0x00c09201000007ff /* 8Mb 0x10 */ 227 .quad 0x00c0920b80000002 /* screen 0x18 - for display */ 228 229 .quad 0x0000e90100000068 # TSS0 descr 0x20 230 .quad 0x0000e20100000040 # LDT0 descr 0x28 231 .quad 0x0000e90100000068 # TSS1 descr 0x30 232 .quad 0x0000e20100000040 # LDT1 descr 0x38 233 end_gdt: 234 .fill 128,4,0 235 stack_ptr: 236 .long stack_ptr 237 .word 0x10 238 239 /*************************************/ 240 .align 3 241 ldt0: .quad 0x0000000000000000 242 .quad 0x00c0fa01000003ff # 0x0f, base = 0x10000 243 .quad 0x00c0f201000003ff # 0x17 244 tss0: 245 .long 0 /* back link */ 246 .long stack0_krn_ptr, 0x10 /* esp0, ss0 */ 247 .long 0, 0 /* esp1, ss1 */ 248 .long 0, 0 /* esp2, ss2 */ 249 .long 0 /* cr3 */ 250 .long 0 /* eip */ 251 .long 0 /* eflags */ 252 .long 0, 0, 0, 0 /* eax, ecx, edx, ebx */ 253 .long 0, 0, 0, 0 /* esp, ebp, esi, edi */ 254 .long 0,0,0,0,0,0 /* es, cs, ss, ds, fs, gs */ 255 .long LDT0_SEL /* ldt */ 256 .long 0x8000000 /* trace bitmap */ 257 258 .fill 128,4,0 259 stack0_krn_ptr: 260 .long 0 261 262 /************************************/ 263 .align 3 264 ldt1: .quad 0x0000000000000000 265 .quad 0x00c0fa01000003ff # 0x0f, base = 0x10000 266 .quad 0x00c0f201000003ff # 0x17 267 tss1: 268 .long 0 /* back link */ 269 .long stack1_krn_ptr, 0x10 /* esp0, ss0 */ 270 .long 0, 0 /* esp1, ss1 */ 271 .long 0, 0 /* esp2, ss2 */ 272 .long 0 /* cr3 */ 273 .long task1 /* eip */ 274 .long 0x200 /* eflags */ 275 .long 0, 0, 0, 0 /* eax, ecx, edx, ebx */ 276 .long stack1_ptr, 0, 0, 0 /* esp, ebp, esi, edi */ 277 .long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */ 278 .long LDT1_SEL /* ldt */ 279 .long 0x8000000 /* trace bitmap */ 280 281 .fill 128,4,0 282 stack1_krn_ptr: 283 .long 0 284 285 /************************************/ 286 task0: 287 movl $0x17, %eax 288 movw %ax, %ds 289 movl $65, %al /* print 'A' */ 290 int $0x80 291 movl $0xfff, %ecx 292 1: loop 1b 293 jmp task0 294 295 .fill 128,4,0 296 stack0_ptr: 297 .long 0 298 299 task1: 300 movl $0x17, %eax 301 movw %ax, %ds 302 movl $66, %al /* print 'B' */ 303 int $0x80 304 movl $0xfff, %ecx 305 1: loop 1b 306 jmp task1 307 308 .fill 128,4,0 309 stack1_ptr: 310 .long 0 311 312 /*** end ***/
question:
在代码的第177行(177 ljmp $TSS1_SEL, $0)和第180行(180 ljmp $TSS0_SEL, $0)可以看到,中断程序转移到对应的任务去执行了,后边的代码怎么执行?iret永远也执行不到了么?
answer:
实际上,程序在开始运行时,从第79行(79 andl $0xffffbfff, (%esp))可以看到标志寄存器的中断嵌套位NT被设置成了0,也就是说中断返回时,将会使用堆栈保存的任务状态,而不是任务切换的方式保存。81,82行设置了当前任务状态寄存器为TSS0_SEL,可以看到tss0里面,除了esp0,ss0,ldt,trace bitmap设置了外,其余是0.
嵌套任务标志NT(Nested Task)
嵌套任务标志NT用来控制中断返回指令IRET的执行。具体规定如下:
(1)、当NT=0,用堆栈中保存的值恢复EFLAGS、CS和EIP,执行常规的中断返回操作; (2)、当NT=1,通过任务转换实现中断返回。
随后在手动设置返回环境后,进入task0,在第一次时钟中断产生时,进入时钟中断处理程序,此时task0的状态全都保存到了堆栈,然后经判断转去task1,在这里完成了复杂的工作,这个ljmp意义很大(177,180),
第一次时钟中断
ljmp(177):
cpu首先把当前状态(注意是当前状态,而不是task0的状态)保存到tss0,此时tss0的eip指向下一条语句(178 jmp 2f)。task0的运行状态被压入堆栈,此时的tr为TSS0_SEL
然后cpu转去执行tss1_sel 描叙的内容,可以看到tss1里面存放的是完整的信息,并且eip指向task1。并且重新加载tr为TSS1_SEL
第二次时钟中断,
ljmp(180)
同样cpu首先把当前状态(注意是当前状态,而不是task1的状态)保存到tss1,此时tss1的eip指向下一条语句(181 2: popl %eax),这条语句就是上一次中断要跳转的位置。task1的运行状态被压入堆栈,此时的tr为TSS1_SEL
然后cpu转去执行tss0_sel描叙的内容,注意tss0_sel描述符里面存放的是上次时钟中断的位置,所以会立即执行178 jmp 2f,最后执行iret,还记得上次堆栈里面放的是task0的运行状态,iret会恢复堆栈内容到寄存器(根据NT标志),因此这次中断后会执行task0中断产生时的下一条语句。同事cpu重新加载tr为tss_se0
第三次。。。。过后就如第二样了,就第一次有点差别,第一次切换时,是直接去执行task1,以后的task0,task1,都从堆栈中恢复。
写的有点乱,自己记录一下,总算是明白这个时钟中断处理程序是怎么切换任务的了。
参考:Linux内核完全剖析 基于0.12内核
小弟初入道,高手路过请指教!!!
浙公网安备 33010602011771号