Linux進程上下文概念詳解

一、上下文基本概念

進程上下文和中斷上下文是操作系統中很重要的兩個概念,這兩個概念在操作系統課程中不斷被提及,是最經常接觸、看上去很懂但又說不清楚到底怎麼回事。造成這種局面的原因,可能是原來接觸到的操作系統課程的教學總停留在一種淺層次的理論層面上,沒有深入去研究。
處理器總處於以下狀態中的一種:
1、內核態,運行於進程上下文,內核代表進程運行於內核空間;
2、內核態,運行於中斷上下文,內核代表硬件運行於內核空間;
3、用戶態,運行於用戶空間。

用戶空間的應用程序,通過系統調用,進入內核空間。這個時候用戶空間的進程要傳遞很多變量、參數的值給內核,內核態運行的時候也要保存用戶進程的一些寄存器值、變量等。所謂的“進程上下文”,可以看作是用戶進程傳遞給內核的這些參數以及內核要保存的那一整套的變量和寄存器值和當時的環境等。

硬件通過觸發信號,導致內核調用中斷處理程序,進入內核空間。這個過程中,硬件的一些變量和參數也要傳遞給內核,內核通過這些參數進行中斷處理。所謂的“中斷上下文”,其實也可以看作就是硬件傳遞過來的這些參數和內核需要保存的一些其他環境(主要是當前被打斷執行的進程環境)。


關於進程上下文LINUX完全註釋中的一段話:

   當一個進程在執行時,CPU的所有寄存器中的值、進程的狀態以及堆棧中的內容被稱爲該進程的上下文。當內核需要切換到另一個進程時,它需要保存當前進程的所有狀態,即保存當前進程的上下文,以便在再次執行該進程時,能夠必得到切換時的狀態執行下去。在LINUX中,當前進程上下文均保存在進程的任務數據結構中。在發生中斷時,內核就在被中斷進程的上下文中,在內核態下執行中斷服務例程。但同時會保留所有需要用到的資源,以便中斷服務結束時能恢復被中斷進程的執行。

內核空間和用戶空間是操作系統理論的基礎之一,即內核功能模塊運行在內核空間,而應用程序運行在用戶空間。現代的CPU都具有不同的操作模式,代表不同的級別,不同的級別具有不同的功能,在較低的級別中將禁止某些操作。Linux系統設計時利用了這種硬件特性,使用了兩個級別,最高級別和最低級別,內核運行在最高級別(內核態),這個級別可以進行所有操作,而應用程序運行在較低級別(用戶態),在這個級別,處理器控制着對硬件的直接訪問以及對內存的非授權訪問。內核態和用戶態有自己的內存映射,即自己的地址空間。

      正是有了不同運行狀態的劃分,纔有了上下文的概念。用戶空間的應用程序,如果想要請求系統服務,比如操作一個物理設備,或者映射一段設備空間的地址到用戶空間,就必須通過系統調用來(操作系統提供給用戶空間的接口函數)實現。

      通過系統調用,用戶空間的應用程序就會進入內核空間,由內核代表該進程運行於內核空間,這就涉及到上下文的切換,用戶空間和內核空間具有不同的地址映射,通用或專用的寄存器組,而用戶空間的進程要傳遞很多變量、參數給內核,內核也要保存用戶進程的一些寄存器、變量等,以便系統調用結束後回到用戶空間繼續執行,所謂的進程上下文,就是一個進程在執行的時候,CPU的所有寄存器中的值、進程的狀態以及堆棧中的內容,當內核需要切換到另一個進程時,它需要保存當前進程的所有狀態,即保存當前進程的進程上下文,以便再次執行該進程時,能夠恢復切換時的狀態,繼續執行。

      同理,硬件通過觸發信號,導致內核調用中斷處理程序,進入內核空間。這個過程中,硬件的一些變量和參數也要傳遞給內核,內核通過這些參數進行中斷處理,中斷上下文就可以理解爲硬件傳遞過來的這些參數和內核需要保存的一些環境,主要是被中斷的進程的環境。

      Linux內核工作在進程上下文或者中斷上下文。提供系統調用服務的內核代碼代表發起系統調用的應用程序運行在進程上下文;另一方面,中斷處理程序,異步運行在中斷上下文。中斷上下文和特定進程無關。

      運行在進程上下文的內核代碼是可以被搶佔的(Linux2.6支持搶佔)。但是一箇中斷上下文,通常都會始終佔有CPU(當然中斷可以嵌套,但我們一般不這樣做),不可以被打斷。正因爲如此,運行在中斷上下文的代碼就要受一些限制,不能做下面的事情:

