深入Linux內核架構第一章筆記


1. Linux是多任務系統, 支持並發執行若干進程,系統同時真正運行的進程數目不超過CPU的數量,因此內核會按照時間間隔在不同進程之間切換。

2.確定那個進程運行多長時間的過程稱為調度。

3.內核啟動init進程作為第一個進程,該進程負責進一步的系統初始化操作,並顯示登陸提示符或登陸界面。因此init是進程樹的根,所有進程都直接或間接來源次進程。

4. 進程不是內核支持的唯一一種程序執行方式,除此以外,還有線程。

5. Linux將虛擬地址空間分為兩部分,內核空間和用戶空間。

6. Intel 將處理器分為4個特權狀態,ring0 ~ ring3, 但是Linux只使用了兩種狀態:用戶態和核心態。

7。用來將虛擬地址空間映射到物理地址空間的數據結構稱為頁表。

8。為了減少頁表的大小並容許忽略不需要的區域,Linux將虛擬地址划分為多個部分,也就是多級頁表。

 

PGD (Page Global Directory) àPMD (Page Middle Directory)àPTE (Page Table Entry) àoffset

9. 多級頁表節省了大量的內存,但是它的缺點是需要逐級訪問多個數組才能將虛擬地址轉換為物理地址,耗時較長。於是CPU通過MMU來優化訪問操作及利用TLB(Translation lookaside buffer)來加速。

10. 內核很多時候需要分配連續頁,為快速檢測內存中的連續區域,一般情況下用伙伴系統,也就是空閑內存兩兩分組,每組中的兩塊內存稱為伙伴。另外一種方法就是slab。

11.slab主要針對經常釋放並分配的內存對象,需要對象時,可以快速分配。雖然slab的性能很好,但是對嵌入式系統而言,開銷太大,於是有了一個 slab 模擬層,名為 SLOB。這個 slab 的替代品在小型嵌入式 Linux 系統中具有優勢,但是即使它保存了 512KB 內存,依然存在碎片和難於擴展的問題。在禁用 CONFIG_SLAB 時,內核會回到這個 SLOB 分配器中

 

Linux 的slab 可有三種狀態:

  • 滿的:slab 中的所有對象被標記為使用。
  • 空的:slab 中的所有對象被標記為空閑。
  • 部分:slab 中的對象有的被標記為使用,有的被標記為空閑。

    與傳統的內存管理模式相比, slab 緩存分配器提供了很多優點。

  • 內核通常依賴於對小對象的分配,它們會在系統生命周期內進行無數次分配。
  • slab 緩存分配器通過對類似大小的對象進行緩存而提供這種功能,從而避免了常見的碎片問題。
  • slab 分配器還支持通用對象的初始化,從而避免了為同一目的而對一個對象重復進行初始化。
  • slab 分配器還可以支持硬件緩存對齊和着色,這允許不同緩存中的對象占用相同的緩存行,從而提高緩存的利用率並獲得更好的性能。

     

隨着大規模多處理器系統和NUMA系統的廣泛應用,slab分配器逐漸暴露出自身嚴重的不足:

  • 較多復雜的隊列管理。在slab分配器中存在眾多的隊列,例如針對處理器的本地緩存隊列,slab中空閑隊列,每個slab處於一個特定狀態的隊列之中。所以,管理太費勁了。
  • slab管理數據和隊列的存儲開銷比較大。每個slab需要一個struct slab數據結構和一個管理者kmem_bufctl_t型的數組。當對象體積較小時,該數組將造成較大的開銷(比如對象大小為32字節時,將浪費1/8空間)。為了使得對象在硬件告訴緩存中對齊和使用着色策略,還必須浪費額外的內存。同時,緩沖區針對節點和處理器的隊列也會浪費不少內存。測試表明在一個1000節點/處理器的大規模NUMA系統中,數GB內存被用來維護隊列和對象引用。
  • 緩沖區回收比較復雜。
  • 對NUMA的支持非常復雜。slab對NUMA的支持基於物理頁框分配器,無法細粒度的使用對象,因此不能保證處理器級的緩存來自同一節點(這個我暫時不太懂)。
  • 冗余的partial隊列。slab分配器針對每個節點都有一個partial隊列,隨着時間流逝,將有大量的partial slab產生,不利於內存的合理使用。
  • 性能調優比較困難。針對每個slab可以調整的參數比較復雜,而且分配處理器本地緩存時,不得不使用自旋鎖。
  • 調試功能比較難於使用。

12. SLOB是slab在小型嵌入式系統上的一個模擬器,目的是為了降低資源開銷。

