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用于描述一个虚拟内存区域。结构体如下:
其中vm_ops成员指向这个VMA的操作集,结构体定义如下:
“没事的,小涛哥,其实说真的,不是我安慰你哈,从开始我什么都不懂,到现在我也算个入门级的高手了,都是你一手带过来的,我已经对你推崇备至了,你就放心吧,会的你尽情教,不会的,你慢慢说,我都听你的”小王善解人意的说。
“嗯,你真好,看来我没看错你,那好,继续课程”。。。
一般情况下,用户空间是不可能也不应该直接访问设备的,但是设备驱动程序可实现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()调用设置为保留后进行,具体怎么操作,咱们下集继续。
相关文章推荐
- Linux内核开发之内存与I/O访问(一)
- Linux内核开发之内存与I/O访问(六)
- Linux内核开发之内存与I/O访问(二)
- Linux内核开发之内存与I/O访问(二)
- Linux内核开发之内存与I/O访问(一)
- Linux内核开发之内存与I/O访问(二)
- Linux内核开发之内存与I/O访问(三)
- Linux内核开发之内存与I/O访问(五)
- Linux内核开发之内存与I/O访问(一)
- Linux内核开发之内存与I/O访问(二)
- Linux内核开发之内存与I/O访问(四)
- Linux内核开发之内存与I/O访问(五)
- 【Linux开发】GCC 4.8及以上支持内存非法访问检查
- 从 Linux 内核访问用户空间内存
- Linux内核内存管理-内存访问与缺页中断【转】
- linux驱动开发--copy_to_user 、copy_from_user函数实现内核空间数据与用户空间数据的相互访问
- 从 Linux 内核访问用户空间内存
- 从 Linux 内核访问用户空间内存
- [初级知识]linux内核开发中基本内存分配与申请
- 从 Linux 内核访问用户空间内存