時間:晚上7點 地點:寢室中.. “小王,今天就不多話了,接著昨天沒講完的,不然連不起來了,都..”我催促著。 上節(jié)講到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); //置業(yè)為保留 } } //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) //用戶要映射的區(qū)域太大 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()外,在驅動程序中實現(xiàn)VMA的nopage()函數通常可以為設備提供更加靈活的內存映射途徑。當發(fā)生缺頁時,nopage()會被內核自動調用,。這是因為,當發(fā)生缺頁異常時,系統(tǒng)會經過如下處理過程: 1)找到缺頁的虛擬地址所在的VMA 2)如果必要,分配中間頁目錄表和頁表 3)如果頁表項對應的物理頁表不存在,則調用這個VMA的nopage()方法,它返回物理頁面的頁描述符。 4)將物理頁面的地址填充到頁表中。 實現(xiàn)nopage后,用戶空間可以通過mremap()系統(tǒng)調用重新綁定映射區(qū)所綁定的地址,下面給出一個在設備驅動中使用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; //返回頁描述符 } 上述函數對常規(guī)內存進行映射,返回一個頁描述符,可用于擴大或縮小映射的內存區(qū)域,由此可見,nopage()和remap_pfn_range()一個較大的區(qū)別在于remap_pfn _range()一般用于設備內存映射,而nopage()還可以用于RAM映射。
小王,這節(jié)和前邊一節(jié)是在一起看的,我也可以喘口氣歇歇了,你慢慢看吧,就不煩你了,晚上吃飯叫上我哈.. |
|
來自: 看風景D人 > 《linux內核開發(fā)理論貼》