Linux switch to 深入分析

2021-08-23 13:51:33 字數 4041 閱讀 6329

深入分析任務切換與堆疊 by liu wanli 下面可以直接鏈結文章出處:

關鍵字:時間中斷、任務切換、堆疊、linux0.01

引言:

任務切換與堆疊的關係怎樣?很多朋友可能不知道她們之間有什麼關係,還有一些朋友可能認為他們之間不會有太大的關係(文獻4)。而我認為:任務切換跟堆疊有著密切的關係!下面是我對它們之間關係進行的**,這裡的任務切換我指的是發生時間中斷時進行強制排程發生的任務切換,所以下面考慮堆疊時我是從中斷開始**的。當然,我在進行這方面分析的時候,也癒感它們的複雜性,錯誤之處在所難免,望各位朋友多多指正。建議讀者水平:* * *

一、時間中斷。

假設乙個程序在使用者空間執行時(這時cpl=3),發生了時間中斷。這時的中斷處理過程為(文獻1:p438):

1、根據中斷向量號找到中斷門描述符;

2、從描述符中分解出選擇子、偏移量、屬性欄位並進行相應的特權檢查;

3、根據描述符型別轉入相應中斷處理程式中去執行。

好象太膚淺了一些?再看看(文獻1:p439圖10.20):

1、選擇子為空?no繼續;

2、取得對應描述符;(描述符中dpl屬性應該為0,文獻3中斷向量初始化部分)

3、儲存段描述符?yes繼續;

4、非一致**段且dplcounter)>0) return;

current->counter=0;

if(!cpl)return;

schedule();

} 省略號為無關緊要的兩條語句,進行程序的計時。如果時間片沒有用完(counter>0)或cpl為0,不發生排程直接返回,當然這裡也不是就直接返回到以前執行的程序空間,而是返回到do_timer()中,注意開始的返回位址,然後再通過iret指令從中斷處理返回到程序中去。當然,根據我們的假設,這兒cpl應該為3,因為是在使用者空間發生中斷的。我們要從最複雜的情況來討論這個問題。好了,就讓我們進入到中心點吧,請進schedule()。

四、schedule()。 (文獻3:sched.c: schedule())

void schedule(void )

呵,這裡我又省略了幾句**,它執行的是排程演算法,即從所有狀態為『執行』的程序中找出下乙個要執行的程序,然後將編號賦給next。進行切換!

switch_to()是乙個巨集,它在(文獻3: sched.h)中定義:

#define switch_to(n) __tmp; /

__asm__("cmpl %%ecx,current /n/t" /

"je 1f/n/t" /

"xchgl %%ecx, current/n/t" /

"movw %%dx, %1/n/t" /

"ljmp *%0/n/t" /

"cmpl %%ecx, %2/n/t" /

"jne 1f/n/t" /

"clts/n" /

"1:" /

::"m" (*&__tmp.a), "m" (*&__tmp.b), /

"m" (last_task_used_math),"d" _tss(n), "c" ((long) task[n])); /

} 這是任務切換的關鍵**,原理是直接通過tss來進行任務的切換(文獻1:p420)。那我就將這段關鍵**逐行解說一下吧。cmpl %%ecx, current,比較任務n是不是當前程序,如果是當然就不用切換了,直接結束schedule()。xchgl %%ecx,current,current指標指向任務n的任務結構,ecx暫存器儲存當前程序的任務結構指標。movw %%dx, %1, 使__tmp.b=『gdt中第n個任務的tss選擇子』,注意_tss(n)是求選擇子的巨集!ljmp *%0,這句**就是真正的任務切換羅, at&t語法的ljmp相當於intel的jmp far section:offset指令格式,它的絕對位址前加*號。這裡引用(文獻1:p420)一段話:當段間轉移指令jmp所含指標的選擇子指示乙個可用任務狀態段tss描述符時,正常情況下就發生從當前任務到由該可用任務的切換。目標任務的入口點由目標任務tss內的cs和eip欄位所規定的指標確定,這樣的jmp指令內的偏移被丟棄。再具體的任務切換你也許得翻翻(文獻1:p421),這裡我只講有關堆疊的處理,那就是把暫存器現場儲存到當前任務的tss。把通用暫存器、段暫存器、eip及eflags的當前值儲存到當前的tss中。儲存的eip的值是返回位址,指向引起任務切換指令的下一條指令;恢復目標任務的暫存器現場,根據儲存在tss中的內容恢復各通用暫存器、段暫存器、eflag、eip。好了,基本概念就引用這麼多,那麼,剛才提到的程序馬上要被切換出去了,它儲存tss中eip是什麼呢?顯然,根據剛才的分析應該是cmpl %%ecx, %2這條指令。這意味著什麼呢?這就是說,如果下次這個任務要被切換成執行狀態時,它將從cmpl %%ecx, %2這條指令開始執行!那麼,由彼任務推到此任務,也就是說我們切換至任務next時,它也是從這條指令開始執行的!於是我們進入到任務next的堆疊空間,並開始執行,但由於任務next和當前的任務有著相同的堆疊路徑(這和linux中的核心控制路徑是不是一回事呢?),所以我們還是引用當前的堆疊來繼續分析。

