天天看點

Linux核心綜述Linux核心綜述

Linux核心綜述

核心是什麼?它的任務是什麼呢?對這些問題的回答有很多,簡單的概括一下吧,核心是位于上層應用與硬體之間的一個軟體中間層,能為上層應用提供服務(例如提供的系統調用),并且對其進行管理(例如何時配置設定給程序CPU、記憶體等資源),同時能夠驅動硬體完成功能動作。

如今對核心的實作有2種基本的理念,一種是微核心(microkernel)、另外一種則是一體化核心(Monolithic kernel):

  •   Microkernels:微核心僅實作了最基本的必要的核心功能(比如:記憶體管理、任務、線程、程序間通信等),其餘的所有服務(包括裝置驅動)在使用者模式下運作,而處理這些服務同處理其他的任何一個程式一樣。因為每個服務隻是在自己的位址空間運作。是以這些服務之間彼此之間都受到了保護。另外的技術優勢還包括:可擴充性好等等,劣勢在于會帶來額外的通信時間。
Linux核心綜述Linux核心綜述
  •  Monolithic kernels:核心的所有的子系統都被打包進一個單一檔案。Linux就屬于這樣的核心,但Linux實作了Modules,可以實作Modules的動态插入與删除

下圖是Linux系統的一個基本視圖:

Linux核心綜述Linux核心綜述
  • 程序(process)、任務切換(Task switch)和任務排程(Task Schedule):每個程序都在自己運作的CPU的虛拟記憶體中配置設定有位址空間,互相之間是隔離的,通信需要調用核心服務。
    •  任務切換:核心在CPU硬體的幫助之下,完成任務狀态的保護及恢複
    • 任務排程:核心必須決定何時排程那個程序并配置設定給它多長的運作時間
  • Unix Processes:
    • Linux實作了一個分級體系:程序必須來源于一個parent程序,init程序可以說是系統中其他所有程序的祖先程序,用pstree列印出的顯示比較類似的顯示了這個過程,為實作這個分級體系,Linux使用了fork和exec。
      • fork:執行對parent程序的一個幾乎完全的拷貝(PID不同),采用Copy on Write技術在parent或child程序寫操作時才區分parent和child的内容。
      • exec:在原空間中加載一新的程式并執行,old content被覆寫。
    •  線程(Threads):在Linux中也存在兩種叫法,程序(a heavy process)和線程(a light thread),一個程序中可以包含多個線程,這些線程共享程序空間的資料和資源,但是代碼執行路徑不同,可以并行執行。Linux使用clone方法來産生線程,該方法類似fork,但是clone方法卻要嚴格區分與父程序共享的資源和線程獨享的資源。
    • 命名空間(Namespace):利用命名空間,可以讓不同的processes擁有不同的系統視圖,例如,傳統Linux使用大量全局實體,如PID,利用指令空間,一些全局量可以劃分組,每個組擁有一個PID集合或者每個組提供不同的檔案系統視圖,卷不能同時挂載到不同視圖。當然,并不是核心的所有組成都使用了命名空間。
  • 位址空間(Address Space)和優先級(Privilege level):
    • 系統CPU的位數決定了可以管理的位址空間位數,這裡得到的位址空間是指虛拟位址空間(virtual address space),虛拟位址空間被劃分為核心位址空間(TASK_SIZE之上部分)和使用者位址空間(0~TASK_SIZE)兩部分。TASK_SIZE是一個與架構相關的值,例如IA-32架構,其值為3GiB,運作在使用者空間的各個程序認為自己獨享該空間,而核心位址空間則為各使用者空間程序所”共享”但不能通路,64位架構則更加複雜。
    •   優先級:CPU硬體定義了執行的不同優先級,但Linux隻使用2個:核心模式和使用者模式,使用者模式下運作的程序不能通路核心位址空間。 
      • 系統調用:發生系統調用時,為完成任務,程序依然運作在本程序上下文,可通路使用者空間
      • 中斷:程序運作在中斷上下文,不可通路使用者空間、不能睡眠
      • 核心線程:一般也不通路使用者空間,但可以睡眠,也可被排程,可用ps檢視,名字類似[kthreadd],帶[]即是。
  • 虛拟位址空間和實體位址空間:轉換使用頁表。但是大多數情況下,虛拟位址空間的大多數區域沒有使用,也沒有映射到實體記憶體。
  • 頁表(Page Tables):

