ARM學習筆記之驅動程序篇四----內核管理子系統

1.7 linux 內核管理子系統

                                                          

 1.7.1 內存管理子系統

1,管理模型

內存管理系統可以分爲兩部分,分別是內核空間內存管理和用戶空間內存管理:

內存管理子系統的職責是:進程請求內存時分配可用內存,進程釋放內存後回收內存,以及跟蹤系統內存使用情況。現代操作系統要求能夠使多個程序共享系統資源,同時要求內存限制對於開發者是透明的。在這種情況下,虛擬內存應運而生。虛擬內存可以使得進程可以訪問比實際內存大得多的空間,並且使得多個程序共享內存顯得更加有效。

當程序從內存中取得數據的時候,需要使用地址指出需要訪問的內存位置(注意:這個地址是虛擬地址,他們組成的進程的虛擬地址空間)。每個進程都有自己的虛擬地址空間,這樣做的好處是可以防止非法讀取或覆蓋其他進程的數據(虛擬地址允許進程使用超過物理內存的內存空間,因此操作系統可以給每個進程提供獨立的虛擬線性地址空間)。

                                       

   頁:作爲內存管理的基本單元,頁的許多狀態需要被記錄下來(比如,內核需要知道什麼時候可以被回收),因此內核爲內核中的每個頁都準備了頁描述符struct page{}。系統在初始化時根據物理內存的大小建立起一個page結構數組mem_map,作爲物理頁面的“倉庫”。

2,物理內存分配

                

物理地址有896M直接映射到虛擬地址的內存空間,這是一一對應的映射,只有起始地址不一樣,偏移是一樣的。這個大小大多是固定的,哪怕你的內存超過1G,太小了就另外說了,當你內存很大的時候,超過896M時,剩餘的那些內存怎麼辦呢?這多出來的叫做高端內存,如果你使用vmalloc申請空間,就會在高端內存中分配,如果你使用kmalloc申請空間,就會在小於896的內存中分配。所以還是很講究的。

夥伴系統(夥伴算法):每當頁面被分配和回收的時候,系統都會遇到外部碎片或內存碎片的問題(即頁面散佈在內存中,即使可用頁面足夠多,但是無法分配大塊的連續頁面)。爲了解決這個問題,Linux系統提供了夥伴算法。夥伴算法原理:夥伴系統把內存中空閒塊組成鏈表,將不同大小的空閒內存塊組織起來,雖然大小不一樣,但是都是2的冪次方。當系統中有進程釋放內存的時候,夥伴系統就會搜索與所釋放塊大小相等的可用空閒內存塊,如果找到相鄰的空閒塊,就將其合併成兩倍於自身大小的塊。這種合併的塊稱爲夥伴。

slab分配器:爲了減少內存分配、初始化、銷燬和釋放的代價,通常會把經常使用的內存區以緩存的方式對待,並加以維護(即比如系統經常會使用task_struct,這就將該結構體以緩存方式常駐內存),當進程不在需要該內存區時,就會把該內存放入緩衝區。由此可見,slab實際上由許多緩存組成。緩存分爲 “專用”和“通用”。專用緩存保存特定對象的內存區,比如各種描述符,比如進程描述符"struct task_struct"。

3,虛擬地址轉換爲物理地址

                    

首先將32位的虛擬地址的高10位取出來作爲偏移,這個偏移加上CR3寄存器裏面的一級頁表基地址,就是存儲二級頁表基地址的單元的地址,根據該單元存儲的二級頁表的基地址找到頁表,然後取出32位虛擬地址的中間10位作爲偏移,將二級頁表的基地址和偏移相加得到物理頁表的基地址的存儲單元的基地址,從該單元取出物理頁表的基地址加上32位虛擬地址的低12位就是物理頁表的物理地址。

只有實實在在的去訪問虛擬地址所對應的內存時,纔會分配內存,如果不訪問,則拿到的只是一個虛擬地址。

1.7.2 進程管理子系統

1,程序與進程

