内核空间到用户空间的共享内存映射


当内核空间和用户空间存在大量数据交互时, 共享内存映射就成了这种情况下的不二选择; 它能够最大限度的降低内核空间和用户空间之间的数据拷贝, 从而大大提高系统的性能.


以下是创建从内核空间到用户空间的共享内存映射的模板代码(在内核2.6.18和2.6.32上测试通过):

1.内核空间分配内存:

#include
#include
#include

int mmap_alloc(int require_buf_size)
{
  struct page *page;
 
  mmap_size = PAGE_ALIGN(require_buf_size);

#if USE_KMALLOC //for kmalloc
  mmap_buf = kzalloc(mmap_size, GFP_KERNEL);
  if (!mmap_buf) {
    return -1;
  }
  for (page = virt_to_page(mmap_buf ); page < virt_to_page(mmap_buf + mmap_size); page++) {
    SetPageReserved(page);
  }
#else //for vmalloc
  mmap_buf  = vmalloc(mmap_size);
  if (!mmap_buf ) {
    return -1;
  }
  for (i = 0; i < mmap_size; i += PAGE_SIZE) {
    SetPageReserved(vmalloc_to_page((void *)(((unsigned long)mmap_buf) + i)));
  }
#endif

  return 0;
}

2.用户空间映射内存

int test_mmap()
{
  mmap_fd = open("/dev/mmap_dev", O_RDWR);
  mmap_ptr = mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, mmap_fd, 0);
  if (mmap_ptr == MAP_FAILED) {
    return -1;
  }
  return 0;
}


3.内核空间映射内存: 实现file_operations的mmap函数
static int mmap_mmap(struct file *filp, struct vm_area_struct *vma)
{
  int ret;
  unsigned long pfn;
  unsigned long start = vma->vm_start;
  unsigned long size = PAGE_ALIGN(vma->vm_end - vma->vm_start);

  if (size > mmap_size || !mmap_buf) {
    return -EINVAL;
  }

#if USE_KMALLOC
  return remap_pfn_range(vma, start, (virt_to_phys(mmap_buf) >> PAGE_SHIFT), size, PAGE_SHARED);
#else
  /* loop over all pages, map it page individually */
  while (size > 0) {
          pfn = vmalloc_to_pfn(mmap_buf);
          if ((ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED)) < 0) {
            return ret;
          }
          start += PAGE_SIZE;
          mmap_buf += PAGE_SIZE;
          size -= PAGE_SIZE;
  }
#endif
  return 0;
}

static const struct file_operations mmap_fops = {
  .owner = THIS_MODULE,
  .ioctl = mmap_ioctl,
  .open = mmap_open,
  .mmap = mmap_mmap,
  .release = mmap_release,
};

4.用户空间撤销内存映射

void test_munmap()
{    
  munmap(mmap_ptr, mmap_size);
  close(mmap_fd);
}


5.内核空间释放内存; 必须在用户空间执行munmap系统调用后才能释放

void mmap_free()
{
#if USE_KMALLOC
  struct page *page;
  for (page = virt_to_page(mmap_buf); page < virt_to_page(mmap_buf + mmap_size); page++) {
    ClearPageReserved(page);
  }
  kfree(mmap_buf);
#else
  int i;
  for (i = 0; i < mmap_size; i += PAGE_SIZE) {
    ClearPageReserved(vmalloc_to_page((void *)(((unsigned long)mmap_buf) + i)));
  }
  vfree(mmap_buf);
#endif
  mmap_buf = NULL;
}


参考资料:

http://www.scs.ch/~frey/linux/memorymap.html



你可能感兴趣的:(Linux内核)