Linux系統經過長時間的發展,很多使用者都很瞭解Linux系統了,這裡我發表一下Linux系統中共使用了四種堆棧個人理解,和大家討論討論。
一 系統引導初始化臨時使用的堆棧
二 進入保護模式後提供核心程式始化使用的堆棧,該堆棧也是後來任務0使用的使用者態堆棧
三 每個任務通過系統調用,執行核心程式時使用的堆棧,稱之為任務的核心態堆棧,每個任務都有自己獨立的核心態堆棧
四 任務在使用者態執行的堆棧,位於任務進程 )邏輯地址空間近末端處
使用多個棧或在不同情況下使用不同棧的主要原因
(一)由於從實模式進入保護模式,使得CPU對記憶體定址訪問方式發生了變化,因此需要重新設定堆棧地區
(二) 為瞭解決不同CPU特權級共用使用堆棧帶來的保護問題,執行0級的核心代碼和執行3級的使用者代碼需要使用不同的棧。當一個任務進入核心態運行時,就會使用其TSS段中給出的特權級0的堆棧指標tss.ss0.tss.esp0,即核心棧,原使用者棧指標會儲存在核心棧中,而當從核心態返回使用者態時,就會恢複使用使用者態的堆棧
以下分別說明。
開機初始化時bootsect.s,setup.s)
當bootsect代碼被ROM BIOS引導載入到實體記憶體0x7c00處時,並沒有設定堆棧段,程式也沒有使用堆棧,直到bootsect被移動到0x9000:0處時,才把堆棧段寄存器SS設定為0x9000,堆棧指標esp寄存器設定為0xff00,所以堆棧堆棧在0x9000:0xff00處boot/bootsect.s L61,62)setup.s也使用這個堆棧
進入保護模式時候head.s,L31)
此時堆棧段被設定為核心資料區段0x10),堆棧指標esp設定成指向user_stack數組sched.c L67~72)的頂端,保留了1頁記憶體作為堆棧使用
初始化時main.c)
在執行move_to_user_mode()代碼把控制權移交給任務0之前,系統一直使用上述堆棧,而在執行過move_to_user_mode()之後,main.c的代碼被“切換”成任務0中執行。通過執行fork()系統調用,main.c中的init()將在任務1中執行,並使用任務1的堆棧,而main()本身則在被“切換”成為任務0後,仍熱繼續使用上述核心程式自己的堆棧作為任務0的使用者態堆棧。
任務的堆棧
每個任務都有兩個堆棧,分別用於使用者態和核心態程式的執行,並且分別稱為使用者態堆棧和核心態堆棧。
除了處於不同CPU特權級中,這兩個堆棧之間的主要區別在於任務的核心態堆棧很小,所儲存的資料最多不能超過4096個位元組,而任務的使用者態堆棧卻可以在使用者的64MB空間中延伸
在使用者態運行時
每個任務除了任務0和任務1)有自己的64MB地址空間,當一個任務進程)剛被建立時,它的使用者態堆棧指標被設定在其地址空間的靠近末端部分,應用程式在使用者態下運行時就一直使用這個堆棧,實際物理地址記憶體則由CPU分頁機制確定。
在核心態運行時
每個任務有其自己的核心態堆棧,用於任務在核心代碼中執行期間。其所在的線性地址中位置由該任務TSS段中ss0和esp0兩個欄位指定,任務核心態堆棧被設定在位於其任務資料結構所在頁面的末端,即於任務的任務資料結構task_struct)放在同一頁面中,參見kernel/fork.c L93
p->tss.esp0 = PAGE_SIZE + (long)p;
p->tss.ss0 = 0x10
*為什麼從主存區申請得來的用於儲存任務資料結構的一頁記憶體也能被設定成核心資料區段中的資料呢?就是說tss.ss0為什麼可以是0x10?
使用者核心態仍然屬於核心資料空間,在head.s中設定核心程式碼片段和資料區段的描述符,段長度都設定成了16MB,這個長度值是Linux0.11核心所能支援的最大實體記憶體長度head.s,110開始的注釋),所以,核心代碼可以定址到整個實體記憶體範圍中的任何位置,當然也包括主存區,每當任務執行核心程式而需要使用其核心棧時,CPU就會利用TSS結構把它的核心態堆棧設定成由tss.ss0和tss.esp0這兩個值構成
任務0空閑進程idle)和任務1初始化進程init)的堆棧
任務0和任務1的程式碼片段和資料區段相同,限長都是640KB,但它們被映射到不同的線性地址空間,任務0的段基址從線性地址0開始,而任務1的段基址從64MB開始,但他們全部映射到物理地址0~640KB範圍中,這個地址也就是核心代碼和基本資料所存放的地方,在執行了move_to_user_mode()後,任務0和任務1的核心態堆棧分別位於各自任務資料結構所在頁面的末端,而任務0的使用者態堆棧就是前面進入保護模式後使用的堆棧,即user_stack[]數組的位置,由於任務1在建立時複製了任務0的使用者堆棧,所以剛開始時任務0和任務1共用使用同一個使用者堆棧空間,但是當任務1開始運行時,寫時複製機制會為任務1另行分配主存區頁面作為堆棧空間使用,只有到這個時候,任務1才開始使用自己獨立的使用者堆棧記憶體頁面,因此任務0的堆棧需要在任務1實際開始使用之前保持乾淨,即任務0此時不能使用堆棧,以確保複製的堆棧頁面中不含任務0的資料
這樣你就學會Linux系統中使用了四種堆棧知識了。
- 設定Linux刪除自動備份節省系統空間
- Linux作業系統配置和使用Samba完成共用目錄
- 拋棄Linux作業系統選擇微軟?
- 解決Linux網銀更好網上購物
- 商業帝國的"雲端運算"Linux作業系統