mmap延后触发内存申请测试

内核模块程序

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
 #define MY_BUFFER_SIZE (4 * PAGE_SIZE)
static char *my_buffer;

struct vm_area_struct *vma;


 static int my_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
    struct page *page = NULL;
    unsigned long addr = (((unsigned long)vmf->virtual_address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT));
    printk(KERN_ERR "my_fault enter\n");    
    if (addr >= MY_BUFFER_SIZE) {
        return VM_FAULT_SIGBUS;
    }
     if (!my_buffer) {
        my_buffer = kmalloc(MY_BUFFER_SIZE, GFP_KERNEL);
        if (!my_buffer) {
            return VM_FAULT_OOM;
        }
    }
     page = virt_to_page(my_buffer + addr);
     get_page(page);
     vmf->page = page;
     
     printk(KERN_ERR "my_fault ok\n");
     return 0;
}
 static struct vm_operations_struct my_vm_ops = {
    .fault = my_fault,
};

 static int my_mmap(struct file *filp, struct vm_area_struct *vmap)
{
/*    int ret;
    unsigned long pfn = virt_to_phys((void *)my_buffer) >> PAGE_SHIFT;
     ret = remap_pfn_range(vma, vma->vm_start, pfn, vma->vm_end - vma->vm_start, vma->vm_page_prot);
    if (ret < 0) {
        printk(KERN_ERR "Failed to remap buffer\n");
        return ret;
    }
*/
    vmap->vm_ops = &my_vm_ops;
    vma = vmap; 
    printk(KERN_ERR "my_mmap ok\n");
     return 0;
}

 static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
    int len;
     if (*offset >= MY_BUFFER_SIZE) {
        return 0;
    }
     len = min(count, (size_t)(MY_BUFFER_SIZE - *offset));
     if (copy_to_user(buf, my_buffer + *offset, len)) {
        return -EFAULT;
    }
     *offset += len;
     return len;
}
 static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
    int len;
     if (*offset >= MY_BUFFER_SIZE) {
        return 0;
    }
     len = min(count, (size_t)(MY_BUFFER_SIZE - *offset));
     if (copy_from_user(my_buffer + *offset, buf, len)) {
        return -EFAULT;
    }
     *offset += len;
     return len;
}
static int my_open(struct inode *inode, struct file *filp)
{
    return 0;
}
static int my_release(struct inode *inode, struct file *filp)
{
    if(my_buffer)
    {
        kfree(my_buffer);
        my_buffer = NULL;
        printk(KERN_ERR "my_release ok\n");
    }
    return 0;
}
 static loff_t my_llseek(struct file *file, loff_t offset, int whence)
{
    loff_t newpos;
     switch (whence) {
    case 0: /* SEEK_SET */
        newpos = offset;
        break;
    case 1: /* SEEK_CUR */
        newpos = file->f_pos + offset;
        break;
    case 2: /* SEEK_END */
        newpos = MY_BUFFER_SIZE + offset;
        break;
    default: /* can't happen */
        return -EINVAL;
    }
     if (newpos < 0 || newpos > MY_BUFFER_SIZE) {
        return -EINVAL;
    }
     file->f_pos = newpos;
     return newpos;
}
 static const struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .read = my_read,
    .write = my_write,
    .llseek = my_llseek,
    .open = my_open,
    .release = my_release,
    .mmap = my_mmap,
};
 static int __init my_init(void)
{
    int ret;
     ret = register_chrdev(0, "my_module2", &my_fops);
    if (ret < 0) {
        printk(KERN_ERR "Failed to register device\n");
        return ret;
    }
     return 0;
}
 static void __exit my_exit(void)
{
    unregister_chrdev(0, "my_module2");
}
 module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");

应用层程序

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

#define BUF_SIZE (4 * 4096)

int main(int argc, char **argv)
{
    int fd, ret;
    char *buf;
    off_t offset = 0;

    fd = open("/dev/my_module", O_RDWR);
    if (fd < 0) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    buf = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (buf == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    printf("Writing data to buffer...\n");
    snprintf(buf, BUF_SIZE, "Hello, world!");

    printf("Reading data from buffer...\n");
    ret = read(fd, buf, BUF_SIZE);
    if (ret < 0) {
        perror("read");
        exit(EXIT_FAILURE);
    }
    printf("Data read from buffer: %s\n", buf);

    printf("Seeking to offset 6...\n");
    offset = lseek(fd, 6, SEEK_SET);
    if (offset < 0) {
        perror("lseek");
        exit(EXIT_FAILURE);
    }

    printf("Reading data from buffer at offset 6...\n");
    ret = read(fd, buf, BUF_SIZE);
    if (ret < 0) {
        perror("read");
        exit(EXIT_FAILURE);
    }
    printf("Data read from buffer at offset 6: %s\n", buf);

    munmap(buf, BUF_SIZE);
    close(fd);

    return 0;
}

执行输出,触发了四次缺页异常

# ./a_delay.out 
my_mmap ok
Writing data to buffer...
my_fault enter
my_fault ok
Reading data from buffer...
my_fault enter
my_fault ok
my_fault enter
my_fault ok
my_fault enter
my_fault ok
Data read from buffer: Hello, world!
Seeking to offset 6...
Reading data from buffer at offset 6...
Data read from buffer at offset 6:  world!
my_release ok
# dmesg 
[ 1370.921056] my_mmap ok
[ 1370.924994] my_fault enter
[ 1370.925145] my_fault ok
[ 1370.925995] my_fault enter
[ 1370.926114] my_fault ok
[ 1370.926203] my_fault enter
[ 1370.926263] my_fault ok
[ 1370.926355] my_fault enter
[ 1370.926414] my_fault ok
[ 1370.945439] my_release ok

补充说明:
filemap_fault函数是Linux内核中的一种函数,用于在用户空间进程访问文件时,将文件映射到内存中。该函数的作用是将文件页映射到用户空间的虚拟地址空间中,以便进程可以直接访问文件的内容。该函数的实现过程中,会先检查文件页是否已经在内存中,如果不在则会调用文件系统的readpage函数将文件页读入内存。如果文件页已经在内存中,则直接将该页映射到用户进程的虚拟地址空间中。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值