2012年3月16日 星期五

練習寫 os 的苦難過程

這篇用來紀錄開發 simple os 時, 遇到的難題 


timer isr 加上 loop delay (0)


git version: 42f0e3c7c723e3d3fc5e0a3c7a4c19ab41a2a9d1
git branch: int_test
問題: qemu 直接 hang 住; bochs 則發出 invalid opcode exception。

在參考 Orange's 一個作業系統的實現, 將 timer isr 分成兩部份後, 想要測試中斷重入的問題, 所以加上 loop delay, 結果整個 process 切換出了問題, 靠著我的法寶, bochs 內建的 debugger, 還是無法找出問題, 先記錄在這裡。

在 qemu 執行不正常。

in bochs:


Invalid Opcode exception。

將中斷點設定在
411   pushal

總算可以中斷除錯了。

而問題發生在
431   jne 1f # reenter
我忘記加上 f 了, 導致重新進入中斷時, jmp 到絕對位址 1 (8:1)去了。
431   jne 1 # reenter
hwint01 是鍵盤中斷, 我特地改到鍵盤中斷, 方便測試這個 bug。之前就猜測有可能是重新進入中斷時產生這問題。

p_kernel.s
405 .align 16
406 hwint01:
407   #HW_INT_MASTER $0
408
409   sub $4, %esp
410
411   pushal
412   pushl %ds
413   pushl %es
414   pushl %fs
415   pushl %gs
416
417   mov %ss, %dx
418   mov %dx, %ds
419   mov %dx, %es
420
421
422   incb %gs:(0)
423
424   mov $EOI, %al
425   outb %al, $INT_M_CTL
426
427   # check k_reenter
428   incl (k_reenter)
429   cmpl $0, (k_reenter)
430   #jne .re_enter
431   jne 1f # reenter
432
433   mov $STACK_TOP, %esp # switch to kernel stack
434
435   pushl $restart_v2
436   jmp 2f
437
438 1: # reenter
439   pushl $restart_reenter_v2
440
441 2: # non reenter
442   sti
443
444   pushl 0
445   call clock_handler
446   addl $4, %esp
447
448   pushl $100
449   call loop_delay
450   add $4, %esp
451
452   addl $2, (VB)
453   pushl VB
454   pushl $TIMER_STR
455   call s32_print
456   add $8, %esp
457
458   #jmp .
459   #nop
460   #nop
461
462
463   cli
464   retl
465
466 restart_v2:
467   movl (ready_process), %esp
468   lldt LDT_SEL_OFFSET(%esp)
469
470   lea P_STACKTOP(%esp), %eax
471   movl %eax, (tss+TSS3_S_SP0)
472
473 #.re_enter:
474 restart_reenter_v2:
475   decl (k_reenter)
476
477   popl %gs
478   popl %fs
479   popl %es
480   popl %ds
481   popal
482
483   add $4, %esp
484
485   iretl
486
487 .align 16
488 hwint00:
489   HW_INT_MASTER $1
490 .align 16
491 hwint02:
492   HW_INT_MASTER $2



這個 bug 修好之後又產生新問題。本來在 clock_handler 會一直印 @, 但最後會印 k, 連 process a, b, c 都無法切換, 真是奇怪, 更奇怪的是, 就當機了 ... 在 reenter 的部份看來還是有問題。



git branch: int_t0
找到問題了, 由於 delay 太長, 導致總是發生 reenter timer isr, 最後 kernel stack 爆掉, 所以會有奇怪的行為, 我加大 kernel stack 這問題變會延後發生。

以下的畫面下方的數字便是 reenter 的參考數字, 只有 0 時, 才能切換 process, 可以看到一開始還可以切換 process, 可是到最後數字變得越來越大, 已經無法再執行 proc A。




timer isr 加上 loop delay (1)


git version: 78dbca4590d33fccb2dbb581e4984cecb5d0732d

又是 timer isr 問題, 在改成類似 minix 的中斷處理架構後, 一樣要測試中斷重入的問題, 所以加上 loop delay, 結果整個 process 切換出了問題, 只有執行 timer isr, 無法切換 process。

loop delay 的參數為 100 才會有這樣的問題, 10 的話 process 可以正常切換。透過 bochs 內建的 debugger 之後, 發現執行到 process A 時, 會馬上跳回 timer isr code, 真是奇怪, 造成 process A 的程式碼都不會執行。

這問題看來是這樣:
若是 timer 1 秒發一次中斷, 而 timer isr 需要 2 秒才能做完, 便會有這樣的情形。

process 切換加上 system call get_ticks()

git version: e0e80522a112a3f96fc06d96ac45243a2dda685e ~ a2afffe6e81b567349233b80a59831595fc64b00^

fixed: a2afffe6e81b567349233b80a59831595fc64b00


使用 milli_delay() (使用 get_ticks() system call), 很奇怪的又有無法切換 process 的問題, 只出現幾次的 process 切換後, 就只看到 timer interrupt 裡頭印出的字元, 可以看到中斷重入的現象。

我很害怕又是哪裡的切換 (ring0/ring1) 或是中斷沒處理好, 明明已經花了很多時間去了解/驗證了。不過最後的結果令我意外, 是很好解決也很明顯的 bug。

原來 processs a, b 卡在 milli_delay() loop 裡頭, 計算 timer tick 的變數我竟然只宣告成 u8, 造成離開 loop 的條件無法達成, 改成 u32 就沒問題了。

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。