驅動基礎之分配內存

1 kmalloc()

函數原型如下所示:

void *kmalloc(size_t size,int gfp_mask);

該函數是一個簡單的接口,用它可以獲得以字節爲單位的內存。該函數返回一個指向內存塊的指針,其內存塊至少有size大小。所分配的內存區在物理地址上是連續的。出錯時,它返回NULL。除非沒有足夠的內存可用,否則內核總能分配成功。

通過kmalloc()獲得的物理內存應該被kfree()釋放,函數原型如下所示:

void kfree(const void *addr);

如果想要釋放的內存不是由kmalloc()分配的,或者想要釋放的內存早就釋放了,此時調用kfree()會導致嚴重的後果。與用戶空間類似,分配和回收要配對使用,以避免內存泄露和其他BUG,注意,調用kfree(NULL)是安全的。

gfp_mask 表示分配標誌符,有如下常用標誌:

GFP_KERNEL 內核內存的常用分配方法,可能引起休眠。

使用GFP_KERNEL允許kmalloc()在空閒內存較少時把當前進程轉入休眠以等待一個頁面。在當前進程休眠時,內核會採取適當的行動,或者是把緩衝區的內容刷新到硬盤上,或者是從一個用戶進程換出內存,以獲取一個內存頁面。

GFP_ATOMIC 用於中斷處理例程、tasklet、持有自旋鎖以及其他不能休眠的地方。內核通常會爲原子性的分配預留一些空閒頁面。使用GFP_ATOMIC標誌時,kmalloc()甚至可以用掉最後一個空閒頁面。

2 __get_free_pages()

函數原型如下所示:

unsigned long __get_free_pages(unsigned int gfp_mask,

unsigned long order)

該函數分配(1<<order)個連續的物理頁,並且返回分配內存的首地址,分配的內存頁不清零。內存頁釋放函數如下所示:

void free_pages(unsigned long addr,unsigned long order)

釋放頁時要謹慎,只能釋放屬於你的頁,傳遞了錯誤的地址或者錯誤的order值,都可能導致系統崩潰。

order 表示分配的頁數是(1<<order),order可允許最大的值是10或者11(對應於1024或者2048個頁),這依賴於體系結構。

kmalloc()和__get_free_pages()的異同:

kmalloc()和__get_free_pages()申請的內存位於DMA映射區和常規區域映射區,而且在物理上也是連續的,它們與真實的物理地址只有一個固定的偏移,因此存在較簡單的轉換關係。

基於頁的分配策略能更有效地使用內存。按頁分配不會浪費內存空間,而用kmalloc()則會因爲分配粒度的原因浪費一定數量的內存。

3 vmalloc()

函數原型如下所示:

void *vmalloc(unsigned long size);

大多數情況下,只有硬件設備需要獲得物理地址連續的內存。在很多體系結構上,硬件設備存在於內存管理單元MMU之外,它根本不理解什麼是虛擬地址。因此,硬件設備用到的任何內存區都必須是物理地址連續的內存,而不僅僅是虛擬地址連續的內存。

很多內核代碼都使用kmalloc()來獲得內存,而不是vmalloc()。這主要是出於性能的考慮,vmalloc()爲了把物理上不連續的頁轉換爲虛擬地址空間連續的頁,必須專門建立頁表項。因爲這些原因,vmalloc()僅僅在不得已時纔會使用,一般是爲了獲得大塊內存時,比如模塊被動態插入到內核中時,就把模塊裝載到由vmalloc()分配的內存上。

vmalloc()在<linux/vmalloc.h>中聲明,在<mm/vmalloc.c>中定義。

vmalloc()返回一個指針,指向虛擬地址連續的內存,其大小至少爲size。在發生錯誤時,函數返回NULL。vmalloc()可能睡眠,因此不能在中斷上下文中進行調用。通過vmalloc()獲得的虛擬內存應該被vfree()釋放,函數原型如下所示:

void vfree(void *addr);

vfree()可能睡眠,因此不能在中斷上下文中進行調用。

4 ioremap()

函數原型如下所示:

void *ioremap(unsigned long offset, unsigned long size)

ioremap()與vmalloc()類似,也需要建立新的頁表,但是它並不進行vmalloc()中所執行的內存分配行爲。ioremap()返回一個特殊的虛擬地址,該虛擬地址位於vmalloc映射區域。通過ioremap()獲得的虛擬地址應該被iounmap()釋放,函數原型如下所示:

void iounmap(void *addr);

ioremap()有個變體是devm_ioremap(),類似於其他以devm_開頭的函數,通過devm_ioremap()進行的映射通常不需要在驅動退出和出錯時進行iounmap()。devm_ioremap()函數原型如下所示:

void __iomem *devm_ioremap(struct device *dev, unsigned long offset,

unsigned long size);

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