程序:存放在磁盤上的一系列代碼和數據的可執行映像,是一個靜止的實體。

進程:是一個執行中的程序,它是動態的實體。

2,進程的四要素

(1)有一段程序供其執行。這段程序不一定是某個進程所專有,可以與其他進程共用。

(2)有進程專用的內核空間堆棧

(3)在內核中有一個task_struct 數據結構,即通常所說的“進程控制塊”。有了這個數據結構,進程才能成爲內核調度的一個基本單位,接受內核的調度。

(4)有獨立的用戶空間。

                                       

3,進程的狀態

                                                

4,進程描述

在Linux內核代碼中,線程、進程都使用結構task_struct(sched.h)來表示,它包含了大量描述進程/線程的信息,其中比較重要的有:

pid_t pid;//進程號
long state;//進程狀態
int prio;//進程優先級

5,進程調度

從就緒的進程中選出最適合的一個來執行。

(1)調度策略

SCHED_NORMAL(SCHED_OTHER):普通的分時進程

SCHED_FIFO:先入先出的實時進程

SCHED_RR:時間片輪轉的實時進程

SCHED_BATCH:批處理進程

SCHED_IDLE:只在系統空閒時才能夠被調度執行的進程

(2)調度時機

(1)主動式

在內核中直接調用schedule()。當進程需要等待資源而暫時停止運行時,會把自己的狀態置於掛起(睡眠),並主動請求調度,讓出CPU。

範例:

current->state = TASK_INTERRUPTIBLE;

schedule();

(2)被動式

被動式調度又名搶佔式調度。分爲:用戶態搶佔(Linux2.4、Linux2.6)和內核態搶佔(Linux2.6)。

1,用戶搶佔

用戶搶佔發生在:a.從系統調用返回用戶空間;b.從中斷處理程序返回用戶空間。

內核即將返回用戶空間的時候,如果need_resched標誌被設置,會導致schedule()被調用,即發生用戶搶佔。

a.當某個進程耗盡它的時間片時,會設置need_resched標誌

b.當一個優先級更高的進程進入可執行狀態的時候,也會設置need_resched標誌。

2,內核搶佔

用戶態搶佔缺陷:進程/線程一旦運行到內核態,就可以一直執行,直到它主動放棄或時間片耗盡爲止。這樣會導致一些非常緊急的進程或線程將長時間得不到運行,降低整個系統的實時性。

改進方式:允許系統在內核態也支持搶佔,更高優先級的進程/線程可以搶佔正在內核態運行的低優先級進程/線程。

內核搶佔可能發生在:a.中斷處理程序完成,返回內核空間之前;b.當內核代碼再一次具有可搶佔性的時候,如解鎖及使能軟中斷等。

在支持內核搶佔的系統中,某些特例下是不允許搶佔的:

(1). 內核正在運行中斷處理。

(2). 內核正在進行中斷上下文的Bottom Half(中斷的底半部)處理。硬件中斷返回前會執行軟中斷,此時仍然處於中斷上下文中。

(3). 進程正持有spinlock自旋鎖、writelock/readlock讀寫鎖等,當持有這些鎖時,不應該被搶佔,否則由於搶佔將可能導致其他進程長期得不到鎖,而讓系統處於死鎖狀態。

(4). 內核正在執行調度程序Scheduler。搶佔的原因就是爲了進行新的調度,沒有理由將調度程序搶佔掉再運行調度程序。

搶佔計數

 爲保證Linux內核在以上情況下不會被搶佔,搶佔式內核使用了一個變量preempt_count,稱爲內核搶佔計數。這一變量被設置在進程的thread_info結構中。每當內核要進入以上幾種狀態時,變量preempt_count就加1,指示內核不允許搶佔。每當內核從以上幾種狀態退出時,變量preempt_count就減1,同時進行可搶佔的判斷與調度。

(3)調度步驟

Schedule函數工作流程如下:
(1)清理當前運行中的進程。

(2)選擇下一個要運行的進程。

(3)設置新進程的運行環境。

(4)進程上下文切換。

    

 

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