linux進程的地址空間,核心棧,用戶棧,內核線程
地址空間:
32位linux系統上,進程的地址空間為4G,包括1G的內核地址空間,和3G的用戶地址空間。
內核棧:
進程控制塊task_struct中保存了2個page大小的信息。
為什么每一個進程都是用各自的內核棧呢?
引用(http://hi.baidu.com/iruler/blog/item/0c3363f377ccc5c90a46e023.html)“
假設某個進程通過系統調用運行在內核態(使用這個全局內核堆棧),此時如果被搶占,發生一次切換,另一個進程開始運行,如果這個當前進程又通過系統調用陷入內核,那么這個進程也將使用這個全局內核堆棧,這樣的話就把以前那個進程的內核空間堆棧給破壞了。 而如果進程使用獨立的內核棧,就避免了這種情況的發生
內核線程:
擁有自己獨立內核棧的調度單元,可以參與調度,在內核空間執行。
用戶棧:
每一個線程有一個用戶棧,由ss和esp指向。
===================================================
進程1 進程2
內核代碼區 kcode (0xc0001000) kcode (0xc0001000)
內核棧區 kstack(0xc000F000) kstack(0xc001F000)
內核棧區 kstack(0xc000D000) kstack(0xc001D000)
...
內核數據區 kdata (0xc0003000) kdata (0xc0003000)
---------------------------------------------------------------------------------------------
用戶代碼區 ucode (0x70001000) ucode (0x70001000)
用戶棧區 ustack (0x7000F000) ustack (0x7000F000)
用戶棧區 ustack (0x7000D000) ustack (0x7000D000)
...
用戶數據區 udata (0x70003000) udata (0x70003000)
===================================================
合理的解釋:
內核1G空間的映射頁表(256個entries*4M)只有一份,n個進程共享(都復制了一份在自己的進程頁表內, 256個內核的entries+768個用戶空間的entries, 總共1024個entries,假定使用4M頁面,並且一開始就全都分配好)。
每個進程用戶空間的這些entries各不相同,比如說,同樣的0x70001000虛擬地址, 進程1指向物理內存0x2000, 而進程2指向0x1000。
每個線程對應的內核棧的虛擬地址不重疊。
thread1's kernel stack = 0f000,
thread2's kernel stack = 0d000,
thread3's kernel stack = 1f000,
thread4's kernel stack = 1d000
...
思考1:
如果內核棧不是預先分配好的(分 配的意思是指"在內核空間中分出一段一段不重疊空間作為各個線程的棧", e.g. kmalloc調用),那么步入內核態的時候, 壓棧,發生缺頁異常,必須對內核棧占用的這個頁進行換頁,而換頁歷程的調用必然涉及參數的壓棧出棧,而這個時候內核棧沒有就緒,異常發生嵌套,系統出錯!
思考2:
在內核里面做kmalloc是可以的,添加一個entries,關聯一塊物理內存,ok可以用了。
思考3:
如果希望進程共享某一個虛擬內存地址0x80001000的數據, 那么在需要共享的進程p1,p2的頁表中添加1個entries(0x80001000->0x3000)。
另外內核是天然的共享對象,所以才在每一個進程中頁表中設置內核空間頁表的一份拷貝。
如果有個家伙特立獨行,創建n個關於內核空間的頁表,指向n個物理內存塊,那么他就需要再在這n個物理內存中"鋪設"n分內核代碼和數據的副本(真是自找麻煩)。
思考4:
內核棧確實不適合共享(一個特殊的內存區域)。怎么辦?像用戶空間棧一樣固定在某一個虛擬地址,安插頁表項entries指向不同的物理內存?顯然不行! 只有一個辦法,在內核空間內分配n個不重疊的空間出來。
思考5:
內核步入的時候最初的“內核棧”並不是真正的內核棧,這個棧是全局的,每個cpu一個,是過渡到真正的內核棧使用的。(http://bbs.pediy.com/archive/index.php?t-87518.html)
思考6:
獨立內核棧的場景, at first 我們分析一下如果共用一個內核棧會出現什么情況,假設有A、B三個進程,A調用系統調用read(1,...)讀按鍵,此時正好又沒有按鍵,
所以A被阻塞在內核,此時內核調度B執行,此時B也調用一個系統調用被阻塞了,而此時按鍵事件到來,進程A被喚醒,A繼續執行。我們想想B進入內核
已經破壞了A進入內核的內核棧,那此時A能正常返回嗎?所以從上面分析A、B肯定是擁有各自的內核棧。此內核棧好像是和task_struct以前分配的
一共分配了3個頁面,除了task_struct占得內存外,其余的就是內核棧。而在x86上這個棧等指針保存在TSS斷的SS0和Esp0中。(http://bbs.chinaunix.net/thread-1930753-1-1.html)
參考資料:
1, 內核棧的使用(http://tech.ddvip.com/2008-09/122095404362368.html)