1、睡眠或者放棄CPU,這樣做的後果是災難性的,因爲內核在進入中斷之前會關閉進程調度,一旦睡眠或者放棄CPU,這時內核無法調度別的進程來執行,系統就會死掉;

2、嘗試獲得信號量 如果獲得不到信號量,代碼就會睡眠,會產生和上面相同的情況;

3、執行耗時的任務 中斷處理應該儘可能快,因爲內核要響應大量服務和請求,中斷上下文佔用CPU時間太長會嚴重影響系統功能;

4、訪問用戶空間的虛擬地址 因爲中斷上下文是和特定進程無關的,它是內核代表硬件運行在內核空間,所以在中端上下文無法訪問用戶空間的虛擬地址。

二、上下文切換與多處理器

於進程有兩個幻覺:一認爲自己獨享內存;二以爲自己獨享處理器。我們對於一臺機器上的多個進程的幻覺是感覺他們是同時運行。

我們來依次解釋下上面的三個幻覺:
關於獨享內存不是我們的重點,簡單說說。獨享內存是指我們每個進程都獨享虛擬內存。而虛擬內存地址最終是通過MMU翻譯成實際的物理地址。這樣做只是爲了提供一種邏輯上的連續性,屏蔽內存碎片或是規避因內存有限而擴展到硬盤的各種問題,這樣不用考慮實際的的限制從而使應用程序開發變得容易。還有一個值得注意的問題是在這個虛擬內存中如果這個進程是多線程的,那麼將共享改空間,除了各自的堆棧、寄存器和所謂的虛擬處理器。這樣會導致一個問題就是多個線程的stacksize對進程棧空間的要求呈線性增長,與複雜的多層級遞歸運算類似,導致stackoverflow。這也是好多語言比如Java的線程模型要求線程創建時指定好stacksize大小的原因。

以前是單處理器的機器,後來因爲通過單純的提高處理器主頻,已經無法明顯提升系統整體性能。實在沒辦法了,科學家們就想啊想啊,就相出了多核處理器。這樣的話處理線程級的多任務,就可以實現真正的並行了。但問題是處理器的核數遠小於需要並行的任務數量。有許多因素都客觀限制處理器核數。那要完成多個進程同時執行的幻覺就只能通過來回的輪番執行,快速切換。這就到上下文切換的話題了。

關於上下文切換我僅僅參考linux內核的實現從技術角度來解釋:
在linux中一個叫做task_struct結構體代表一個線程,linux調度器會對一個結構體:sched_entity結構體感興趣並對其進行調度,而它正好嵌入到task_struct中。因此對可以看出linux調度是線程級的。那具體怎麼調度呢?
Linux用紅黑樹存所有可運行的進程(注意是可運行),使用等待隊列wait_queue記錄休眠(被阻塞)線程。用一個例子來介紹調度和上下文切換的細節,例如網卡產生一箇中斷通知有網絡數據,執行中的線程阻塞(從執行狀態剝離並放入等待隊列),然後再到紅黑樹裏面選一個來執行。這個過程的詳細過程是:虛擬內存映射和處理器狀態均要切換到新線程,前一個線程寄存器、棧信息還有其他狀態信息被保存。而新線程的棧信息和寄存器信息被恢復,剛好是反操作。我們把上述過程叫做上下文切換。等到網絡數據讀取就緒,在等待隊列中的線程又被喚醒,接着放入紅黑樹中,成爲可執行態,等待被執行。

多處理器就是一臺機器具有多個處理器。他的主流架構叫做對稱多處理器(SMP),這些處理器共享內存,共用一個系統,程序可以並行執行在每一個處理器上。拿多核處理器來說,通過一個核心執行一個線程,操作系統通過指令分派讓一個核心負責一個程序執行,達到真正意義上的並行。目前的手機尤其是android手機通過添加核心數來提升運行速度。這確實可以得到提高。但是在軟件角度還受到幾方面限制:一是調度算法針對核心數優化,以充分利用多核優勢;二是程序的並行性,如果程序是單線程再多核同時也只能跑在一個上面,其他的卻白白浪費;還有就是,增加核心數和處理能力並非成線性關係。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章