哦,有點糊塗了,好象是。休息一下,再參考一下(文獻2:上冊p373)。專家也是這樣說的;)

要不,我們這麼理解,剛才被中斷的程序發生了強制排程,且也發生了任務切換,只不過是切換到它自己,實際上不是喲。好吧,jmp成功,開始執行。

五、轉折點,從schedule()返回。

cmpl %%ecx, %2;jne 1f; clts;1: 這幾句是與協處理器有關,還有ts標誌,我們就直接到1:吧,開始從schedule()返回,注意switch_to()是巨集,它在schedule()末端。返回到哪兒去了呢?跟蹤一下,看看上面的堆疊示意圖,返回位址就是呼叫do_timer後的那條語句,

addl $4, %esp

jmp ret_from_sys_call

這兒esp加4就是把堆疊中的cpl去掉,因為我們不用了,跳轉到ret_from_sys_call。哦,剩下的處理與系統呼叫返回共用**。

六、ret_from_sys_call (文獻3,kernel/system_call.s)

先看看我們的焦點,堆疊怎麼樣了呢?

| eax |

| ebx |

| ecx |

| edx |

| fs |

| es |

| ds |

-----------

| 外層eip |

| 外層cs |

| eflag |

| 外層esp |

| 外層ss |

-----------

ret_from_sys_call:

movel current, %eax

cmpl task, %eax

je 3f

movl cs(%esp), %ebx

testl $3, %ebx

je 3f

cmpw $0x17, oldss(%esp)

jne 3f

2: ....

3: popl %eax

popl %ebx

popl %ecx

popl %edx

pop %fs

pop %es

pop %ds

iret

2標號處我省略了一些有關訊號及其它一些處理。讓我們分析一下,如果當前任務是0號程序,或是任務先前的cpl為3(即使用者態),或是任務先前的堆疊段為ldt中指定的堆疊,jmp到3標號處。由先前的假設可知,此任務的cpl為3,那就跳吧。把eax, ebc, ecx, edx, fs, es, ds暫存器從堆疊中恢復出來。

現在堆疊如下:

| 外層eip |

| 外層cs |

| eflag |

| 外層esp |

| 外層ss |

-----------

記得我們還有最後一條語句喲,iret。這條指令大家想必已經很熟悉了,它恢復eip、cs、eflag、esp、ss。記得不,這是不是已經恢復到了最初的時間中斷時程序被中斷的那一刻?恭喜!你終於可以繼續做你需要做的事情了!小心,還有下乙個時間中斷,哦,你不怕?因為它不會影響你的連貫性。

深入篇 decltype深入分析

int fun int main void double tempa 3.0 const double ctempa 5.0 const double ctempb 6.0 const double const cptrtempa ctempa 1.dcltempa推斷為const double 保...

koa redux middleware 深入解析

對於現有的一些框架比如koa,express,redux,都需要對資料流進行一些處理,比如koa,express的請求資料處理,包括json.stringify,logger,或者一些安全相關的處理都需要在資料流中進行,還比如redux的整個資料的修改,支援中介軟體來擴充套件使用者對於資料修改的支援...

python深入 Python的深入理解

處理檔案和目錄 python 3 帶有乙個模組叫做 os,代表 作業系統 operating system os 模組 包含非常多的函式用於獲取 和修改 本地目錄 檔案程序 環境變數等的資訊。python 盡最大的努力在所有支援的作業系統上提供乙個統一的api,這樣你就可以在保證程式能夠在任何的計算...