Linux内核空间映射到用户空间

一:简介

       共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。
       采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据。一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

 

二:实例应用

       linux的2.2.x内核支持多种共享内存方式,如mmap()系统调用,Posix共享内存,以及系统V共享内存。当内核空间和用户空间存在大量数据交互时, 共享内存映射就成了这种情况下的不二选择; 它能够最大限度的降低内核空间和用户空间之间的数据拷贝, 从而大大提高系统的性能.

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

1.内核空间分配内存

[cpp]  view plain  copy
 print ?
  1. #include <linux/types.h>  
  2. #include <linux/mm.h>  
  3. #include <linux/vmalloc.h>  
  4.   
  5. int mmap_alloc(int require_buf_size)  
  6. {  
  7.   struct page *page;  
  8.     
  9.   mmap_size = PAGE_ALIGN(require_buf_size);  
  10.   
  11. #if USE_KMALLOC //for kmalloc  
  12.   mmap_buf = kzalloc(mmap_size, GFP_KERNEL);  
  13.   if (!mmap_buf) {  
  14.     return -1;  
  15.   }  
  16.   for (page = virt_to_page(mmap_buf ); page < virt_to_page(mmap_buf + mmap_size); page++) {  
  17.     SetPageReserved(page);  
  18.   }  
  19. #else //for vmalloc  
  20.   mmap_buf  = vmalloc(mmap_size);  
  21.   if (!mmap_buf ) {  
  22.     return -1;  
  23.   }  
  24.   for (i = 0; i < mmap_size; i += PAGE_SIZE) {  
  25.     SetPageReserved(vmalloc_to_page((void *)(((unsigned long)mmap_buf) + i)));  
  26.   }  
  27. #endif  
  28.   
  29.   return 0;  
  30. }  


2.用户空间映射内存

[cpp]  view plain  copy
 print ?
  1. int test_mmap()  
  2. {  
  3.   mmap_fd = open("/dev/mmap_dev", O_RDWR);  
  4.   mmap_ptr = mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, mmap_fd, 0);  
  5.   if (mmap_ptr == MAP_FAILED) {  
  6.     return -1;  
  7.   }  
  8.   return 0;  
  9. }  


3.内核空间映射内存: 实现file_operations的mmap函数

[cpp]  view plain  copy
 print ?
  1. static int mmap_mmap(struct file *filp, struct vm_area_struct *vma)  
  2. {  
  3.   int ret;  
  4.   unsigned long pfn;  
  5.   unsigned long start = vma->vm_start;  
  6.   unsigned long size = PAGE_ALIGN(vma->vm_end - vma->vm_start);  
  7.   
  8.   if (size > mmap_size || !mmap_buf) {  
  9.     return -EINVAL;  
  10.   }  
  11.   
  12. #if USE_KMALLOC  
  13.   return remap_pfn_range(vma, start, (virt_to_phys(mmap_buf) >> PAGE_SHIFT), size, PAGE_SHARED);  
  14. #else  
  15.   /* loop over all pages, map it page individually */  
  16.   while (size > 0) {  
  17.           pfn = vmalloc_to_pfn(mmap_buf);  
  18.           if ((ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED)) < 0) {  
  19.             return ret;  
  20.           }  
  21.           start += PAGE_SIZE;  
  22.           mmap_buf += PAGE_SIZE;  
  23.           size -= PAGE_SIZE;  
  24.   }  
  25. #endif  
  26.   return 0;  
  27. }  
  28.   
  29. static const struct file_operations mmap_fops = {  
  30.   .owner = THIS_MODULE,  
  31.   .ioctl = mmap_ioctl,  
  32.   .open = mmap_open,  
  33.   .mmap = mmap_mmap,  
  34.   .release = mmap_release,  
  35. };  


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

[cpp]  view plain  copy
 print ?
  1. void test_munmap()  
  2. {      
  3.   munmap(mmap_ptr, mmap_size);  
  4.   close(mmap_fd);  
  5. }  


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

[cpp]  view plain  copy
 print ?
  1. void mmap_free()  
  2. {  
  3. #if USE_KMALLOC  
  4.   struct page *page;  
  5.   for (page = virt_to_page(mmap_buf); page < virt_to_page(mmap_buf + mmap_size); page++) {  
  6.     ClearPageReserved(page);  
  7.   }  
  8.   kfree(mmap_buf);  
  9. #else  
  10.   int i;  
  11.   for (i = 0; i < mmap_size; i += PAGE_SIZE) {  
  12.     ClearPageReserved(vmalloc_to_page((void *)(((unsigned long)mmap_buf) + i)));  
  13.   }  
  14.   vfree(mmap_buf);  
  15. #endif  
  16.   mmap_buf = NULL;  
  17. }  
对于大数据的内存访问,一般来说在Linux系统中采用内存映射是最好的方式,这样对于应用层来说,可以很方便的访问到内核的空间..

猜你喜欢

转载自blog.csdn.net/chen_jianjian/article/details/75659067