13. 內存管理實際上是一種關於權衡的零和游戲。您可以開發一種使用少量內存進行管理的算法,但是要花費更多時間來管理可用內存。也可以開發一個算法來有效地管理內存,但卻要使用更多的內存。最終,特定應用程序的需求將促使對這種權衡做出選擇。

每個內存管理器都使用了一種基於堆的分配策略。在這種方法中,大塊內存(稱為 堆)用來為用戶定義的目的提供內存。當用戶需要一塊內存時,就請求給自己分配一定大小的內存。堆管理器會查看可用內存的情況(使用特定算法)並返回一塊內存。搜索過程中使用的一些算法有 first-fit(在堆中搜索到的第一個滿足請求的內存塊 )和 best-fit(使用堆中滿足請求的最合適的內存塊)。當用戶使用完內存后,就將內存返回給堆。這種基於堆的分配策略的根本問題是碎片(fragmentation)。

14. 給出了 slab 結構的高層組織結構。在最高層是 cache_chain,這是一個 slab 緩存的鏈接列表。這對於 best-fit 算法非常有用,可以用來查找最適合所需要的分配大小的緩存(遍歷列表)。cache_chain 的每個元素都是一個 kmem_cache 結構的引用(稱為一個 cache)。它定義了一個要管理的給定大小的對象池。

15. proc 文件系統提供了一種簡單的方法來監視系統中所有活動的 slab 緩存。這個文件稱為 /proc/slabinfo,它除了提供一些可以從用戶空間訪問的可調整參數之外,還提供了有關所有 slab 緩存的詳細信息。

16. 全局變量jiffies用來記錄自系統啟動以來產生的節拍的總數。啟動時,內核將該變量初始化為0,此后,每次時鍾中斷處理程序都會增加該變量的值。一秒內時鍾中斷的次數等於Hz,所以jiffies一秒內增加的值也就是Hz。 系統運行時間以秒為單位,等於jiffies/Hz。

注意,jiffies類型為無符號長整型(unsigned long),其他任何類型存放它都不正確。

Jiffies_64 是64為系統。

17.系統調用是用戶進程和內核交互的經典方法。主要包括進程管理, 信號,文件,目錄和文件系統,保護機制及定時器函數。

18.萬物皆文件,外設包括字符設備,塊設備及網絡設備。

19.Linux支持的文件系統有:Ext2, Ext3, ResierFS,XFS, VFAT等。

Ext2基於inode, 也就是說每個文件都構造了一個單獨的管理結構,稱為inode。

VFS(Virtual File System or Virtual File Switch)將底層文件系的具體信息和應用層隔離開來。

20. 模塊是個普通的程序,只不過運行在內核空間而不是用戶空間。模塊特性使得內核可以支持種類繁多的設備,而不會造成內核自身膨脹。

21.由於內核是基於頁的內存映射來實現訪問設備的,緩存也是按照頁的組織緩存起來的,也就是頁緩存。

22.內核提供的標准鏈表可以將任何類型的數據結構連接起來,當然這就意味着此鏈表不是類型安全的。此鏈表的機構存在於include/list.h下。

23. kobject是組成設備模型的基本結構。類似於java中的object類,是所有用來描述設備模型的數據結構的基類,它嵌入於所有的描述設備模型的容器對象中,例如bus,devices,drivers等。這些容器通過kobject鏈接起來,形成一個樹狀結構,這個樹狀結構與/sys中是一一對應的。需要注意的是,並不是說每一個kobject對象都需要在sysfs中表示,但是每一個被注冊到系統中的kset都會被添加到sysfs文件系統中,一個kset對象就對應一個/sys中的一個目錄,kset中的每一個kobject成員,都對應sysfs中一個文件或者一個目錄。

目前為止,Kobject主要提供如下功能:

  • 通過parent指針,可以將所有Kobject以層次結構的形式組合起來。
  • 使用一個引用計數(reference count),來記錄Kobject被引用的次數,並在引用次數變為0時把它釋放(這是Kobject誕生時的唯一功能)。
  • 和sysfs虛擬文件系統配合,將每一個Kobject及其特性,以文件的形式,開放到用戶空間。

    Kobject是基本數據類型,每個Kobject都會在"/sys/"文件系統中以目錄的形式出現。

    Ktype代表Kobject(嚴格地講,是包含了Kobject的數據結構)的屬性操作集合(由於通用性,多個Kobject可能共用同一個屬性操作集,因此把Ktype獨立出來了)。注3:在設備模型中,ktype的命名和解釋,都非常抽象,理解起來非常困難,后面會詳細說明。

    Kset是一個特殊的Kobject(因此它也會在"/sys/"文件系統中以目錄的形式出現),它用來集合相似的Kobject(這些Kobject可以是相同屬性的,也可以不同屬性的)。

