平時我們寫Linux驅(qū)動和用戶空間交互時,都是通過 因為用戶空間是不能直接內(nèi)核空間數(shù)據(jù)的,他們映射的是不同的地址空間,只能先將數(shù)據(jù)拷貝過來,然后再操作。 如果用戶空間需要傳幾MB的數(shù)據(jù)給內(nèi)核,那么原來的拷貝方式顯然效率特別低,也不太現(xiàn)實,那怎么辦呢? 想想,之所以要拷貝是因為用戶空間不能直接訪問內(nèi)核空間,那如果可以直接訪問內(nèi)核空間的buffer,是不是就解決了。 簡單來說,就是讓一塊物理內(nèi)存擁有兩份映射,即擁有兩個虛擬地址,一個在內(nèi)核空間,一個在用戶空間。關系如下: 通過 應用層代碼很簡單,主要就是通過 登錄后復制 第二參數(shù)是要映射的內(nèi)存空間的大小。 第三個參數(shù) 第四個參數(shù)可填 驅(qū)動層主要是實現(xiàn) 登錄后復制 驅(qū)動主要步驟: 1、使用 2、實現(xiàn)mmap函數(shù) 登錄后復制 1、通過 2、設置屬性:不使用cache,使用buffer 3、映射:通過 當應用層調(diào)用前言
copy_from_user把用戶空間傳過來的數(shù)據(jù)進行拷貝,為什么要這么做呢?mmap映射就可以實現(xiàn)。應用層
mmap系統(tǒng)調(diào)用進行映射,然后就可以對返回的地址進行操作。char * buf;
/* 1. 打開文件 */
fd = open("/dev/hello", O_RDWR);
if (fd == -1)
{
printf("can not open file /dev/hello\n");
return -1;
}
/* 2. mmap
* MAP_SHARED : 多個APP都調(diào)用mmap映射同一塊內(nèi)存時, 對內(nèi)存的修改大家都可以看到。
* 就是說多個APP、驅(qū)動程序?qū)嶋H上訪問的都是同一塊內(nèi)存
* MAP_PRIVATE : 創(chuàng)建一個copy on write的私有映射。
* 當APP對該內(nèi)存進行修改時,其他程序是看不到這些修改的。
* 就是當APP寫內(nèi)存時, 內(nèi)核會先創(chuàng)建一個拷貝給這個APP,
* 這個拷貝是這個APP私有的, 其他APP、驅(qū)動無法訪問。
*/
buf = mmap(NULL, 1024*8, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
mmap的第一個參數(shù)是想要映射的起始地址,通常設置為NULL,表示由內(nèi)核來決定該起始地址。PROT_READ | PROT_WRITE表示映射后的空間是可讀可寫的。MAP_SHARED或MAP_PRIVATE:
MAP_SHARED:多個APP都調(diào)用mmap映射同一塊內(nèi)存時, 對內(nèi)存的修改大家都可以看到。就是說多個APP、驅(qū)動程序?qū)嶋H上訪問的都是同一塊內(nèi)存。MAP_PRIVATE:創(chuàng)建一個copy on write的私有映射。當APP對該內(nèi)存進行修改時,其他程序是看不到這些修改的。就是當APP寫內(nèi)存時, 內(nèi)核會先創(chuàng)建一個拷貝給這個APP,這個拷貝是這個APP私有的, 其他APP、驅(qū)動無法訪問。驅(qū)動層
mmap接口,而mmap接口的實現(xiàn),主要是調(diào)用了remap_pfn_range函數(shù),函數(shù)原型如下:int remap_pfn_range(
struct vm_area_struct *vma,
unsigned long addr,
unsigned long pfn,
unsigned long size,
pgprot_t prot);
vma:描述一片映射區(qū)域的結(jié)構(gòu)體指針addr:要映射的虛擬地址起始地址pfn:物理內(nèi)存所對應的頁框號,就是將物理地址除以頁大小得到的值size:映射的大小prot:該內(nèi)存區(qū)域的訪問權(quán)限kmalloc或者kzalloc函數(shù)分配一塊內(nèi)存kernel_buf,因為這樣分配的內(nèi)存物理地址是連續(xù)的,mmap后應用層會對這一個基地址去訪問這塊內(nèi)存。static int hello_drv_mmap(struct file *file, struct vm_area_struct *vma)
{
/* 獲得物理地址 */
unsigned long phy = virt_to_phys(kernel_buf);//kernel_buf是內(nèi)核空間分配的一塊虛擬地址空間
/* 設置屬性:cache, buffer*/
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
/* map */
if(remap_pfn_range(vma, vma->vm_start, phy>>PAGE_SHFIT,
vma->vm_end - vma->start, vma->vm_page_prot)){
printk("mmap remap_pfn_range failed\n");
return -ENOBUFS;
}
return 0;
}
static struct file_operations my_fops = {
.mmap = hello_drv_mmap,
};
virt_to_phys將虛擬地址轉(zhuǎn)為物理地址,這里的kernel_buf是內(nèi)核空間的一塊虛擬地址空間remap_pfn_range函數(shù)映射,phy>>PAGE_SHIFT其實就是按page映射,除了這個參數(shù),其他的起始地址、大小和權(quán)限都可以由用戶在系統(tǒng)調(diào)用函數(shù)中指定。mmap后,就會調(diào)用到驅(qū)動層的mmap函數(shù),最終應用層的虛擬地址和驅(qū)動中的物理地址就建立了映射關系,應用層也就可以直接訪問驅(qū)動的buffer了。
以上就是Linux驅(qū)動IO篇——mmap操作的詳細內(nèi)容,更多請關注www.92cms.cn其它相關文章!






