使用内存映射的好处是可以加快我们应用与内核数据传送的速度,一般的调用需要在应用层申请空间,同时需要在内核申请空间,有内存映射就可以只申请一个空间就可以.
加载函数中申请空间
//分配内存
key_dev->virt_mem = kzalloc(PAGE_SIZE, GFP_KERNEL);
卸载函数中释放空间
kfree(key_dev->virt_mem);
fops中实现mmp函数
int key_drv_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long addr;
addr = virt_to_phys(key_dev->virt_mem);
//将当前内存映射到用户空间
//参数1--描述应用空间的mmap函数的需求(参数)
//参数2--应用空间的起始位置
//参数3--内核空间的内存的物理地址的页地址
//参数4--内存的大小
//参数5--权限
if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT,
PAGE_SIZE, vma->vm_page_prot)) {
printk(KERN_ERR "%s: io_remap_pfn_range failed\n",
__func__);
return -EAGAIN;
}
return 0;
}
phys_to_virt是将已经映射的物理内存的地址转换为虚拟地址
PAGE_SHIFT为12 向右移动实际为除以2^12 在内核中一页的地址个数为2^12个实际为4K空间(一个地址对应一个字节)
所以addr 右移动12位实际为其页地址
应用层实现mmp
//演示mmap的使用
//参数1---映射到用户进程空间的起始位置,一般填NULL, 由系统自动分配
//参数2--映射的长度
//参数3--映射之后的内存访问权限
/*
PROT_EXEC Pages may be executed.
PROT_READ Pages may be read.
PROT_WRITE Pages may be written.
PROT_NONE Pages may not be accessed.
*/
//参数4--是否私有或者共享--MAP_SHARED/MAP_PRIVATE
//参数5--文件描述符
//参数6--映射内核中内存的起始的偏移量,一般都是填0
//返回值--映射成功之后的用户空间的起始地址
paddr = (char *)mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(MAP_FAILED == paddr)
{
perror("mmap");
exit(1);
}
memcpy(paddr, test_data, strlen(test_data));
//测试是否成功
sleep(1);
memcpy(rbuf, paddr, 128);
printf("rbuf = %s\n", rbuf);
文件io中的ioctl和mmap接口
应用程序如何使用mmap:
#include <sys/mman.h>
//参数1---映射到用户进程空间的起始位置,一般填NULL, 由系统自动分配
//参数2--映射的长度
//参数3--映射之后的内存访问权限
/*
PROT_EXEC Pages may be executed.
PROT_READ Pages may be read.
PROT_WRITE Pages may be written.
PROT_NONE Pages may not be accessed.
*/
//参数4--是否私有或者共享--MAP_SHARED/MAP_PRIVATE
//参数5--文件描述符
//参数6--映射内核中内存的起始的偏移量,一般都是填0
//返回值--映射成功之后的用户空间的起始地址
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
-----------------------------------------------------