一、KMALLOC
kmalloc 是一個功能強大且高速(除非被阻塞)的工具,所分配到的內存在物理內存中連續且保持原有的數據(不清零)。原型:
#include void *kmalloc(size_t size, int flags); |
參數詳解:
size 參數
內核管理系統的物理內存,物理內存只能按頁面進行分配。kmalloc 和典型的用戶空間 malloc 在實際上有很大的差別,內核使用特殊的基于頁的分配技術,以最佳的方式利用系統 RAM。Linux 處理內存分配的方法:創建一系列內存對象集合,每個集合內的內存塊大小是固定。處理分配請求時,就直接在包含有足夠大內存塊的集合中傳遞一個整塊給請求 者。
必須注意的是:內核只能分配一些預定義的、固定大小的字節數組。kmalloc 能夠處理的最小內存塊是 32 或 64 字節(體系結構依賴),而內存塊大小的上限隨著體系和內核配置而變化。考慮到移植性,不應分配大于 128 KB的內存。若需多于幾個 KB的內存塊,最好使用其他方法。
flags 參數
內存分配最終總是調用 __get_free_pages 來進行實際的分配,這就是 GFP_ 前綴的由來。
所有標志都定義在 主要的標志常被稱為分配優先級,包括: GFP_KERNEL 最常用的標志,意思是這個分配代表運行在內核空間的進程進行。內核正常分配內存。當空閑內存較少時,可能進入休眠來等待一個頁面。當前進程休眠時, 內核會采取適當的動作來獲取空閑頁。所以使用 GFP_KERNEL 來分配內存的函數必須是可重入,且不能在原子上下文中運行。 GFP_ATOMIC 內核通常會為原子性的分配預留一些空閑頁。當當前進程不能被置為睡眠時,應使用 GFP_ATOMIC,這樣kmalloc 甚至能夠使用最后一個空閑頁。如果連這最后一個空閑頁也不存在,則分配返回失敗。常用來從中斷處理和進程上下文之外的其他代碼中分配內存,從不睡眠。 GFP_USER 用來為用戶空間分配內存頁,可能睡眠。 GFP_HIGHUSER 類似 GFP_USER,如果有高端內存,就從高端內存分配。 GFP_NOIO GFP_NOFS 功能類似 GFP_KERNEL,但是為內核分配內存的工作增加了限制。具有GFP_NOFS 的分配不允許執行任何文件系統調用,而 GFP_NOIO 禁止任何 I/O 初始化。它們主要用在文件系統和虛擬內存代碼。那里允許分配休眠,但不應發生遞歸的文件系統調。 Linux 內核把內存分為 3 個區段: 可用于DMA的內存(位于一個特別的地址范圍的內存, 外設可以在這里進行 DMA 存取)、常規內存和高端內存(為了訪問(相對)大量的內存而存在的一種機制)。目的是使每中計算機平臺都必須知道如何將自己特定的內存范圍歸類到這三個區 段中,而不是所有RAM都一樣。 當要分配一個滿足kmalloc要求的新頁時, 內核會建立一個內存區段的列表以供搜索。若指定了 __GFP_DMA, 只有可用于DMA的內存區段被搜索;若沒有指定特別的標志, 常規和 可用于DMA的內存區段都被搜索; 若 設置了 __GFP_HIGHMEM,所有的 3 個區段都被搜索(注意:kmalloc 不能分配高端內存)。 內存區段背后的機制在 mm/page_alloc.c 中實現, 且區段的初始化時平臺相關的, 通常在 arch 目錄樹的 mm/init.c中。 有的標志用雙下劃線做前綴,他們可與上面標志'或'起來使用,以控制分配方式: __GFP_DMA 要求分配可用于DMA的內存。 __GFP_HIGHMEM 分配的內存可以位于高端內存. __GFP_COLD 通常,分配器試圖返回'緩存熱(cache warm)'頁面(可在處理器緩存中找到的頁面)。 而這個標志請求一個尚未使用的'冷'頁面。對于用作 DMA 讀取的頁面分配,可使用此標志。因為此時頁面在處理器緩存中沒多大幫助。 __GFP_NOWARN 當一個分配無法滿足,阻止內核發出警告(使用 printk )。 __GFP_HIGH 高優先級請求,允許為緊急狀況消耗被內核保留的最后一些內存頁。 __GFP_REPEAT __GFP_NOFAIL __GFP_NORETRY 告訴分配器當滿足一個分配有困難時,如何動作。__GFP_REPEAT 表示努力再嘗試一次,仍然可能失敗; __GFP_NOFAIL 告訴分配器盡最大努力來滿足要求,始終不返回失敗,不推薦使用; __GFP_NORETRY 告知分配器如果無法滿足請求,立即返回。 前面知識點摘自: http://www.douban.com/note/56607778/ 二、后備高速緩存 (lookaside cache) 內核中普通對象進行初始化所需的時間超過了對其進行分配和釋放所需的時間,因此不應該將內存釋放回一個全局的內存池,而是將內存保持為針對特定目而初始化的狀態。例如,如果內存被分配給了一個互斥鎖,那么只需在為互斥鎖首次分配內存時執行一次互斥鎖初始化函數(mutex_init)即可。后續的內存分配不需要執行這個初始化函數,因為從上次釋放和調用析構之后,它已經處于所需的狀態中了。 linux2.6中USB和SCSI驅動程序使用了這種高速緩存,是為一些反復使用的塊增加某些特殊的內存池。后背高速緩存管理也叫slab分配器,相關函數和類型在 slab分配器實現高速緩存具有kmem_cache_t類型。 kmem_cache_t * kmem_cache_create( const char *name, size_t size, size_t align, unsigned long flags; void (*constructor)(void*, kmem_cache_t *, unsigned long), void (*destructor)(void*, kmem_cache_t *, unsigned long)); 用于創建一個新的高速緩存對象。 constructor用于初始化新分配的對象,destructor用于清除對象。 一旦某個對象的高速緩存被創建以后,就可以調用kmem_cache_alloc從中分配內存對象。 void * kmem_cache_alloc(kmem_cache_t *cache,int flags); 釋放內存對象使用kmem_cache_free void kmem_cache_free(kmem_cache_t *cache,const void *obj); 在內存空間都被釋放后,模塊被卸載前,驅動程序應當釋放他的高速緩存。 int kmem_cache_destory(kmem_cache_t *cache); 要檢查其返回狀態,如果失敗,表明莫塊中發生了內存泄露。 基于slab的高速緩存scullc kmem_cache_t *scullc_cache; scullc_cache=kmem_cache_creat('scullc',scullc_quantum,0,SLAB_HWCACHE_ALIGN,NULL,NULL); if(!scullc_cache) { scullc_cleanup(); return -ENOMEM; } if(!dpte->data[s_pos]) { dptr->data[s_pos]=kmem_cache_alloc(scullc_cache,GFP_KERNEL); if(!dptr->data[s_pos]) goto nomem; memset(dptr->data[s_pos],0,scullc_quantum); } for(i=0;i if(dptr->data[i]) kmem_cache_free(scullc_cache,dptr->data[i]); } if(scullc_cache) kmem_cache_destory(scullc_cache); 三、內存池 內核中有些地方的內存分配是不允許失敗的,為確保能分配成功,內核建立一種稱為內存池的抽象,他試圖始終保持空閑狀態,以便緊急情況使用。 mempool_t * mempool_creat(int min_nr, mempool_alloc_t *alloc_fn, //對象分分配 mempool_alloc_slab mempool_free_t *free_fn, //釋放 mempool_free_slab void *pool_data); 可以用如下代碼來構造內存池 cache=kmem_cache_creat(...); //創建一個高速緩存 pool=mempool_creat(MY_POOL_MINIMUM,mempool_alloc_slab,mempool_free_slab,cache);//建立內存池對象 void *mempool_alloc(mempool_t *poll,int gfp_mask);//分配對象 void *mempool_free(void *element,mempool_t *poll);//釋放對象 void mempool_destroy(mempool_t *poll);//銷毀內存池 注意:mempool會分配一些內存塊,空閑且不會被用到,造成內存的大量浪費。所以一般情況不要用內存池。 四、實例分析 1.定義內存操作的指針 2.在init函數中,分別使用kmalloc,get_zeroed_page,vmalloc申請物理內存,整頁內存,虛擬內存 3.申請內存之后就是讀寫內存 4.當我們不用內存時,在exit函數中釋放內存,防止造成內存泄露 5.編譯測試 如圖所示,當我們insmod時,打印出了申請的內存的地址,并且成功的讀出了我們寫入的數據 附上驅動代碼: 1 /*在內存中申請1k 大小的內存做為簡單的一個設備來訪問*/ 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 14 #include 15 16 #define Driver_NAME 'kmalloc' 17 #define DEVICE_NAME 'kmalloc' 18 19 static int major = 0; 20 21 //auto to create device node 22 static struct class *drv_class = NULL; 23 static struct class_device *drv_class_dev = NULL; 24 25 //定義內存分配的指針 26 char *buf1 = NULL; 27 char *buf2 = NULL; 28 char *buf3 = NULL; 29 30 static int key_open(struct inode *inode, struct file *file) 31 { 32 printk('<0>function open!nn'); 33 return 0; 34 } 35 36 static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 37 { 38 return 0; 39 } 40 41 static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) 42 { 43 printk('<0>function write!nn'); 44 return 1; 45 } 46 47 static int key_release(struct inode *inode, struct file *filp) 48 { 49 printk('<0>function write!nn'); 50 return 0; 51 } 52 53 static int key_ioctl(struct inode *inode,struct file *flip,unsigned int command,unsigned long arg) 54 { 55 printk('<0>function ioctl!nn'); 56 return 0; 57 } 58 59 static struct file_operations key_fops = { 60 .owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */ 61 .open = key_open, 62 .read = key_read, 63 .write = key_write, 64 .release= key_release, 65 .ioctl = key_ioctl, 66 }; 67 68 69 static int __init key_irq_init(void) 70 { 71 printk('<0>nHello,this is %s module!nn',Driver_NAME); 72 //register and mknod 73 major = register_chrdev(0,Driver_NAME,&key_fops); 74 drv_class = class_create(THIS_MODULE,Driver_NAME); 75 drv_class_dev = device_create(drv_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME); /*/dev/key_query*/ 76 77 //kmalloc分配內存實驗1 78 // buf1 申請一個100字節的物理內存,若無內存,則休眠 79 buf1 = (unsigned char *)kmalloc(100,GFP_KERNEL); 80 if(buf1 == NULL){ 81 printk('<0>buf1 kmalloc error !nn'); 82 return -1; 83 } 84 printk('<0>buf1 kmalloc mem addr = %x nn',buf1); 85 memset(buf1,0,100); //將所分配到的內存清理 86 strcpy(buf1,'<<------buf1 Kmalloc Mem OK!------>>');//向內存中寫入數據 87 printk('<0>BUF1: %snn',buf1); //從內存中讀出數據 88 89 //get_zeroed_page申請分配整頁內存,并且初始化為0 90 buf2 = (unsigned char *)get_zeroed_page(GFP_KERNEL); 91 if(buf2 == NULL){ 92 printk('<0>buf2 get_zeroed_page error !nn'); 93 return -1; 94 } 95 printk('<0>buf2 get_zeroed_page addr = %x nn',buf2); 96 strcpy(buf2,'<<------buf2 get_zeroed_page OK!------>>');//向內存中寫入數據 97 printk('<0>BUF2: %snn',buf2); 98 99 //Vmalloc 申請1000000個字節(1M)的空間 虛擬地址分配(零碎的物理地址空間) 100 buf3 = (unsigned char *)vmalloc(1000000); 101 if(buf3 == NULL){ 102 printk('<0>buf3 vmalloc error !nn'); 103 return -1; 104 } 105 printk('<0>buf3 vmalloc addr = %x nn',buf3); 106 strcpy(buf3,'<<------buf3 vmalloc OK!------>>');//向內存中寫入數據 107 printk('<0>BUF3: %snn',buf3); 108 109 return 0; 110 } 111 112 static void __exit key_irq_exit(void) 113 { 114 printk('<0>nGoodbye,%s!nn',Driver_NAME); 115 116 unregister_chrdev(major,Driver_NAME); 117 device_unregister(drv_class_dev); 118 class_destroy(drv_class); 119 120 //釋放前面申請的內存 121 printk('<0>buf1 kfree addr = %x nn',buf1); 122 kfree(buf1);
上一篇:IMX257 LED驅動程序實現
下一篇:IMX257 linux設備驅動之Cdev結構
推薦閱讀最新更新時間:2025-06-07 23:40




設計資源 培訓 開發板 精華推薦
- 肖特激光玻璃助力實現核聚變領域又一個歷史性里程碑
- 英飛凌SEMPER? NOR閃存系列獲得ASIL-D功能安全認證
- 如何在開關模式電源中運用氮化鎵技術
- LGVL配合FreeType為可變字體設置字重-ESP32篇
- 使用樹莓派進行 ESP32 Jtag 調試
- ESP32怎么在SPIFFS里面存儲html,css,js文件,以及網頁和arduino的通訊
- ESP32 freeRTOS使用測試
- API調用小記(Touchdesigner和ESP32)
- 芯科科技Tech Talks技術培訓重磅回歸: 賦能物聯網創新,共筑智能互聯未來
- 關于ESP32/8266使用async-mqtt-client庫的一些基本介紹