mmap实验例子

mmap内存映射实现

实验内容:在内核下面申请4K大小的内存(kmalloc申请的物理连续内存),在应用程序中通过mmap把内核态下申请的内存块映射到用户空间,然后在应用程序中通过read,write操作读写内核空间数据,最后读取用户态映射的地址空间,看内容是否和内核数据空间一致。

我的编译工具:
/opt/gcc-linaro-6.2.1-2016.11-x86_64_aarch64-linux-gun/bin/arrch64-linux-gun-gcc

内核实现:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/semaphore.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/netdevice.h>
#include <linux/uio_driver.h>
#include <linux/syscalls.h>
#include <linux/types.h>
#include <linux/proc_fs.h>

/* variable declare */
static struct proc_dir_entry *mmap_dir  = NULL;
static struct proc_dir_entry *mmap_file = NULL;

struct self_data {
    int arry[1024];
};

int    alloc_size = 0;
struct self_data *p_mem = NULL;


/* function declare */
static int mmap_open(struct inode *p_node, struct file *p_file);
static ssize_t mmap_read(struct file *p_file, char __user *p_buf, size_t size, loff_t * p_loff);
static ssize_t mmap_write(struct file *p_file, const char __user *p_buf, size_t size, loff_t *p_loff);
static int mmap_mmap(struct file *p_file, struct vm_area_struct *vma);
static int mmap_release(struct inode *p_node, struct file *p_file);

static const struct file_operations mmap_opts = {
    .owner   = THIS_MODULE,
    .open    = mmap_open,
    .read    = mmap_read,
    .write   = mmap_write,
    .mmap    = mmap_mmap,
    .release = mmap_release,

};

int mmap_release(struct inode *p_node, struct file *p_file)
{
    return 0;
}

static int mmap_open(struct inode *p_node, struct file *p_file)
{
    return simple_open(p_node, p_file);
}

static ssize_t mmap_read(struct file *p_file, char __user *p_buf, size_t size, loff_t * p_loff)
{
    copy_to_user(p_buf, p_mem, size);

    return size;

}

static ssize_t mmap_write(struct file *p_file, const char __user *p_buf, size_t size, loff_t *p_loff)
{
    copy_from_user(p_mem, p_buf, size);

    return size;
}
static int mmap_mmap(struct file *p_file, struct vm_area_struct *vma)
{
    unsigned long phyaddr = 0;
    unsigned long size = vma->vm_end - vma->vm_start;
    int ret;

    if (p_mem) {
        phyaddr = __pa(p_mem);
    }
    if (phyaddr  == 0) {
        pr_err("get phy addr failed!\n");
    }
    ret = remap_pfn_range(vma, vma->vm_start, phyaddr >> PAGE_SHIFT, size, PAGE_SHARED);
    if (ret != 0) {
        pr_err("remap_pfn_range failed!\n");
    }
    printk(KERN_EMERG"phyaddr = %p, remap size = %d\n", phyaddr, size);

    return ret;
}

static int __init my_mmap_init(void)
{
    mmap_dir= proc_mkdir("module_mmap", NULL);
    if (mmap_dir == NULL) {
    	pr_err("proc_mkdie failed!\n");
    }

   mmap_file =  proc_create("self_mmap", 0666, mmap_dir, &mmap_opts);
   if (mmap_file == NULL) {
       pr_err("proc_data failed!\n");
   }

    alloc_size = PAGE_ALIGN(sizeof(struct self_data));
    p_mem  = kmalloc(alloc_size, GFP_KERNEL);
    if (p_mem == NULL) {
        pr_err("kmalloc failed!\n");
        return -1;
    }
    SetPageReserved(virt_to_page(p_mem));

    memset(p_mem, 0, alloc_size);
    
    printk(KERN_EMERG"alloc size = %d\n", alloc_size);

    return 0;
}

static void __exit my_mmap_exit(void)
{
    if (p_mem) {
        kfree(p_mem);
    }
    if (mmap_dir) {
    
    }
}

module_init(my_mmap_init);
module_exit(my_mmap_exit);

MODULE_LICENSE("GPL v2");

Makefile 文件:

#指定内核编译结果路径
KERNDIR ?=
ARCH ?=
PWD := $(shell pwd)

#指定交叉编译工具和GCC,LD,不然会使用本地的gcc
CROSS_COMPILE ?=
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld

obj-m := module_mmap.o

all:
	 $(MAKE) ARCH=$(ARCH) -C $(KERNDIR)  M=${PWD} modules

用户态实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>


char *p_file = NULL;
char buf[1024];

int main (void)
{
    int fd = 0;
    int i;
    void *p_viraddr = NULL;
    ssize_t len;

    for (i = 0; i < 1024; i++) {
        buf[i] = 'r';   
    }

    fd =  open("/proc/module_mmap/self_mmap", O_RDWR);
    if (fd < 0) {
        printf("file open failed\n");
        return 0;
    }

    /* 将内核空间内存,映射到用户空间,并返回虚拟地址的首地址 */
    p_viraddr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    if (p_viraddr == NULL) {
		printf("mmap failed!\n");
		close(fd);
		return 0;
	}
	
	/* 往内核地址空间写数据---最终调用的是我们实现的mmap_write函数 */
    len = write(fd, buf, 1024);
    if (len < 0) {
        printf("write error\n");
		close(fd);
		return 0;
    }
   
   sleep(5);
   memset(buf, 0, 1024);

   len = read(fd, buf, 1024);
   if (len < 0) {
        printf("read error\n");
	    close(fd);
        return 0;
   }
   printf("kernal addr content: \n");
   for (i = 0; i < 1024; i++) {
       printf("%c", buf[i]);
   }

   printf ("\nuser addr content: \n");
   for (i = 0; i < 1024; i++) {
       printf("%c",((char *)p_viraddr)[i]);
   }
   close(fd);

   return 0;
}

用户态文件编译:

/opt/gcc-linaro-6.2.1-2016.11-x86_64_aarch64-linux-gun/bin/arrch64-linux-gun-gcc -g mmap_main.c -o mmap_main

最后:将编译出的module_mmap.ko 和mmap_main文件,拷贝到内核编译结果目录:rootf, 然后image打包。
实验现象:
kernal addr content:
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr …
user addr content:
rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr…

static int hah_mmap(struct file *filp, struct vm_area_struct *vma)
{
size_t size = vma->vm_end - vma->vm_start;
phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;

/* Does it even fit in phys_addr_t? */
if ((offset >> PAGE_SHIFT) != vma->vm_pgoff) {
    return -EINVAL;
}

/* It's illegal to wrap around the end of the physical address space. */
if ((offset + (phys_addr_t)size - 1) < offset) {
    return -EINVAL;
}

#ifndef SOC_SIM_MODE
if (!pfn_valid(vma->vm_pgoff)) {
vma->vm_page_prot = phys_mem_access_prot(filp, vma->vm_pgoff, size, vma->vm_page_prot);
}
#endif
/* Remap-pfn-range will mark the range VM_IO */
if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, size, vma->vm_page_prot) != 0) {
return -EAGAIN;
}
return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值