11.mmap
概念¶
初始只需记得会在虚拟的内存空间和实际的空间建立映射关系,我们需要做的就是怎么把实际的物理空间映射出去。你想映射某个内核 buffer,你需要得到它的物理地址,这得由你提供。
驱动程序要做的事情有 3 点:
1、确定物理地址
2、确定属性:是否使用 cache、buffer
3、建立映射关系
驱动编程¶
在初始化的时候分配驱动的内存空间
static char *kernel_buf;
static int bufsiz = 1024*8;
static int __init hello_drv_init(void)
{
int ret;
kernel_buf = kmalloc(bufsiz, GFP_KERNEL);
strcpy(kernel_buf, "old");
/*设备注册*/
return 0;
}
然后编写mmap函数
static int hello_drv_mmap(struct file *file, struct vm_area_struct *vma)
{
/* 获得物理地址 */
unsigned long phy = virt_to_phys(kernel_buf);
/* 设置属性: cache, buffer */
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
/* map */
if (remap_pfn_range(vma, vma->vm_start, phy >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
printk("mmap remap_pfn_range failed\n");
return -ENOBUFS;
}
return 0;
}
/*
* 设备操作函数结构体
*/
static struct file_operations hello_drv_fops = {
.owner = THIS_MODULE,
.open = hello_drv_open,
.read = hello_drv_read,
.write = hello_drv_write,
.release = hello_drv_release,
.mmap = hello_drv_mmap,
};
注销的时候释放内存空间
static void __exit hello_drv_exit(void)
{
printk("%s %s line %d\r\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(hello.major, HELLO_DEV_NAME);
//删除类、删除设备
device_destroy(hello.class, MKDEV(hello.major, 0));
class_destroy(hello.class);
kfree(kernel_buf);
}
测试代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
/*
* ./hello_drv_test
*/
int main(int argc, char **argv)
{
int fd;
char *buf;
int len;
char str[1024];
/* 1. 打开文件 */
fd = open("/dev/hello", O_RDWR);
if (fd == -1)
{
printf("can not open file /dev/hello\n");
return -1;
}
/* 2. mmap
* MAP_SHARED : 多个APP都调用mmap映射同一块内存时, 对内存的修改大家都可以看到。
* 就是说多个APP、驱动程序实际上访问的都是同一块内存
* MAP_PRIVATE : 创建一个copy on write的私有映射。
* 当APP对该内存进行修改时,其他程序是看不到这些修改的。
* 就是当APP写内存时, 内核会先创建一个拷贝给这个APP,
* 这个拷贝是这个APP私有的, 其他APP、驱动无法访问。
*/
buf = mmap(NULL, 1024*8, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED)
{
printf("can not mmap file /dev/hello\n");
return -1;
}
printf("mmap address = 0x%x\n", buf);
printf("buf origin data = %s\n", buf); /* old */
/* 3. write */
strcpy(buf, "new");
/* 4. read & compare */
/* 对于MAP_SHARED映射: str = "new"
* 对于MAP_PRIVATE映射: str = "old"
*/
read(fd, str, 1024);
if (strcmp(buf, str) == 0)
{
/* 对于MAP_SHARED映射,APP写的数据驱动可见
* APP和驱动访问的是同一个内存块
*/
printf("compare ok!\n");
}
else
{
/* 对于MAP_PRIVATE映射,APP写数据时, 是写入原来内存块的"拷贝"
*/
printf("compare err!\n");
printf("str = %s!\n", str); /* old */
printf("buf = %s!\n", buf); /* new */
}
while (1)
{
sleep(10); /* cat /proc/pid/maps */
}
munmap(buf, 1024*8);
close(fd);
return 0;
}