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

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

2012-12-11 17:46 211 查看
“小王,不瞒你说,我现在是悲喜交加啊,悲的是:这最后一章,我讲的是胆颤心惊(以前自己都没学好,现在也算还账了),喜的是每讲一张,我知道离结束就近了一点,赶快把这个东西过掉,进入下一环节,那又是我牛皮吹破天的时代了”看着小王期盼和怀疑的眼神,我,昔日的风采也不见了。
“没事的,小涛哥,其实说真的,不是我安慰你哈,从开始我什么都不懂,到现在我也算个入门级的高手了,都是你一手带过来的,我已经对你推崇备至了,你就放心吧,会的你尽情教,不会的,你慢慢说,我都听你的”小王善解人意的说。
“嗯,你真好,看来我没看错你,那好,继续课程”。。。
一般情况下,用户空间是不可能也不应该直接访问设备的,但是设备驱动程序可实现mmap()函数,这个函数可使得用户空间能直接访问设备的物理地址。实际上,mmap()S实现了这样的一个映射过程,它将用户空间的一段内存与设备内存关联,当用户访问用户空间的这段地址范围时,实际上会转化为对设备的访问。
mmp()必须以PAGE_SIZE为单位进行映射,实际上,内存只能以页为单位进行映射,若要映射非PAGE_SIZE整数倍的地址范围,要先进行页对齐,强行以PAGE
_SIZE的倍数大小进行映射。驱动中mmp()函数原型如下:int(*mmp)(structfile*,structvm_area_struct*);它实现的机制是建立页表,并填充VMA结构体中
vm_operations_struct指针,vm_area_struct用于描述一个虚拟内存区域。结构体如下:
structvm_area_struct{
structmm_struct*vm_mm;//所处的地址空间
unsignedlongvm_start;//开始虚拟地址
unsignedlongvm_end;//结束虚拟地址

structvm_area_struct*vm_next;

pgprot_tvm_page_prot;//访问权限
unsignedlongvm_flags;//标志
...
structvm_operations_struct*vm_ops;//操作VMA的函数集指针
unsignedlongvm_pgoff;//偏移(页帧号)
structfile*vm_file;
void*vm_private_data;
...
};

其中vm_ops成员指向这个VMA的操作集,结构体定义如下:

structvm_operations_struct{
void(*open)(structvm_area_struct*area);
void(*close)(structvm_area_struct*area);
structpage*(*nopage)(structvm_area_struct*area,unsignedlongaddress,int*type);
int(*populate)(structvm_area_struct*area,unsignedlongaddress,unsignedlonglen,pgprot_tprot,unsignedlongpgoff,intnonblock);
...
};
在内核生成一个VMA后,它就会调用该VMA的open()函数。
而驱动中的mmap()函数将在用户进行mmap()系统调用时最终被调用,当用户调用mmap()时候内核会进行如下处理:
1)在进程的虚拟空间查找一块VMA.
2)将这块VMA进行映射。
3)如果设备驱动程序或文件系统的file_operations定义了mmap()操作,则调用它。
4)将这个VMA插入到进程的VMA链表中。
file_operations中mmap()函数的第一个参数就是步骤1中找的VMA.由mmap()系统调用映射的内存可由munmap()解除映射。这个函数原型如下:
intmunmap(caddr_taddr,size_tlen);
但是,需要注意的是:当用户进行mmap()系统调用后,尽管VMA在设备驱动文件操作结构体的mmap()被调用前就已经产生,内核却不会调用VMA的open
函数,通常需要在驱动的mmap()函数中先上调用vma->vm_ops->open().为了说明问题,给出一个vm_operations_struct操作范例:

staticintxxx_mmp(structfile*filp,structvm_area_struct*vma)
{
if(remap_pfn_range(vma,vma->vm_start,vm->vm_pgoff,vma->vm_end-vma->start,vma->vm_page_prot))//建立页表
return-EAGAIN;
vma->vm_ops=&xxx_remap_vm_ops;
xxx_vma_open(vma);
return0;
}
voidxxx_vma_open(structvm_area_struct*vma)//VMA打开函数
{
...
printk(KERN_NOTICE"xxxVMAopen,virt%lx,phys%1x\n",vma->vm_start,vma->vm_pgoff《PAGE_SHIFT);
}
voidxxx_vma_close(structvm_area_struct*vma)//VMA关闭函数
{
...
printk(KERN_NOTICE"xxxVMAclose.\n");
}
staticstructvm_operation_structxxx_remap_vm_ops=//VM操作结构体
{
.open=xxx_vma_open,
.close=xxx_vma_close,
...
}
在这段代码中调用的remap_pfn_range()创建页表。我们前边说过在内核空间用kmalloc申请内存,这部分内存如果要映射到用户空间可以通过mem_map
_reserve()调用设置为保留后进行,具体怎么操作,咱们下集继续。

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: