Linux 系統中共使用了四種堆棧:
1) 系統初始化時臨時使用的堆棧;
2) 供核心程式自己使用的堆棧(核心堆棧),只有一個,位於系統地址空間固定的位置,也是後來任務0的使用者態堆棧;
3) 每個任務通過系統調用,執行核心程式時使用的堆棧,我們稱之為任務的核心態堆棧,每個任務都有自己獨立的核心態堆棧;
4) 最後一種是任務在使用者態執行的堆棧,位於任務(進程)地址空間的末端。
任務的堆棧
每個任務都有兩個堆棧,分別用於使用者態和核心態程式的執行,並且分別稱為使用者態堆棧和核心態堆棧。這兩個堆棧之間的主要區別在於任務的核心態堆棧很小,所以儲存的資料量最多不能超過(8096 – 任務資料結構)個位元組,大約為6K位元組。而任務的使用者態堆棧卻可以在使用者線性空間內延伸。
在使用者態運行時
每個任務(除了任務0)有自己的地址空間。當一個任務(進程)剛被建立時,它的使用者態堆棧指標被設定在其地址空間的末端,而其核心態堆棧則被設定成位於其任務資料結構所在頁面的末端。應用程式在使用者態下運行時就一直使用這個堆棧。堆棧實際使用的實體記憶體則由CPU分頁機制確定。由於Linux實現了寫時複製功能(Copy onWrite),因此在進程被建立後,若該進程及其父進程沒有
使用堆棧,則兩者共用同一堆棧對應的實體記憶體頁面。
在核心態運行時
每個任務有其自己的核心態堆棧,與每個任務的任務資料結構(task_struct)放在同一頁面內。這是在建立新任務時,fork()程式在任務tss段的核心級堆棧欄位(tss.esp0和tss.ss0)中設定的。核心為新任務申請記憶體用作儲存其task_struct結構資料,而tss結構(段)是task_struct中的一個欄位。該任務的核心堆棧段值tss.ss0也被設定成核心資料區段,而tss.esp0則指向儲存task_struct結構頁面的末端。
任務核心態堆棧與使用者態堆棧之間的切換
任務調用系統調用時就會進入核心,執行核心代碼。此時核心代碼就會使用該任務的核心態堆棧進行操作。當進入核心程式時,由於優先順序別發生了改變(從使用者態轉到核心態),使用者態堆棧的堆棧段和堆棧指標以及eflags會被儲存在任務的核心態堆棧中。而在執行iret退出核心程式返回到使用者程式時,將恢複使用者態的堆棧和eflags。