Linux内核中ioremap映射的透彻理解(2)

  下面的程序在启动的时候保留一段内存,然后使用ioremap将它映射到内核虚拟空间,同时又用remap_page_range映射到用户虚拟空间,这样一来,内核和用户都能访问。如果在内核虚拟地址将这段内存初始化串"abcd",那么在用户虚拟地址能够读出来:

/************mmap_ioremap.c**************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/wrapper.h> /* for mem_map_(un)reserve */
#include <asm/io.h> /* for virt_to_phys */
#include <linux/slab.h> /* for kmalloc and kfree */

MODULE_PARM(mem_start, "i");
MODULE_PARM(mem_size, "i");

static int mem_start = 101, mem_size = 10;
static char *reserve_virt_addr;
static int major;

int mmapdrv_open(struct inode *inode, struct file *file);
int mmapdrv_release(struct inode *inode, struct file *file);
int mmapdrv_mmap(struct file *file, struct vm_area_struct *vma);

static struct file_operations mmapdrv_fops =
{
 owner: THIS_MODULE, mmap: mmapdrv_mmap, open: mmapdrv_open, release:
 mmapdrv_release,
};

int init_module(void)
{
 if ((major = register_chrdev(0, "mmapdrv", &mmapdrv_fops)) < 0)
 {
  printk("mmapdrv: unable to register character device\n");
  return ( - EIO);
 }
 printk("mmap device major = %d\n", major);

 printk("high memory physical address 0x%ldM\n", virt_to_phys(high_memory) /
1024 / 1024);

 reserve_virt_addr = ioremap(mem_start *1024 * 1024, mem_size *1024 * 1024);
 printk("reserve_virt_addr = 0x%lx\n", (unsigned long)reserve_virt_addr);
 if (reserve_virt_addr)
 {
  int i;
  for (i = 0; i < mem_size *1024 * 1024; i += 4)
  {
   reserve_virt_addr[i] = 'a';
   reserve_virt_addr[i + 1] = 'b';
   reserve_virt_addr[i + 2] = 'c';
   reserve_virt_addr[i + 3] = 'd';
  }
 }
 else
 {
  unregister_chrdev(major, "mmapdrv");
  return - ENODEV;
 }
 return 0;
}

/* remove the module */
void cleanup_module(void)
{
 if (reserve_virt_addr)
  iounmap(reserve_virt_addr);

 unregister_chrdev(major, "mmapdrv");
 return ;
}

int mmapdrv_open(struct inode *inode, struct file *file)
{
 MOD_INC_USE_COUNT;
 return (0);
}

int mmapdrv_release(struct inode *inode, struct file *file)
{
 MOD_DEC_USE_COUNT;
 return (0);
}

int mmapdrv_mmap(struct file *file, struct vm_area_struct *vma)
{
 unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 unsigned long size = vma->vm_end - vma->vm_start;

 if (size > mem_size *1024 * 1024)
 {
  printk("size too big\n");
  return ( - ENXIO);
 }

 offset = offset + mem_start * 1024 * 1024;

 /* we do not want to have this area swapped out, lock it */
 vma->vm_flags |= VM_LOCKED;
 if (remap_page_range(vma, vma->vm_start, offset, size, PAGE_SHARED))
 {
  printk("remap page range failed\n");
  return - ENXIO;
 }
 return (0);
}

  remap_page_range函数的功能是构造用于映射一段物理地址的新页表,实现了内核空间与用户空间的映射,其原型如下:

int remap_page_range(vma_area_struct *vma, unsigned long from, unsigned long to, unsigned long size, pgprot_tprot); 

  使用mmap最典型的例子是显示卡的驱动,将显存空间直接从内核映射到用户空间将可提供显存的读写效率。

(在内核驱动程序的初始化阶段,通过ioremap()将物理地址映射到内核虚拟空间;在驱动程序的mmap系统调用中,使用remap_page_range()将该块ROM映射到用户虚拟空间。这样内核空间和用户空间都能访问这段被映射后的虚拟地址。)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwxjjp.html