linux内核mmap函数测试

应用层mmap函数介绍:

mmap函数是Linux系统中的一个系统调用,用于将一个文件或者其它对象映射到内存中,以便于程序对其进行操作。mmap函数的原型如下:

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_FIXED:指定映射区域的起始地址,如果该地址已被占用,则映射失败;
    • MAP_ANONYMOUS:不使用文件,而是映射一段匿名的内存区域;
  • fd:指定要映射的文件的文件描述符,如果不映射文件,则设为-1;
  • offset:指定文件中的偏移量,通常设为0。

mmap函数成功时返回映射区域的起始地址,失败时返回MAP_FAILED。在使用mmap函数映射文件时,需要先使用open函数打开文件,并使用fstat函数获取文件的大小。映射完成后,可以直接使用指针对映射区域进行读写操作,也可以使用munmap函数解除映射关系。需要注意的是,对映射区域的操作会直接影响到对应的文件,因此需要谨慎处理以确保数据的一致性。

内核中实现样例:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/mm.h>
#include <linux/slab.h>
 #define DEVICE_NAME "rw_mmap_dev"
#define BUF_SIZE 4096
 static char *buf;
static struct cdev cdev;
static dev_t devno;
 static int rw_mmap_open(struct inode *inode, struct file *filp)
{
    return 0;
}
 static int rw_mmap_release(struct inode *inode, struct file *filp)
{
    return 0;
}
 static int rw_mmap_mmap(struct file *filp, struct vm_area_struct *vma)
{
    unsigned long pfn;
    unsigned long size = vma->vm_end - vma->vm_start;
     if (size > BUF_SIZE) {
        printk(KERN_ERR "mmap size too large\n");
        return -EINVAL;
    }
     pfn = virt_to_phys((void *)buf) >> PAGE_SHIFT;
     if (remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot)) {
        printk(KERN_ERR "remap_pfn_range failed\n");
        return -EAGAIN;
    }
     return 0;
}
 static ssize_t rw_mmap_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    ssize_t ret;
     if (*f_pos >= BUF_SIZE) {
        return 0;
    }
     if (*f_pos + count > BUF_SIZE) {
        count = BUF_SIZE - *f_pos;
    }
     ret = copy_to_user(buf, &buf[*f_pos], count);
     if (ret) {
        return -EFAULT;
    }
     *f_pos += count;
     return count;
}
 static ssize_t rw_mmap_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    ssize_t ret;
     if (*f_pos >= BUF_SIZE) {
        return -ENOSPC;
    }
     if (*f_pos + count > BUF_SIZE) {
        count = BUF_SIZE - *f_pos;
    }
     ret = copy_from_user(&buf[*f_pos], buf, count);
     if (ret) {
        return -EFAULT;
    }
     *f_pos += count;
     return count;
}
 static const struct file_operations rw_mmap_fops = {
    .owner = THIS_MODULE,
    .open = rw_mmap_open,
    .release = rw_mmap_release,
    .mmap = rw_mmap_mmap,
    .read = rw_mmap_read,
    .write = rw_mmap_write,
};
 static int __init rw_mmap_init(void)
{
    int err;
     buf = kzalloc(BUF_SIZE, GFP_KERNEL);
     if (!buf) {
        err = -ENOMEM;
        goto fail1;
    }
     err = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
     if (err < 0) {
        goto fail2;
    }
     cdev_init(&cdev, &rw_mmap_fops);
    cdev.owner = THIS_MODULE;
     err = cdev_add(&cdev, devno, 1);
     if (err < 0) {
        goto fail3;
    }
     printk(KERN_INFO "rw_mmap device registered\n");
     return 0;
 fail3:
    unregister_chrdev_region(devno, 1);
fail2:
    kfree(buf);
fail1:
    return err;
}
 static void __exit rw_mmap_exit(void)
{
    cdev_del(&cdev);
    unregister_chrdev_region(devno, 1);
    kfree(buf);
     printk(KERN_INFO "rw_mmap device unregistered\n");
}
 module_init(rw_mmap_init);
module_exit(rw_mmap_exit);
 MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple read/write/mmap device driver");

应用程序测试样例

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
 #define DEVICE_NAME "/dev/rw_mmap_dev"
#define BUF_SIZE 4096
 int main()
{
    int fd, ret;
    char *mmap_buf;
    char rw_buf[BUF_SIZE];
     // Open the device file for mmap
    fd = open(DEVICE_NAME, O_RDWR);
     if (fd < 0) {
        perror("open");
        exit(EXIT_FAILURE);
    }
     // Map the device memory into user space
    mmap_buf = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
     if (mmap_buf == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }
     // Test mmap
    printf("Initial mmap buffer contents: %s\n", mmap_buf);
    sprintf(mmap_buf, "Hello, mmap!");
    printf("New mmap buffer contents: %s\n", mmap_buf);
     // Test read/write
    printf("Writing to device...\n");
    snprintf(rw_buf, BUF_SIZE, "Hello, kernel!");
    ret = write(fd, rw_buf, BUF_SIZE);
     if (ret < 0) {
        perror("write");
        close(fd);
        exit(EXIT_FAILURE);
    }
     printf("Reading from device...\n");
    ret = read(fd, rw_buf, BUF_SIZE);
     if (ret < 0) {
        perror("read");
        close(fd);
        exit(EXIT_FAILURE);
    }
     printf("Device says: %s\n", rw_buf);
     // Unmap the device memory
    ret = munmap(mmap_buf, BUF_SIZE);
     if (ret < 0) {
        perror("munmap");
        exit(EXIT_FAILURE);
    }
     close(fd);
     return 0;
}

对应的应用程序执行输出:

./a.out

Initial mmap buffer contents:
New mmap buffer contents: Hello, mmap!
Writing to device…
Reading from device…
Device says: Hello, kernel!

内核调用栈

Hardware name: ARM-Versatile Express
[<80016494>] (unwind_backtrace) from [<80012ed4>] (show_stack+0x10/0x14)
[<80012ed4>] (show_stack) from [<80245e20>] (dump_stack+0x84/0x98)
[<80245e20>] (dump_stack) from [<7f044020>] (rw_mmap_mmap+0x18/0x84 [mmap_kernel])
[<7f044020>] (rw_mmap_mmap [mmap_kernel]) from [<800d0e48>] (mmap_region+0x37c/0x66c)
[<800d0e48>] (mmap_region) from [<800d1448>] (do_mmap+0x310/0x388)
[<800d1448>] (do_mmap) from [<800bfc40>] (vm_mmap_pgoff+0x6c/0x9c)
[<800bfc40>] (vm_mmap_pgoff) from [<800cf8bc>] (SyS_mmap_pgoff+0x90/0xc0)
[<800cf8bc>] (SyS_mmap_pgoff) from [<8000f400>] (ret_fast_syscall+0x0/0x3c)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值