mmap与remap_pfn_range

参考资料:
 

remap_pfn_range :

remap_pfn_range 是 Linux 内核中的一个函数,用于将物理页面框号(PFN)映射到用户空间的虚拟地址范围中。PFN 是物理页面在内存中的索引,而不是直接的物理地址。这个函数在内核中的 mm/memory.c 文件中定义。
int remap_pfn_range(struct vm_area_struct *vma, unsigned long virt_addr, unsigned long pfn, unsigned long size, pgprot_t prot);

// 参数和返回值说明
vma: 虚拟内存区域结构体指针,描述了要进行映射的虚拟内存区域。
virt_addr: 用户空间中要映射的虚拟地址的起始地址。
pfn: 物理页面框号的起始地址,即要映射的物理页面在内存中的索引。
size: 要映射的内存区域大小。
prot: 要应用于映射区域的页面保护标志,通常使用 vm_page_prot 定义。
映射成功返回0,失败返回错误码
 

mmap:

mmap() 是一个 Unix 和类 Unix 操作系统中的系统调用,用于在进程的地址空间中创建一个新的内存映射区域。它允许进程将文件或设备映射到其地址空间中,从而可以通过内存访问来读取和写入文件,或者与设备进行通信:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

// 参数和返回值说明
addr:欲映射的内存起始地址,通常设置为 NULL,表示由系统选择合适的地址。
length:映射区域的长度,以字节为单位。
prot:映射区域的保护方式,可以是 PROT_READ、PROT_WRITE、PROT_EXEC 和 PROT_NONE 的组合。
flags:控制映射区域的属性,可以是 MAP_SHARED、MAP_PRIVATE、MAP_ANONYMOUS 和其他标志的组合。
fd:要映射的文件描述符,如果不是映射文件,可以设置为 -1,对应的flags要带MAP_ANONYMOUS。
offset:要映射的文件的偏移量,通常设置为 0。
成功时,返回映射区域的起始地址。
失败时,返回 MAP_FAILED,并设置 errno 表示错误原因。
当flags设置为MAP_ANONYMOUS时,这个标志告诉操作系统不关联任何文件,而是直接映射到系统内核管理的一块内存区域,也成为匿名内存区域,适合的使用场景:

1、共享内存:多个进程可以通过映射同一个匿名内存区域来实现进程间通信,从而共享数据。

2、动态内存分配:malloc() 和 free() 等内存管理函数可能会使用匿名内存区域来分配和释放内存。

3、临时缓冲区:某些临时数据或缓冲区可以放置在匿名内存区域中,从而避免了频繁的文件 I/O 操作。

 
mmap驱动和应用测试程序举例
mmap_demo.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

#define DEVICE_NAME "mmap_demo"
#define BUF_SIZE 4096

MODULE_LICENSE("GPL");

static int major;
static char *buffer;

static int mmap_demo_open(struct inode *inode, struct file *file) {
    return 0;
}

static int mmap_demo_release(struct inode *inode, struct file *file) {
    return 0;
}

static int mmap_demo_mmap(struct file *file, struct vm_area_struct *vma) {
    unsigned long size = vma->vm_end - vma->vm_start;

    // 检查请求的内存大小是否合法
    if (size > BUF_SIZE)
        return -EINVAL;

    // 将设备内存映射到用户空间    
    if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(buffer) >> PAGE_SHIFT + vm->vm_pgoff, size, vma->vm_page_prot) < 0)
        return -EAGAIN;

    return 0;
}

static struct file_operations mmap_demo_fops = {
    .open = mmap_demo_open,
    .release = mmap_demo_release,
    .mmap = mmap_demo_mmap,
};

static int __init mmap_demo_init(void) {
    major = register_chrdev(0, DEVICE_NAME, &mmap_demo_fops);
    if (major < 0) {
        printk(KERN_ALERT "Failed to register a major number\n");
        return major;
    }

    buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
    if (!buffer) {
        unregister_chrdev(major, DEVICE_NAME);
        printk(KERN_ALERT "Failed to allocate memory for the device\n");
        return -ENOMEM;
    }

    printk(KERN_INFO "mmap_demo module loaded\n");
    return 0;
}

static void __exit mmap_demo_exit(void) {
    kfree(buffer);
    unregister_chrdev(major, DEVICE_NAME);
    printk(KERN_INFO "mmap_demo module unloaded\n");
}

module_init(mmap_demo_init);
module_exit(mmap_demo_exit);
PAGE_SHIFT可以理解为一个页面的大小,在linux中一般为4K,也就是1 << 12。vma->vm_start表示用户空间要映射的虚拟地址的起始地址,virt_to_phys(buffer) >> PAGE_SHIFT + vm->vm_pgoff表示页帧号
 
应用层测试程序:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>

#define DEVICE_FILE "/dev/mmap_demo"
#define BUF_SIZE 4096

int main() {
    int fd;
    char *mapped_mem;

    // 打开设备文件
    fd = open(DEVICE_FILE, O_RDWR);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    // 将设备内存映射到用户空间
    mapped_mem = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (mapped_mem == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    // 写入数据到设备内存
    const char *data = "Hello, mmap from user space!";
    strncpy(mapped_mem, data, strlen(data));

    // 从设备内存读取数据
    printf("Data from device memory: %s\n", mapped_mem);

    // 解除内存映射
    if (munmap(mapped_mem, BUF_SIZE) == -1) {
        perror("munmap");
    }

    // 关闭设备文件
    if (close(fd) == -1) {
        perror("close");
        exit(EXIT_FAILURE);
    }

    return 0;
}

 

 

 

 

 
 
posted @ 2024-04-06 22:32  lethe1203  阅读(789)  评论(0编辑  收藏  举报