2013年5月23日 星期四

x86 process switch implementation (1.3) - race condition

這個是副產品, 在寫 process switch 這系列時, 順到想的, 因為不是很好表達, 我只在 mosut 講這個, 並沒有想要 blog 一篇, 不過還是寫出來好了, 最主要是給我回憶用的。

我一直在思考, 如何才能用程式碼來表達出 race condition, 而不是老師上課教的, 只能用冥想的方式來理解。

以前老師的例子就是
a=0;

proc_a:
a=a+1;

proc_b:
a=a+1;

若是 a 的初始值為 0, 由於 a=a+1 會被 c compiler 編譯成 3 個指令:

mov 0x804a018,%eax
add $0x1,%eax
mov %eax,0x804a018

所以若不一次做完這 3 個組合語言指令, 在 proc_a, proc_b 執行過後, a 的值可能只被加了一次, 最終結果不是 2, 而是 1。

以下的程式碼便是用來產生這樣的結果, 由於我可以決定 int $0x30 的位置, 所以可以很容易複製這樣的行為, 若是靠 timer 來中斷, 要遇到這樣的巧合就很難控制了。

程式很短, 但不代表他很簡單, 需要了解 x86 process switch implementation(0), x86 process switch implementation(1), 否則應該看不懂這篇。

simple_proc.S
  1 #define STACK_FRAME_OFFSET 6
  2 .code16
  3 .text
  4 .global begin
  5 begin:
  6   xchg %bx, %bx #bochs magic break point
  7   cli
  8 
  9   xor     %eax, %eax
 10   mov     %cs,%ax
 11   mov     %ax,%ds
 12 
 13 ## reset 0x30 interrupt
 14   movw    $0x0, %bx
 15   movw    %bx, %es
 16   movw $switch_proc, %es:0xc0 # isr offset
 17   movw %ax, %es:0xc2 #isr seg
 18 
 19 
 20   movw    $0xb800, %ax
 21   movw    %ax, %gs
 22 
 23 ## set stack frame eip
 24   movw $proc_a, stack_frame_a+2
 25   movw $proc_b, stack_frame_b+2
 26 
 27 ## set stack frame cs
 28   movw %cs, %ax
 29   movw %ax, stack_frame_a+4
 30   movw %ax, stack_frame_b+4
 31 
 32 ## set stack frame flag
 33   # get flag
 34   pushf
 35   movw (%esp), %ax
 36   popf
 37   movw %ax, stack_frame_a+6
 38   movw %ax, stack_frame_b+6
 39 
 40   movw $stack_frame_a, cur_proc
 41   movw cur_proc, %sp
 42   popw %ax
 43   iret
 44 
 45   mov $0x4c00, %ax
 46   int $0x21           
 47 
 48 
 49 .global proc_a
 50 proc_a:
 51 1:
 52 #  mov $0x1, %ax
 53 
 54   movw var_a,%ax
 55   int $0x30
 56   addw $0x1,%ax
 57   movw %ax, var_a
 58 
 59   jmp 1b
 60 
 61 .global proc_b
 62 proc_b:
 63 1:
 64 #  mov $0x2, %al
 65 
 66   movw var_a,%ax
 67   addw $0x1,%ax
 68   movw %ax, var_a
 69   int $0x30
 70 
 71   jmp 1b
 72 
 73 .global switch_proc
 74 switch_proc:
 75   pushw %ax
 76   movw cur_proc, %dx
 77   cmp $stack_frame_a, %dx
 78   je 1f
 79   movw $stack_frame_a, cur_proc
 80   jmp 2f
 81 1:
 82   movw $stack_frame_b, cur_proc
 83 2:
 84   movw cur_proc, %sp
 85   popw %ax
 86   iret
 87 
 88 
 89 cur_proc:
 90   .word 0x0
 91 var_a:
 92   .word 0x0
 93 
 94   .space  256, 0
 95 proc_stack_top_a:
 96   .space  256, 0
 97 proc_stack_top_b:
 98 
 99 stack_frame_a:
100   .word 0x9# ax
101   .word 0x0# eip
102   .word 0x1# cs
103   .word 0x2# flag
104 
105 stack_frame_b:
106   .word 0x9# ax
107   .word 0x0# eip
108   .word 0x1# cs
109   .word 0x2# flag
110 
111 #if 0
112 static int a;
113 ++a;
114 
115 mov    0x804a018,%eax
116 add    $0x1,%eax
117 mov    %eax,0x804a018
118 #endif

L66 ~ 68 是一個完整的 a=a+1; 而 L54 ~ 57 被我安插了 int $0x30, 導致 a=a+1 不能完整執行就會 switch 要另外一個 process, 這個程式的表現一樣要使用 bochs single step, 在 blog 上我只能儘量表達清楚, 無法帶你跑一次 (其實我可以錄下 bochs 畫面, 不過不用這麼折磨我吧)。

那麼要如何解決這問題, 其中之一是使用 spin lock, x86 可以參考 test and set 指令實作出 spin lock, 將這 3 個指令保護下來, 一次做完這 3 個指令:
http://en.wikipedia.org/wiki/X86_instruction_listings
http://en.wikipedia.org/wiki/Test-and-set

沒有留言:

張貼留言

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

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