Linux內核中有大量的驅動,而這些驅動往往具有類似的結構,根據面向對象的思想,我們就可以將這些共同的部分提取為父類,這個父類就是kobject,也就是驅動編程中使用的.ko文件的由來,下面這張圖是我根據內核源碼的kobject繪制的簡單的UML圖,從中可以看出,kobject包含了大量的設備必須的信息,而三大類設備驅動都需要包含這個kobject結構,也就是"繼承"自kobject。一個kobject對象就對應sys目錄中的一個設備。
內核源碼中的kobject結構定義如下

//include/linux/kobject.h

struct kobject {

const char *name;

struct list_head entry;

struct kobject *parent;

struct kset *kset;

struct kobj_type *ktype;

struct kernfs_node *sd; /* sysfs directory entry */

struct kref kref;

#ifdef CONFIG_DEBUG_KOBJECT_RELEASE

struct delayed_work release;

#endif

unsigned int state_initialized:1;

unsigned int state_in_sysfs:1;

unsigned int state_add_uevent_sent:1;

unsigned int state_remove_uevent_sent:1;

unsigned int uevent_suppress:1;

};

這個結構中,

struct kobject
name表示kobject對象的名字,對應sysfs下的一個目錄。
entry是kobject中插入的head_list結構,
parent是指向當前kobject父對象的指針,體現在sys結構中就是包含當前kobject對象的目錄對象,
kset表示當前kobject對象所屬的集合,
ktype表示當前kobject的類型。
sd用於表示VFS文件系統的目錄項,是設備與文件之間的橋梁,sysfs中的符號鏈接就是通過kernfs_node內的聯合體實現的。
kref是對kobject的引用計數,當引用計數為0時,就回調之前注冊的release方法釋放該對象。
state_initialized:1初始化標志位,在對象初始化時被置位,表示對象是否已經被初始化。
state_in_sysfs:1表示kobject對象在sysfs中的狀態,在對應目錄中被創建則置1,否則為0。
state_add_uevent_sent:1是添加設備的uevent事件是否發送標志,添加設備時會向用戶空間發送uevent事件,請求新增設備。
state_remove_uevent_sent:1是刪除設備的uevent事件是否發送標志,刪除設備時會向用戶空間發送uevent事件,請求卸載設備

kobject,kset是Linux設備管理中的基本結構體,但在實際操作中我們幾乎不會實際操作這些結構,因為他們本身並不具有針對某一個具體設備或驅動的信息,在Linux內核中,這兩個結構都是被包含具體的設備結構中,比如cdev,gendisk等,從面向對象的角度考慮,就是每一類設備都可以看作這兩個結構的子類。
通過上面的分析,我們可以看出這三者之間的關系,並畫出下面的結構框圖,sysfs中的上目錄結構就是根據kset之間的數據組織方式進行呈現的。

 

總結,Ktype以及整個Kobject機制的理解。
Kobject的核心功能是:保持一個引用計數,當該計數減為0時,自動釋放(由本文所講的kobject模塊負責) Kobject所占用的meomry空間。這就決定了Kobject必須是動態分配的(只有這樣才能動態釋放)。

而Kobject大多數的使用場景,是內嵌在大型的數據結構中(如Kset、device_driver等),因此這些大型的數據結構,也必須是動態分配、動態釋放的。那么釋放的時機是什么呢?是內嵌的Kobject釋放時。但是Kobject的釋放是由Kobject模塊自動完成的(在引用計數為0時),那么怎么一並釋放包含自己的大型數據結構呢?

這時Ktype就派上用場了。我們知道,Ktype中的release回調函數負責釋放Kobject(甚至是包含Kobject的數據結構)的內存空間,那么Ktype及其內部函數,是由誰實現呢?是由上層數據結構所在的模塊!因為只有它,才清楚Kobject嵌在哪個數據結構中,並通過Kobject指針以及自身的數據結構類型,找到需要釋放的上層數據結構的指針,然后釋放它。

所以,每一個內嵌Kobject的數據結構,例如kset、device、device_driver等等,都要實現一個Ktype,並定義其中的回調函數。同理,sysfs相關的操作也一樣,必須經過ktype的中轉,因為sysfs看到的是Kobject,而真正的文件操作的主體,是內嵌Kobject的上層數據結構!


Kobject是面向對象的思想在Linux kernel中的極致體現,但C語言的優勢卻不在這里,所以Linux kernel需要用比較巧妙(也很啰嗦)的手段去實現,

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM