一.kmalloc與vmallco
在設備驅動程序或者內核模塊中動態開辟內存,不是用malloc,而是kmalloc ,vmalloc,釋放內存用的是kfree,vfree,kmalloc函數返回的是虛擬地址(線性地址). kmalloc特殊之處在於它分配的內存是物理上連續的,這對於要進行DMA的設備十分重要. 而用vmalloc分配的內存只是線性地址連續,物理地址不一定連續,不能直接用於DMA。vmalloc函數的工作方式類似於kmalloc,只不過前者分配的內存虛擬地址是連續的,而物理地址則無需連 續。通過vmalloc獲得的頁必須一個一個地進行映射,效率不高, 因此,只在不得已(一般是為了獲得大塊內存)時使用。vmalloc函數返回一個指針,指向邏輯上連續的一塊內存區,其大小至少為size。在發生錯誤 時,函數返回NULL。vmalloc可能睡眠,因此,不能從中斷上下文中進行調用,也不能從其它不允許阻塞的情況下調用。要釋放通過vmalloc所獲 得的內存,應使用vfree函數
vmalloc和kmalloc的分配內存的特點大概如下:
區別大概可總結為:
1,vmalloc分配的一般為高端內存,只有當內存不夠的時候才分配低端內存;kmallco從低端內存分配。
2,vmalloc分配的物理地址一般不連續,而kmalloc分配的地址連續,兩者分配的虛擬地址都是連續的;
3,vmalloc分配的一般為大塊內存,而kmaooc一般分配的為小塊內存,(一般不超過128k);
二.DMA工作
為了減少CPU對快速設備入出的操作,可以通過把這 批數據的傳輸過程交由一塊專用的接口卡(DMA接口)來控制,讓DMA卡代替CPU控制在快速設備與主存儲器之間直接傳輸數據,其大概工作的機制是在DMA模式下,CPU只須向DMA控制器下達指令,讓DMA控制器來處理數據的傳送,數據傳送完畢再把信息反饋給CPU,這樣就很大程度上減輕了 CPU資源占有率。
工作示意圖大概如下:
以下是測試的代碼:
1 #include <linux/init.h> 2 #include <linux/thread_info.h> 3 #include <linux/module.h> 4 #include <linux/sched.h> 5 #include <linux/errno.h> 6 #include <linux/kernel.h> 7 #include <linux/module.h> 8 #include <linux/slab.h> 9 #include <linux/input.h> 10 #include <linux/init.h> 11 #include <linux/serio.h> 12 #include <linux/delay.h> 13 #include <linux/clk.h> 14 #include <linux/miscdevice.h> 15 #include <linux/io.h> 16 #include <linux/ioport.h> 17 #include <linux/vmalloc.h> 18 #include <linux/dma-mapping.h> 19 #include <linux/export.h> 20 #include <linux/gfp.h> 21 22 #include <asm/dma-mapping.h> 23 #include <asm/uaccess.h> 24 25 #include <linux/gpio.h> 26 #include <mach/gpio.h> 27 #include <plat/gpio-cfg.h> 28 29 MODULE_LICENSE("GPL"); 30 MODULE_AUTHOR("bunfly"); 31 32 33 unsigned long vmalloc_to_pfn(const void *vmalloc_addr); 34 int test_init() 35 { 36 int ret = 0; 37 unsigned char *vmalloc_virt, *normal_virt, *phys; 38 unsigned long pfn; 39 40 printk("KERNEL SPACE: init\n"); 41 42 printk("KERNEL SPACE: read from d0003000: %s\n", 0xd0003000); 43 //讀出d0003000虛擬地址里面的數據 44 45 normal_virt = kmalloc(40, GFP_KERNEL); 46 //kmalloc分配一段虛擬地址,大小,睡眠不可中斷 47 48 phys = virt_to_phys(normal_virt); 49 printk("KMAKLLOC: kmalloc virt: %p <==> phys: %p\n", normal_virt, phys); 50 51 kfree(normal_virt); 52 53 //分配頁 54 //-------------------------------------- 55 vmalloc_virt = vmalloc(500); 56 //vmalloc分配內存 57 58 pfn = vmalloc_to_pfn(vmalloc_virt); 59 //頁分配 60 phys = (pfn << 12) | ((unsigned long)vmalloc_virt & 0xfff); 61 //將頁地址轉換成物理地址 62 normal_virt = phys_to_virt(phys); 63 //再將物理地址轉換成虛擬地址 64 printk("VMALLOC: vmalloc_virt = %p, normal_virt = %p\n", vmalloc_virt, normal_virt); 65 66 //dma分配 67 normal_virt = dma_alloc_coherent(NULL, 1024, &phys, GFP_KERNEL); 68 printk("DMA: vmalloc_virt = %p, normal_virt = %p\n", vmalloc_virt,phys); 69 dma_free_coherent(NULL, 1024, normal_virt, phys); 70 71 vfree(vmalloc_virt); 72 return 0; 73 } 74 75 void test_exit() 76 { 77 printk("KERNEL SPACE: exit\n"); 78 } 79 80 module_init(test_init); 81 module_exit(test_exit); 82 ~ ~ ~
首先,在50003000地址放入數據,然后在d0003000的物理地址中讀出數據:
查看50003000地址中的數據:
啟動板子,插入模塊,運行結果:
可以看到kmalloc的物理地址是低端內存的,(從40000000開始),而vmalloc分配的物理地址是高端內存,而通過 DMA內存的也是低端一點的。
補充說明:kmalloc函數原型:
static __always_inline void *kmalloc(size_t size, gfp_t flags)
其中falgs有
主要的要分配兩種:一般都是
GFP_KERNLE;內核使用內存--------可以睡眠
GFP_USER; 用戶空間內存;
GFP_ATOMIC; 原子分配 ---------不可以睡眠
內核編程的法則:
中斷上下文不可以睡眠(可以用is_inetrruput()函數查看是否是中斷處理調用函數,返回1為真)