Linux内核开发之内存与I/O访问(四)

来源:互联网 发布:微信为什么要封淘宝客 编辑:程序博客网 时间:2024/06/06 03:29

时间:晚上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映射。

 

小王,这节和前边一节是在一起看的,我也可以喘口气歇歇了,你慢慢看吧,就不烦你了,晚上吃饭叫上我哈..


原创粉丝点击