Linux任務切換代碼(switch_to)詳解)
來源:互聯網
上載者:User
Copyright 2009 (c) benzus以下代碼來自Linux-1.0核心 include/linux/sched.h 檔案。(注意到Linux 0.11版的核心基本上也同樣是這段代碼,所以本文也同樣適用於0.11核心)01 #define switch_to(n) { \
02 struct (long a,b;} __tmp; \
03 __asm__("cmpl %%ecx,current \n\t" \
04 "je 1f\n\t" \
05 "xchgl %%ecx, current\n\t" \
06 "movw %%dx, %1\n\t" \
07 "ljmp *%0\n\t" \
08 "cmpl %%ecx, %2\n\t" \
09 "jne 1f\n\t" \
10 "clts\n" \
11 "1:" \
12 ::"m" (*&__tmp.a), "m" (*&__tmp.b), \
13 "m" (last_task_used_math),"d" _TSS(n), "c" ((long) task[n])); \
14 } 注釋:這是一個嵌入式彙編宏,作用是從當前任務切換到任務n,在進程發送器中被調用。下面我來逐行注釋它。第2行定義了一個結構,包含2個long類型整數。第3行將task[n]與current比較,其中task[n]是要切換到的任務,current是當前任務;第4行說明,如果要切換到的任務是當前任務,則跳到標號1,即結束,什麼也不做,否則繼續執行下面的代碼。第5行交換兩個運算元的值,相當於C代碼的:current = task[n] ,ecx = 被切換出去的任務(原任務);第6行將新任務的TSS選擇符賦值給 __tmp.b;第7行是理解任務切換機制的關鍵。長跳轉至 *&tmp,造成任務的切換。AT&T文法的ljmp相當於Intel文法的 jmp far SECTION : OFFSET,在這裡就是將(IP)<-__tmp.a,(CS)<-__tmp.b,它的絕對位址之前加星號("*")。當段間指令jmp所含指標的選擇符指示一個可用任務狀態段的TSS描述符時,將造成任務切換。那麼CPU怎麼識別描述符是TSS描述符而不是其他描述符呢?這是因為所有描述符(一個描述符是64位)中都有4位用來指示該描述符的類型,如描述符類型值是9或11都表示該描述符是TSS描述符。好了,CPU得到TSS描述符後,就會將其載入到任務寄存器TR中,然後根據TSS描述符的資訊(主要是基址)找到任務的tss內容(包括所有的寄存器資訊,如eip),根據其內容就可以開始新任務的運行。我們暫且把這個恢複所有寄存器狀態的過程稱為恢複寄存器現場。第8~10行是判斷原任務上次是否使用過副處理器,若是,則清除寄存器CR0的TS標誌。第2個痛點是:在第7行執行後,完成任務切換(即切換到新的任務裡執行);當任務切換回來後才會繼續執行第8行!下面詳解其原因。既然任務切換時CPU會恢複寄存器現場,那麼它當然也會儲存寄存器現場了。這些寄存器現場都會被寫入原任務的tss結構裡,值得注意的是,EIP會指向引起任務切換指令(第7行)的下一條指令(第8行),所以,很明顯,當原任務有朝一日再次被調度運行時,它將從EIP所指的地方(第8行)開始運行。