如上文所述,由于大多數情況下虛拟位址空間的大多數區域沒有使用,也沒用映射到實體記憶體,因為為節省頁表所占用空間,使用分級 頁表,如下圖所示:

Linux核心綜述Linux核心綜述

       幾個術語:

       PGD:Page Global Directory

       PMD:Page Middle Directory

       PTE: Page Table Entry

      這種方式節約了記憶體空間,但是每次通路記憶體就需要順着這個鍊走一遍,為加速,CPU采用了MMU(Memory Management Unit)         技術和TLB(Translation Lookaside Buffer)技術(Cache緩存轉換位址),注意緩存内容的更新。

    • CPU架構不同,頁表級數不同(IA-32的2級頁表,64位架構的3或4級頁表),但是架構無關代碼卻采用的是四級頁表。
    •  記憶體映射(Memory Mapping):記憶體映射是指任意來源的資料映射進程序的虛拟位址空間,可以用處理通常記憶體的方法處理發生記憶體映射的位址空間,任意的改變都會自動改變源資料。如讀寫檔案時,來自硬碟的檔案映射進記憶體,核心自動把改變寫回該檔案。
  •  實體記憶體配置設定

核心隻能配置設定整個頁框,對記憶體的更小劃分交給了庫,庫将核心傳回的頁框劃分成更小的部分,然後配置設定給程序。 

    •  The Buddy System
Linux核心綜述Linux核心綜述

   所有大小(1,2,4,8,16…)的buddies都在核心的一個特定表中。當配置設定記憶體時,大的buddy分裂成小的buddies,小 的buddies也可以合成大的buddy以提供所需的記憶體。記憶體的頻繁配置設定也會帶來記憶體碎片問題 

    • The Slab Cache

核心也經常需要小于page frame的塊,但由于核心不能使用标準庫提供的功能,是以它需要定義自己的、附加的記憶體管理層 次,這個層次建立在buddy系統之上并對buddy系統提供的頁框進行分割。它建立了一個對small objects使用的通用池, 即slab cache: 

      •  對常用對象,核心建立自己的cache,僅存放特定類型的對象,池中對象按需移出及返還。slab cache自動與buddy系統互動,索要所需頁框。
      • 對普通的small memory blocks的配置設定,核心建立了一系列不同對象大小的池,可使用同一函數通路,即kmalloc和kfree。

标準workload,slab配置設定器可以很好工作,但是對于大型機或嵌入式系統并不行,linux針對這些用例提供了不同 的allocator,但仍向其他子產品提供相同的接口

Linux核心綜述Linux核心綜述
    • Swapping and Page reclaim:

    Swapping通過利用硬碟可以虛拟的擴大可用記憶體,不常用的記憶體頁被swap out到硬碟上,并在頁表中使用特殊标記的項标     記,應用需要使用時則核心産生page fault将該頁swap in 記憶體。

     Page reclaim(頁回收):需要将資料寫回到磁盤,然後回收頁框使用。

  • Timing

核心必須能夠衡量時間及各時間點的不同,通常使用時鐘中斷來計時,每秒鐘的滴答數就是jiffies,用全局變量jiffies_64或其32位 的jiffies來表示,依賴于硬體架構,jiffies按HZ增加,一般是每秒增加1000~100,這個值是很粗粒度的,如今的高性能的timer,依 賴硬體,核心提供了額外的附加方法來提供更高的精度。這個周期性的tick應該是動态的,當沒有什麼任務時,它對周期性的時鐘中斷 可不反應。

  • 系統調用

