Linux驅動程序開發009 - 使用內核內存

序言
我們在編寫用戶空間程序的時候經常需要動態或靜態(如靜態數組)的使用系統內存資源,同樣在內核空間也有類似的操作,但內核空間的操作要遠比用戶空間複雜的多,這一章就對如何在驅動程序中使用內存做個系統的介紹。

對於一個32位的系統來說,可訪問的內存地址空間是4G。Linux系統將這4G的地址空間劃分爲兩部分,以x86爲例,0-3G (0 - PAGE_OFFSET, 0xC0000000)是用戶地址空間,而3G-4G是內核地址空間,我們這裏就討論如何使用這部分地址空間。

靜態數據區
Linux的驅動模塊(Module)是ELF格式的,這裏有必要先對Linux系統的內存段(Section)做個簡單的介紹,如果你對此感興趣,建議你閱讀ELF文件格式,裏面會有更加詳盡的介紹。比較常見的就是.txt/.data/.bss段,它們就是靜態分配的內存區域:
  • .text
text段,又叫代碼段,它是程序代碼所在的區域,這個區域是隻讀的。
  • .data
data段,靜態初始化數據段,所有有初始值的全局變量和靜態變量(static)都位於data段。
  • .bss
bss段,未初始化的全局變量區,由於存放未初始化的全局變量。

由於靜態數據會持續佔有內存,如果你不是特別需要這樣的一塊內存,尤其對內存相對緊張的系統,建議你採用動態分配方式來分配大塊內存。

這裏要強調的一點就是,與用戶空間不同,進程內核棧的大小是有限制的,一般來說它是8K(32位系統)或16K(64位系統)。在<linux/sched.h>文件中有如下的定義:

union thread_union {
    struct thread_info thread_info;
    unsigned long stack[THREAD_SIZE/sizeof(long)];
};


THREAD_SIZE在不同的平臺下有不同的定義,對於ARM平臺,它就是8K,對於X86平臺,如果配置棧大小位4K,那麼它就是4K,否則,它就是8K。由於Linux進程內核棧不是很大,因此一定不能在你的驅動程序中使用大的局部變量(如數組),也不能使用遞歸這樣消耗棧的函數,否則很容易導致進程內核棧的溢出。

有興趣的讀者可以參考一下Linux2.4的內核代碼,它的棧的定義與Linux2.6有些不同,但進程內核棧的大小還是接近的。

動態內存分配
你已經使用過了內存分配函數了,它們主要是kmalloc和vmalloc。
  • Kmalloc
kmalloc是最常用的內核動態內存分配函數,有點類似於用戶空間的malloc函數。kmaloc同Linux內核分配器(Allocator)是緊 密相關的,通常內核支持SLAB,SLOB和SLUB內核分配器,默認的也是最常用的是SLAB,因此這裏我們假設內存分配器是SLAB的。
對於SLAB內存分配器,kmalloc的定義及實現在/include/linux/slab_def.h頭文件中:
static inline void *kmalloc(size_t size, gfp_t flags)

kmalloc是快速的內存分配函數,kmalloc並不清除(初始化)分配的內存區域內容,因此在使用kmalloc分配的內存時要十分小心,必要時需要你顯示的進行初始化。另外,kmalloc分配的內存在物理地址和虛擬地址上都是連續的,因此內存的訪問操作也就會更快。

kmalloc分配的內存位於物理內存映射區,那麼什麼是物理內存映射區呢?我們知道3G到4G的物理空間是Linux內存的內核邏輯地址空間,而物理內存會映射到從3G開始的一段空間,大小就是物理內存的大小,因此這個區域就是物理內存映射區,內核使用 high_memory來標識這個空間。對於x86來說,它的大小就是:(arch/x86/kernel/setup.c)
high_memory = (void *)__va(max_pfn * PAGE_SIZE - 1) + 1;

使用kmalloc時要注意,由於kmalloc總是要從連續的物理內存分配,因此如果分配大塊內存區域時有可能分配失敗,因爲系統不能保證有連續可用的物理內存。因此對於大塊內存區域,我們不建議使用kmalloc來分配,除非必須這樣做。


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