時間:晚上7點
地點:寢室中..
“小王,今天就不多話了,接著昨天沒講完的,不然連不起來了,都..”我催促著。
上節講到kmalloc()申請的記憶體若要被映射到使用者空間可以通過mem_map_reserve()設定為保留後進行。具體怎麼操作呢,給你一個模版吧:
// 核心模組載入函數int __init kmalloc_map_init(void){ ../申請裝置號,添加cedv結構體 buffer = kmalloc(BUF_SIZE, GFP_KERNEL); //申請buffer for(page = virt_to_page(buffer); page< virt_to_page(buffer+BUF_SIZE); page++) { mem_map_reserve(page); //置業為保留 }}//mmap()函數static int kmalloc_map_mmap(struct file *filp, struct vm_area_struct *vma){ unsigned long page, pos; unsigned long start = (unsigned long)vma->start; unsigned long size = (unsigned long)(vma->end - vma->start); printk(KERN_INFO, "mmaptest_mmap called\n"); if(size > BUF_SIZE) //使用者要映射的地區太大 return - EINVAL; pos = (unsigned long)buffer; while(size > 0) //映射buffer中的所有頁 { page = virt_to_phys((void *)pos); if(remap_page_range(start, page, PAGE_SIZE, PAGE_SHARRED)) return -EAGAIN; start += PAGE_SIZE; pos +=PAGE_SIZE; size -= PAGE_SIZE; } return 0;}
另外通常,IO記憶體被映射時需要是nocache的,這個時候應該對vma->vm_page_prot設定nocache標誌。如下:
static int xxx_nocache_mmap(struct file *filp, struct vm_area_struct *vma){ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); //賦nocache標誌 vma->vm_pgoff = ((u32)map_start >> PAGE_SHIFT); if(rempa_pfn_range(vma, vma->vm_start, vma->vm_pgoff, vma->vm_end - vm_start, vma->vm_page_prot)); return - EAGGIN; return 0;}
這段代碼中的pgprot_noncached()是一個宏,它實際上禁止了相關頁的cache和寫緩衝(write buffer),另外一個稍微少的一些限制的宏是:
#define pgprot_writecombine(prot) __pgprot(pgprot_val (prot) & –L_PTE_CACHEABLE); 它則沒有禁止寫緩衝
而除了rempa_pfn_range()外,在驅動程式中實現VMA的nopage()函數通常可以為裝置提供更加靈活的記憶體映射途徑。當發生缺頁時,nopage()會被核心自動調用,。這是因為,當發生缺頁異常時,系統會經過如下處理過程:
1)找到缺頁的虛擬位址所在的VMA 2)如果必要,分配中間頁目錄表和頁表
3)如果頁表項對應的物理頁表不存在,則調用這個VMA的nopage()方法,它返回物理頁面的頁描述符。
4)將物理頁面的地址填充到頁表中。
實現nopage後,使用者空間可以通過mremap()系統調用重新綁定映射區所綁定的地址,下面給出一個在裝置驅動中使用nopage()的典型範例:
static int xxx_mmap(struct file *filp, struct vm_area_struct *vma);{ unsigned long offset = vma->vm_pgoff << PAGE_OFFSET; if(offset >= _ _pa(high_memory) || (filp->flags &O_SYNC)) vma->vm_flags |=VM_IO; vma->vm_ops = &xxx_nopage_vm_ops; xxx_vma_open(vma); return 0;}struct page *xxx_vma_nopage(struct vm_area_struct *vma, unsigned long address, int *type){ struct page *pageptr; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long physaddr = address - vma->vm_start + offset; //實體記憶體 unsigned long pageframe = physaddr >> PAGE_SHIFT; //頁幀號 if(!pfn_valid(pageframe)) //頁幀號有效 return NOPAGE_SIGBUS; pageptr = pfn_to_page(pageframe); //頁幀號->頁描述符 get_page(pageptr); //獲得頁,增加頁的使用計數 if(type) *type = VM_FAULT_MINOR; return pageptr; //返回頁描述符
}
上述函數對常規記憶體進行映射,返回一個頁描述符,可用於擴大或縮小映射的記憶體地區,由此可見,nopage()和remap_pfn_range()一個較大的區別在於remap_pfn
_range()一般用於裝置記憶體映射,而nopage()還可以用於RAM映射。
小王,這節和前邊一節是在一起看的,我也可以喘口氣歇歇了,你慢慢看吧,就不煩你了,晚上吃飯叫上我哈..