|
時(shí)間:晚上7點(diǎn) 地點(diǎn):寢室中.. “小王,今天就不多話了,接著昨天沒講完的,不然連不起來了,都..”我催促著。 上節(jié)講到kmalloc()申請的內(nèi)存若要被映射到用戶空間可以通過mem_map_reserve()設(shè)置為保留后進(jìn)行。具體怎么操作呢,給你一個(gè)模版吧: // 內(nèi)核模塊加載函數(shù) int __init kmalloc_map_init(void) { ../申請?jiān)O(shè)備號,添加cedv結(jié)構(gòu)體 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()函數(shù) 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內(nèi)存被映射時(shí)需要是nocache的,這個(gè)時(shí)候應(yīng)該對vma->vm_page_prot設(shè)置nocache標(biāo)志。如下: static int xxx_nocache_mmap(struct file *filp, struct vm_area_struct *vma) { vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); //賦nocache標(biāo)志 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()是一個(gè)宏,它實(shí)際上禁止了相關(guān)頁的cache和寫緩沖(write buffer),另外一個(gè)稍微少的一些限制的宏是: #define pgprot_writecombine(prot) __pgprot(pgprot_val (prot) & –L_PTE_CACHEABLE); 它則沒有禁止寫緩沖 而除了rempa_pfn_range()外,在驅(qū)動(dòng)程序中實(shí)現(xiàn)VMA的nopage()函數(shù)通??梢詾樵O(shè)備提供更加靈活的內(nèi)存映射途徑。當(dāng)發(fā)生缺頁時(shí),nopage()會(huì)被內(nèi)核自動(dòng)調(diào)用,。這是因?yàn)?當(dāng)發(fā)生缺頁異常時(shí),系統(tǒng)會(huì)經(jīng)過如下處理過程: 1)找到缺頁的虛擬地址所在的VMA 2)如果必要,分配中間頁目錄表和頁表 3)如果頁表項(xiàng)對應(yīng)的物理頁表不存在,則調(diào)用這個(gè)VMA的nopage()方法,它返回物理頁面的頁描述符。 4)將物理頁面的地址填充到頁表中。 實(shí)現(xiàn)nopage后,用戶空間可以通過mremap()系統(tǒng)調(diào)用重新綁定映射區(qū)所綁定的地址,下面給出一個(gè)在設(shè)備驅(qū)動(dòng)中使用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; //物理內(nèi)存 unsigned long pageframe = physaddr >> PAGE_SHIFT; //頁幀號 if(!pfn_valid(pageframe)) //頁幀號有效 return NOPAGE_SIGBUS; pageptr = pfn_to_page(pageframe); //頁幀號->頁描述符 get_page(pageptr); //獲得頁,增加頁的使用計(jì)數(shù) if(type) *type = VM_FAULT_MINOR; return pageptr; //返回頁描述符 } 上述函數(shù)對常規(guī)內(nèi)存進(jìn)行映射,返回一個(gè)頁描述符,可用于擴(kuò)大或縮小映射的內(nèi)存區(qū)域,由此可見,nopage()和remap_pfn_range()一個(gè)較大的區(qū)別在于remap_pfn _range()一般用于設(shè)備內(nèi)存映射,而nopage()還可以用于RAM映射。
小王,這節(jié)和前邊一節(jié)是在一起看的,我也可以喘口氣歇歇了,你慢慢看吧,就不煩你了,晚上吃飯叫上我哈.. |
|
|