您的位置:首页 > 运维架构 > Linux

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

2012-12-11 17:46 387 查看
时间:晚上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映射。

小王,这节和前边一节是在一起看的,我也可以喘口气歇歇了,你慢慢看吧,就不烦你了,晚上吃饭叫上我哈..
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: