Linux 核心開發,linux核心開發
1.1 虛擬記憶體
Linux 是一個多任務的系統,如果每個任務都獨立的佔用記憶體,則實際的實體記憶體將很快消耗殆盡,實際上對於前台正在啟動並執行任務來說,所需要要的記憶體並不多,很多任務基本不需要運行,也就沒有必要一直佔用記憶體,虛擬記憶體技術採用硬碟來充當一部分記憶體,當記憶體不足時就將不需要在記憶體中使用的資料搬移到硬碟中去,當任務需要運行時又將硬碟中的資料搬回實體記憶體。
虛擬記憶體技術不僅起到了保護作業系統的作用,而且使得使用者程式可以使用到比實際實體記憶體更大的地址空間,屏蔽了實際實體記憶體對使用者地址空間的影響。
1.2 地址空間的劃分
對於一個進程而言,Linux 系統將虛擬位址空間劃分為使用者空間與核心空間,使用者空間佔用3G(0x0~0xBFFFFFFF),而核心空間佔用1G(0xC0000000 ~ 0xFFFFFFFF)。
Linux 是一個多使用者的系統,每個使用者就是一個進程,享有獨立的地址空間,但是它們是共用核心空間,不同的進程之間進行切換的時候,核心空間的是不變的。
1.3 使用者空間
Linux 應用程式時在使用者空間運行,只有當產生中斷或者當出現系統調用的時候進程才會從使用者態切換到核心態。完成中斷操作或者系統調用後又回到使用者態。
1.4 核心空間
Linux 的核心程式運行在核心空間,這是為了保護核心程式不受劣質應用程式的影響而崩潰,而且能起到保護資料的作用。因為應用程式不具有直接控制硬體的許可權,只有核心程式才能擁有系統的最高許可權。
1.5 核心記憶體的分配與釋放
在建立進程fork()、動態非配記憶體malloc()時分配的記憶體都只是虛擬記憶體,而不是實體記憶體,之後在實際訪問分配的虛擬位址時,才會由“請頁機制”產生“缺頁”異常,進入實際分配頁框的程式。
“缺頁”異常是是虛擬記憶體賴以生存的基礎,他會告訴核心為進程分配物理頁,並建立頁表,這時虛擬位址才映射到實際的物理地址。
在應用程式中,使用的是malloc來動態分配記憶體,而在核心中使用的是kmalloc來分配記憶體,kmalloc的原型是
#include<linux/slab.h>
#include<linux/gfp.h>
void *kmalloc(size_t size , int flags);
參數:
size:需要分配記憶體的大小
flags:分配標誌,控制kmalloc的行為
以下是flages 可能的標誌。
最常用的GFP_KERNEL,他表示記憶體配置(最終總是調用get_free_pages來實現實際的分配,這就是,這就是GFP首碼的由來)是代表運行在核心空間的進程執行的。使用GFP_KERNEL容許kmalloc在分配空閑記憶體時候如果記憶體不足容許把當前進程睡眠以等待。因此這時分配函數必須是可重新進入的。如果在進程上下文之外如:中斷處理常式、tasklet以及核心定時器中這種情況下current進程不該睡眠,驅動程式該使用GFP_ATOMIC。
用來從中斷處理和進程上下文之外的其他代碼中分配記憶體. 從不睡眠.
核心記憶體的正常分配. 可能睡眠.
用來為使用者空間頁來分配記憶體; 它可能睡眠.
如同 GFP_USER, 但是從高端記憶體配置, 如果有. 高端記憶體在下一個子節描述.
類似 GFP_KERNEL,但禁止任何 I/O 初始化
類似 GFP_KERNEL,但不允許執行任何檔案系統調用
這個標誌要求分配在能夠 DMA 的記憶體區. 確切的含義是平台依賴的並且在下面章節來解釋.
這個標誌指示分配的記憶體可以位於高端記憶體.
這個標誌標識了一個高優先順序請求, 它被允許來消耗甚至被核心保留給緊急狀況的最後的記憶體頁.
當它有困難滿足一個分配. __GFP_REPEAT 意思是" 更儘力些嘗試" 通過重複嘗試 -- 但是分配可能仍然失敗
告訴分配器不要失敗; 它盡最大努力來滿足要求. 使用 __GFP_NOFAIL 是強烈不推薦的
告知分配器立即放棄如果得不到請求的記憶體.
1.6 按頁分配與釋放
如果核心模組需要分配大塊記憶體,使用面向頁的分配技術會更好。
#include<linux/gfp.h>
- get_zeroed_page(unsigned int flags)
返回新頁面的指標,並將分頁清空。
- __get_free_page(unsigned int flags);
申請一個頁面,返回新頁面的指標,但不清零頁面。
- __get_free_pages(unsigned int flags ,unsigned int order)
分配並返回一個紙箱記憶體區第一個位元組的指標,記憶體區可能是一個或者多個頁長,但是沒有清零(物理上連續)。
order 是你在請求的或釋放的頁數的以 2 為底的對數(即, log2N). 例如, 如果你要一個頁 order 為 0, 如果你請求 8 頁就是 3. 如果 order 太大(沒有那個大小的連續區可用), 頁分配失敗. get_order 函數, 它使用一個整數參數, 可以用來從一個 size 中提取 order(它必須是 2 的冪)給主機平台. order 允許的最大值是 10 或者 11 (對應於 1024 或者 2048 頁), 依賴於體系. 但是, 一個 order-10 的分配在除了一個剛剛啟動的有很多記憶體的系統中成功的機會是小的。
- void free_pages(unsigned long addr , unsigned long order);
釋放釋放的頁記憶體。
- void free_page(unsigned long addr);
釋放釋放的頁記憶體,需要注意的是如果釋放的系統記憶體與分配的記憶體不一致會導致系統錯誤。
1.7 核心空間的記憶體分布
核心空間是有核心進行映射的,它不會跟著進程變化,是固定的。
什麼是高端記憶體:
在x86結構中,核心被分為三個區塊,地區分布如下(Linux 與x86類似):
ZONE_DMA 記憶體開始的16MB
ZONE_NORMAL 16MB~896MB
ZONE_HIGHMEM 896MB ~ 結束
896M以上被稱為高端記憶體,896M以下被稱為低端記憶體。
- 直接映射區(Direct Memory Region)
從0xC0000000(3G)開始的最多896的記憶體地區被稱作直接映射區,這是因為該地區的線性地址和物理存在直接的線性轉換關係:
線性地址 = 0xc0000000 + 物理地址
比如物理地址為0x100000 ~ 0x200000 的線性地址就是0xc0100000 ~ 0xc0200000
直接映射區的記憶體可以通過kmalloc直接分配。
該地區的地址通過vmalloc來進行分配,需要注意的是vmalloc所分配出記憶體區的線性地址連續,但是實體記憶體地區是不一定是連續的。它是通過頁表的方式將各個閒置頁串連起來使用,所以效率要比kmalloc 要低很多。不過vmalloc所分配出來的地址可能出於高端記憶體、也可能出於低端記憶體。
該地區可訪問高端記憶體。存取方法是使用alloc_page(_GFP_HIGHMEM)分配高端記憶體頁或者使用kmap函數將分配到的高端記憶體映射到該地區。
永久映射區常用的全域變數:
PKMAP_BASE:永久映射空間的起始地址。永久映射空間為4M。所以它最多能映射4M/4K=1024個頁面。
pkmap_page_table:永久映射空間對應的頁目錄。我們來看一下它的初始化:
pkmap_page_table = pte_offset_kernel(pmd_offset(pgd_offset_k
(PKMAP_BASE), PKMAP_BASE), PKMAP_BASE);
實際上它就是PKMAP_BASE所在的PTE
LAST_PKMAP:永久映射空間所能映射的頁面數。在沒有開啟PAE的情況下被定義為1024
highmem_start_page:高端記憶體的起始頁面
pkmap_count[PKMAP]:每一項用來對應映射地區的引用計數
- 固定映射區(Fixing Mapping Region)
該地區和4G的頂端只有4k的隔離帶,其每個地址項都服務於特定的用途,如ACPI_BASE等。
LINUX核心開發的書
最經典的三本:
《linux核心設計與實現》第二版 by Robert Love
《深入理解linux核心》
《情景分析》
先看《深》,那主要講原理,《linux核心設計與實現》也講原理性並且更淺。最好先看看作業系統原理的書。看了幾遍後,就看情景分析,最好對著《深》看。兩本交叉看,《深》是綱,《情》是目。最後深入代碼。
大家過年,關注的少了~
linux核心開發
前幾天在新華書店看到一本書《21天學會linux編程》,雖說是有點誇張,可乘以十也只需210天而已,只要夠努力,相信你可以!