arm驱动linux设备地址映射到用户空间
运行结果:
mypid is 28529
********打印映射区域内容;和mapChar*******
mapChar = this is a just test temp file
********通过mapChar将数据写入映射区域*******
mapChar = writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,
********checkChar验证*******
mapCharaddress = 0x7f356eaaa000
checkCharaddress = 0x7f356eaa9000
checkChar = writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,
Tip:一个进程的内存映象由下面几部分组成:程序代码、数据、BSS和栈区域,以及内存映射的区域。一个进程的内存区域可以通过查看/proc/pid/maps
三、给驱动设备添加mmap虚拟内存映射
内核函数一)1、 驱动中的mmap(内核空间):
int(*mmap)(struct file *,struct vm_area_struct *);
2、在struct file_operations中与mmap接口函数关联
static struct file_operations module_drv_fops = {
//............
.mmap = memdev_mmap,
//...............
}
结构体一)3、struct vm_area_struct(VMA)结构体如下
struct vm_area_struct {
struct mm_struct * vm_mm; /* The address space we belong to. */
unsigned long vm_start; /* Our start address within vm_mm. */
unsigned long vm_end; /* The first byte after our end address within vm_mm. */
/* linked list of VM areas per task, sorted by address */
struct vm_area_struct *vm_next;
pgprot_t vm_page_prot; /* Access permissions of this VMA. */
unsigned long vm_flags; /* Flags, listed below. */
struct rb_node vm_rb;
struct vm_operations_struct * vm_ops;
/* Information about our backing store: */
unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */
struct file * vm_file; /* File we map to (can be NULL). */
void * vm_private_data; /* was vm_pte (shared mem) */
unsigned long vm_truncate_count;/* truncate_count or restart_addr */
//..................
};
4、struct vm_area_struct(VMA)结构体flag参数
VM_IO将该VMA标记为内存映射的IO区域,VM_IO会阻止系统将该区域包含在进程的存放转存(core dump )中
VM_RESERVED标志内存区域不能被换出
内核函数二)5、内核mmap中创建页表函数:remap_pfn_range();
作用用“addr ~ addr + size之间的虚拟地址”构造页表,参考《虚拟内存共享原理图b》和《虚拟内存共享原理图c》
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
unsigned long pfn, unsigned long size, pgprot_t prot)
a)参数
1) vma: 虚拟内存区域指针(默认使用vma)
2)addr: 虚拟地址的起始值(默认使用vma->vm_start)
3)pfn:总的来说(pfn = virt_to_phys(void *mem))>>PAGE_SHIFT(常用);或使用默认方式vma->vm_pgoff)
推理:pfn是虚拟地址应该映射到的物理地址所在的物理页帧号就是物理地址右移PAGE_SHIFT位,若PAGE_SIZE为4k则PAGE_SHIFT为12(2的12次方为4k),因此PAGE_SIZE为1
4)size: 要映射的区域的大小。(默认使用vma->vm_end - vma->vm_start)
5)prot: VMA的保护属性。(默认使用vma->vm_page_prot)
模板一)6、mmap驱动模板
static int memdev_mmap(struct file*file, struct vm_area_struct *vma){
struct VirtualDisk *devp = file->private_data; /*鑾峰緱璁惧缁撴瀯浣撴寚閽?/
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED;
if (remap_pfn_range(vma,vma->vm_start,virt_to_phys(devp->mem)>>PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
file_oprations中添加或修改.mmap
static struct file_operations module_drv_fops = {
//............
.mmap = memdev_mmap,
//...............
}
实例二)7、完整实例
a)驱动部分
//“module_drv”,"module_","module"
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include //在内核和用户空间中移动数据的函数
#include
#include
#include
#include
#define VIRTUALDISK_SIZE 0x1000//4k
#define MEM_CLEAR 0x1
#define VIRTUALDISK_MAJOR 250
int VirtualDisk_major = VIRTUALDISK_MAJOR;
struct VirtualDisk{
struct cdev cdev;//详细看cdev机制
unsigned char mem[VIRTUALDISK_SIZE ];
long count; /*记录设备目前被多少设备打开*/
};
static struct class *module_class;
static struct class_device *module_class_dev;
struct VirtualDisk *VirtualDiskp;
static int module_drv_open(struct inode *inode, struct file *file)
{
printk("module_dev read\n");
file->private_data = VirtualDiskp;
VirtualDiskp->count++; /*增加设备打开次数*/
return 0;
}
static int module_drv_release(struct inode *inode, struct file *file)
{
printk("module_dev release\n");
VirtualDiskp->count--; /*减少设备打开次数*/
return 0;
}
/*seek文件定位函数:seek()函数对文件定位的起始地址可以是文件开头(SEEK_SET,0)、当前位置(SEEK_CUR,1)、文件尾(SEEK_END,2)*/
static loff_t module_drv_llseek(struct file *file, loff_t offset, int origin){
loff_t ret = 0;/*返回的位置偏移*/
arm驱动linux设备地址映射用户空 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)