mmap将一个文件或者其它对象映射进内存,使得应用层可以直接读取到驱动层的数据,无需通过copy_to_user函数
可以用于像LCD这样的外设, 需要读写大量数据的
一、应用层
mmap用法:
- 用open系统调用打开文件, 并返回描述符fd.
- 用mmap建立内存映射, 并返回映射首地址指针start.
- 对映射(文件)进行各种操作, 显示(printf), 修改(strcpy、memncpy、sprintf、直接修改等).
- 用munmap(void *start, size_t lenght)关闭内存映射.
- 用close系统调用关闭文件fd.
mmap函数:
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
参数 | 含义 |
---|---|
addr | 指向欲映射的内存起始地址,通常设为 NULL, 代表让系统自动选定地址,映射成功后返回该地址。 |
length | 代表将文件中多大的部分映射到内存。 |
prot | 映射区域的保护方式。可以为以下几种方式的组合: PROT_EXEC 映射区域可被执行 PROT_READ 映射区域可被读取 PROT_WRITE 映射区域可被写入 PROT_NONE 映射区域不能存取 |
flags | 影响映射区域的各种特性。 在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。 |
fd |
要映射到内存中的文件描述符。 如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。 有些系统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件, 然后对该文件进行映射,可以同样达到匿名内存映射的效果。 |
offset |
文件映射的偏移量,通常设置为0, 代表从文件最前方开始对应,offset必须是分页大小的整数倍。 |
内存映射之后对内存进行读写,可以用write、read,但是效率低,可以直接对mmap函数返回的地址进行操作
使用 strcpy 等字符串处理函数即可
应用程序示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#define MMSIZE 128
int main(int argc, char *argv[])
{
int len;
char wr_buf[] = "hello mmap";
char read_buf[1024];
char *start;
...
...
/*open*/
int fd;
fd = open(argv[1], O_RDWR);
if(fd < 0){
printf("open failed\n");
return -2;
}
printf("pid = %d\n", getpid());
/* 将文件映射到进程的虚拟内存空间 */
start = mmap(NULL, MMSIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
printf("mmap address = 0x%x\n", start);
/* 写数据 */
strcpy(start, wr_buf);
/* 读数据 */
strcpy(read_buf, start);
// read(fd, read_buf, MMSIZE);
printf("new data = %s\n", start);
printf("old data = %s\n", read_buf);
while(1){
sleep(5);
}
munmap(start, MMSIZE);
close(fd);
return 0;
}
二、驱动层
- 首先,驱动程序先分配好一段内存
- 接着用户进程通过库函数mmap()来告诉内核要将多大的内存映射到内核空间
- 内核经过一系列函数调用后调用对应的驱动程序的file_operation中指定的xxx_mmap函数
- 在xxx_mmap函数中调用remap_pfn_range()来建立映射关系。
驱动程序示例:文章来源:https://www.toymoban.com/news/detail-573059.html
......
......
#define MM_SIZE 1024 * 8
#define MIN(x,y) (x < y ? x : y)
static char *buf = NULL;
static ssize_t hello_read (struct file *filp, char *buff, size_t size, loff_t *offset)
{
int err;
err = copy_to_user(buff, buf, MIN(MM_SIZE, size));
return MIN(MM_SIZE, size);
}
static int hello_mmap (struct file *filp, struct vm_area_struct *vma)
{
/*设置属性: cache,buffer */
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
if(remap_pfn_range(vma,//虚拟内存区域,即设备地址将要映射到这里
vma->vm_start,//虚拟空间的起始地址
virt_to_phys(buf)>>PAGE_SHIFT,//与物理内存对应的页帧号,物理地址右移12位
vma->vm_end - vma->vm_start,//映射区域大小,一般是页大小的整数倍
vma->vm_page_prot))//保护属性,
{
return -EAGAIN;
}
return 0;
}
/*定义 file_operations 结构体*/
static const struct file_operations hello_drv = {
.read = hello_read,
.open = hello_open,
.mmap = hello_mmap,
......
......
};
/*入口函数*/
static int hello_init(void)
{
//内核申请内存只能按页申请,申请该内存以便后面把它当作虚拟设备
buf = kmalloc(MM_SIZE, GFP_KERNEL);
strcpy(buf, "abc"); //先放入一些数据
......
......
return 0;
}
/*退出函数*/
static void hello_exit(void)
{
kfree(buf);
......
......
}
......
......
三、测试
直接口述吧
当mmap的参数为MAP_SHARED时,使用strcpy和read读取的数据和写入的相同;
当mmap的参数为MAP_PRIVATE时,使用strcpy读取的数据和写入的相同,read的数据和写入的不同(因为read到的是原内存的数据,MAP_PRIVATE标志会使得在写入内存数据时,内核会自动把内存空间拷贝一份,从而read到的是原来的内存,而写入的数据写入到了拷贝的新内存中)
文章来源地址https://www.toymoban.com/news/detail-573059.html
到了这里,关于【IMX6ULL驱动开发学习】19.mmap内存映射的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!