概念及作用:
应用层有这样的需求:我要映射一片空间,但这片空间不是随便的一片空间,他跟我当前要操作的设备有关联,
但我又不知道他的绝对物理地址,只是想能够获得一片与该设备相关的,偏移一定偏移量的区域,并映射到用户空间。
那么此后我在用户空间操作这片虚拟地址就相当于操作了与该设备相关的物理地址。类似的需求在lcd中常常会用到。
这里的回调机制与read(),write()的回调机制一样。
运用层mmap:
/*
* 函数功能:在进程的虚拟地址空间建立一个内存映射,映射到某个物理地址(该物理地址与当前操作的这个设备有某种关系)
* 输入参数:addr-- 指定映射的起始地址,如果写NULL,系统自动分配
* length-- 映射的长度
* prot--- 指定映射区域的访问权限,不能和open的打开模式冲突
* PROT_EXEC :可执行
* PROT_READ :可读
* PROT_WRITE :可写
* PROT_NONE :不可访问
* flags--- 指明了映射方式
* MAP_SHARED :共享方式,多个进程映射同一区域,应用层映射空间的修改会互相影响,
* msync或munmap函数调用时回写到内核空间内存。
* MAP_PRIVATE :私有方式,多个进程映射同一区域,内核中先拷贝同一区域再映射,
* 应用层的修改不会互相影响,不能回到回写到内核中拷贝前的内存。
* fd--- 文件描述符(普通文件或设备文件)
* offset-- 偏移量,必须页对齐。映射的开始地址= 文件首地址+偏移量 。偏移量大小必须是页大小的倍数
* 返回值: 成功返回映射的虚拟地址,失败返回(void *)-1 宏名为:MAP_FAILED,置错误码
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
使用注意事项:
1.偏移量也对其后,实际赋给mmap的就可能比用户预想地小,这时候用户预想地大小就得加大,否则会出错
2.映射得到的虚拟地址也是页对齐物理地址映射过来的,要得到非页对齐物理地址所对应的虚拟地址,就得加个某个数
实例:
驱动层mmap:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
...
}
内核为驱动编写者提供的一些函数:
使用注意事项:
remap_pfn_range()中的参数:物理地址是要左移12位的,这会导致运用层算便宜的时候只能按相对于页对齐物理地址的偏移,
对于非页对齐物理地址,其偏移可能得加某个数。
实例:
源码下载:
https://git.coding.net/xxgui1992/LinuxDriver-mmap.git