系統調用由核心實作,執行系統調用時,處理器必須從使用者模式進入核心模式,系統調用的實作随CPU架構甚至CPU不同而不同,一 般IA-32是利用軟中斷方式,但系統調用是使用者自主從使用者模式進入核心模式的唯一方法

  • 裝置驅動

友善使用者程式利用/dev/下的裝置檔案驅動裝置,大緻分成字元裝置(順序讀寫、禁止随機讀寫、按位元組/字元讀寫)和塊裝置(随機讀 寫、塊讀寫,禁止按位元組讀寫)

  •  網絡

        網卡依然由驅動控制,但/dev/沒有網絡裝置檔案。linux利用socket接口

  • 檔案系統

利用VFS支援多種檔案系統。

Linux核心綜述Linux核心綜述
  • 子產品和熱插拔

子產品支援核心運作時動态加載和解除安裝。Modules僅在核心空間執行,與靜态編譯進核心的子產品擁有同等的權限。

  • Caching

分為page cache和buffer cache,頁cache用于加速對磁盤等慢速裝置的通路,而buffer cache很久前多用于system cache,但 如今多被page cache代替

  •  List處理

在<list.h>中存在:

Linux核心綜述Linux核心綜述

其他結構體要使用它時,可以包含它:

Linux核心綜述Linux核心綜述

雙向循環連結清單的頭也是list_head類型,并且常用LIST_HEAD(list_name)宏初始化。

  •  對象管理和引用計數
    • 通用核心對象Generic Kernel objects:

    如下資料結構可作為basis内嵌到其他資料結構中:

Linux核心綜述Linux核心綜述

              内嵌:

Linux核心綜述Linux核心綜述

         k_name: 是通過sysfs傳回給使用者空間的對象名

         kref    : 用于引用管理

         entry  : 把kobject聯成一個連結清單

         parent  :用于kobjects之間組成分層架構

         kset    : 指向該kobject所在的kset

          ktype  :  提供kobject嵌入的對象的更詳細的資訊

Linux核心綜述Linux核心綜述

         原子性的引用計數,當其值為0時,該對象就可以從記憶體中删除了。

Linux核心綜述Linux核心綜述
    •  有時需要将kobjects組合成一個集合,這就需要:
Linux核心綜述Linux核心綜述

ktype  :指向引發the behaviors of kset的遠端對象

list     :指向組合成該set的kobject對象連結清單

uevent_ops:指向操作集合,用于将該set的資訊傳遞到使用者空間

kset隻是管理自身的特性,對所包含的kobjects不做任何操作。

Linux核心綜述Linux核心綜述

kobj_type提供了與sysfs的接口。

    • 引用計數
Linux核心綜述Linux核心綜述

   原子性使得即使在多核處理器中也可以,相應的輔助函數有kret_init()、kret_get()、kret_put()

  • 資料類型
    •  類型定義

     核心使用typedef來定義各種新的類型,以獨立于架構,這種定義是放在架構相關代碼中的,有時核心也需要精确bit位數的                類型。

    •  位元組序

     根據系統架構的不同,存儲多位元組資料使用big-endian或little-endian來存儲多位資料,差別如下:

Linux核心綜述Linux核心綜述

    核心提供了大量的函數/宏來在CPU使用的格式和存儲在記憶體中的格式之間的轉換

    •  Per-CPU Variables

    使用DEFINE_PER_CPU(name,type)來為系統中每個CPU建立一個該類型變量對象,使用時需要使                 用get_cpu(name,cpu)來擷取指定cpu上的該變量,smp_processor_id()來傳回目前active的cpu

    • 通路使用者空間

    一些代碼中有用__user标記的指針,用于标記這些指向使用者空間區域的指針

 應用程式錯誤一般會導緻段錯誤或者core dump,但是核心錯誤會導緻系統當機,所有的核心代碼都必須保護,以防止并    發,但由于對多核的支援,核心代碼必須是可重入的和線程安全的,即程式可以并行執行,但資料必須防止并行通路。核心必   須在大小端機器上都能運作。從2.6.23核心開始,IA-32和AMD64都統一到x86目錄下了,但仍區分。

繼續閱讀