免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 18481 | 回复: 26
打印 上一主题 下一主题

【一个完整的例程】内核和用户空间共享内存 [复制链接]

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-09-27 18:31 |只看该作者 |倒序浏览
之所以想写这篇帖子,是有两个方面原因。其一是内核版有一个关于《内核可以从线性地址直接计算物理地址,用来做什么呢?》的讨论,偶说计算出物理地址可以共享给用户空间读写。dreamice兄说能否说一下详细的应用。其二是alb*版主提到wheelz曾经写过这样一个例程,拜读了一把,发现在传递物理地址和内存大小上,wheelz的例程还有些不够灵活。alb*版主提到可以通过文件的方式实现动态的传递。

因此,偶也写了这样一个例程,可以动态的将内核空间的物理地址和大小传给用户空间。

整个内核模块,在模块插入时建立proc文件,分配内存。卸载模块的时候将用户空间写入的内容打印出来。
以下是内核模块的代码和用户空间的测试代码。
  1. /*This program is used to allocate memory in kernel
  2. and pass the physical address to userspace through proc file.*/

  3. #include <linux/version.h>
  4. #include <linux/module.h>
  5. #include <linux/proc_fs.h>
  6. #include <linux/mm.h>

  7. #define PROC_MEMSHARE_DIR                                "memshare"
  8. #define PROC_MEMSHARE_PHYADDR                        "phymem_addr"
  9. #define PROC_MEMSHARE_SIZE                                "phymem_size"

  10. /*alloc one page. 4096 bytes*/
  11. #define PAGE_ORDER                                0
  12. /*this value can get from PAGE_ORDER*/
  13. #define PAGES_NUMBER                                1

  14. struct proc_dir_entry *proc_memshare_dir ;
  15. unsigned long kernel_memaddr = 0;
  16. unsigned long kernel_memsize= 0;

  17. static int proc_read_phymem_addr(char *page, char **start, off_t off, int count)
  18. {
  19.         return sprintf(page, "%08lx\n", __pa(kernel_memaddr));
  20. }
  21. static int proc_read_phymem_size(char *page, char **start, off_t off, int count)
  22. {
  23.         return sprintf(page, "%lu\n", kernel_memsize);
  24. }

  25. static int __init init(void)
  26. {
  27.         /*build proc dir "memshare"and two proc files: phymem_addr, phymem_size in the dir*/
  28.         proc_memshare_dir = proc_mkdir(PROC_MEMSHARE_DIR, NULL);
  29.         create_proc_info_entry(PROC_MEMSHARE_PHYADDR, 0, proc_memshare_dir, proc_read_phymem_addr);
  30.         create_proc_info_entry(PROC_MEMSHARE_SIZE, 0, proc_memshare_dir, proc_read_phymem_size);

  31.         /*alloc one page*/
  32.         kernel_memaddr =__get_free_pages(GFP_KERNEL, PAGE_ORDER);
  33.         if(!kernel_memaddr)
  34.         {
  35.                 printk("Allocate memory failure!\n");
  36.         }
  37.         else
  38.         {
  39.                 SetPageReserved(virt_to_page(kernel_memaddr));
  40.                 kernel_memsize = PAGES_NUMBER * PAGE_SIZE;
  41.                 printk("Allocate memory success!. The phy mem addr=%08lx, size=%lu\n", __pa(kernel_memaddr), kernel_memsize);
  42.         }
  43.         return 0;
  44. }

  45. static void __exit fini(void)
  46. {
  47.         printk("The content written by user is: %s\n", (unsigned char *) kernel_memaddr);
  48.         ClearPageReserved(virt_to_page(kernel_memaddr));
  49.         free_pages(kernel_memaddr, PAGE_ORDER);
  50.         remove_proc_entry(PROC_MEMSHARE_PHYADDR, proc_memshare_dir);
  51.         remove_proc_entry(PROC_MEMSHARE_SIZE, proc_memshare_dir);
  52.         remove_proc_entry(PROC_MEMSHARE_DIR, NULL);

  53.         return;
  54. }
  55. module_init(init);
  56. module_exit(fini);
  57. MODULE_LICENSE("GPL");
  58. MODULE_AUTHOR("Godbach ([email]nylzhaowei@163.com[/email])");
  59. MODULE_DESCRIPTION("Kernel memory share module.");
复制代码
用户空间的测试代码:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <string.h>
  5. #include <fcntl.h>
  6. #include <sys/stat.h>
  7. #include <sys/types.h>
  8. #include <sys/mman.h>

  9. int main(int argc, char* argv[])
  10. {
  11.         if(argc != 2)
  12.         {
  13.                 printf("Usage: %s string\n", argv[0]);
  14.                 return 0;
  15.         }
  16.         
  17.         unsigned long phymem_addr, phymem_size;
  18.         char *map_addr;
  19.         char s[256];
  20.         int fd;
  21.         
  22.         /*get the physical address of allocated memory in kernel*/
  23.         fd = open("/proc/memshare/phymem_addr", O_RDONLY);
  24.         if(fd < 0)
  25.         {
  26.                 printf("cannot open file /proc/memshare/phymem_addr\n");
  27.                 return 0;
  28.         }
  29.         read(fd, s, sizeof(s));
  30.         sscanf(s, "%lx", &phymem_addr);
  31.         close(fd);

  32.         /*get the size of allocated memory in kernel*/
  33.         fd = open("/proc/memshare/phymem_size", O_RDONLY);
  34.         if(fd < 0)
  35.         {
  36.                 printf("cannot open file /proc/memshare/phymem_size\n");
  37.                 return 0;
  38.         }
  39.         read(fd, s, sizeof(s));
  40.         sscanf(s, "%lu", &phymem_size);
  41.         close(fd);
  42.         
  43.         printf("phymem_addr=%lx, phymem_size=%lu\n", phymem_addr, phymem_size);
  44.         /*memory map*/
  45.         int map_fd = open("/dev/mem", O_RDWR);
  46.         if(map_fd < 0)
  47.         {
  48.                 printf("cannot open file /dev/mem\n");
  49.                 return 0;
  50.         }
  51.         
  52.         map_addr = mmap(0, phymem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, phymem_addr);
  53.         strcpy(map_addr, argv[1]);
  54.         munmap(map_addr, phymem_size);
  55.         close(map_fd);
  56.         return 0;
  57.         
  58. }
复制代码
测试的内核是2.6.25.以下是执行结果。
debian:/home/km/memshare# insmod memshare_kernel.ko
debian:/home/km/memshare# ./memshare_user 'hello,world!'
phymem_addr=e64e000, phymem_size=4096
debian:/home/km/memshare# cat /proc/memshare/phymem_addr
0e64e000
debian:/home/km/memshare# cat /proc/memshare/phymem_size
4096
debian:/home/km/memshare# rmmod memshare_kernel
debian:/home/km/memshare# tail /var/log/messages
Sep 27 18:14:24 debian kernel: [50527.567931] Allocate memory success!. The phy mem addr=0e64e000, size=4096
Sep 27 18:15:31 debian kernel: [50592.570986] The content written by user is: hello,world!


仓促之间,有些地方处理的还是比较简单。希望高手多多指正。需要了解这方面实现的可以参考一下,共同学习。

[ 本帖最后由 Godbach 于 2008-9-27 18:44 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2008-09-27 18:51 |只看该作者
学习了
说起来 我以前在
http://hi.baidu.com/msingle/blog ... b94e40ad34de18.html
中看到proc   这个有关零拷贝的文章    和楼主方法很像哈
我自己还是用用户态传地址  内核态换算并锁页 以及mmap的方法来做的    同时实现了内核态分配内存 两者同时访问以及用户态分配内存  两者同时访问    感觉要更简单方便些

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
3 [报告]
发表于 2008-09-27 18:54 |只看该作者
原帖由 duanius 于 2008-9-27 18:51 发表
学习了
说起来 我以前在
http://hi.baidu.com/msingle/blog ... b94e40ad34de18.html
中看到proc   这个有关零拷贝的文章    和楼主方法很像哈
我自己还是用用户态传地址  内核态换算并锁页 以及 ...


是吗。有机会把你的实现方法贴出来,一起学习一下啊。

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
4 [报告]
发表于 2008-09-27 18:55 |只看该作者
看了一下你给的链接。确实实现的方式比较相同。这就是零拷贝吗。以前只是听说过这个说法,没有具体去研究。

论坛徽章:
0
5 [报告]
发表于 2008-09-27 19:01 |只看该作者
哦  那也算不上我的方法    看过人用过以及书上有  应该是很普遍的吧     要贴代码  我还要切下系统先。。。。

论坛徽章:
0
6 [报告]
发表于 2008-09-27 19:16 |只看该作者
原帖由 Godbach 于 2008-9-27 18:55 发表
看了一下你给的链接。确实实现的方式比较相同。这就是零拷贝吗。以前只是听说过这个说法,没有具体去研究。

这个恐怕算不上零拷贝   首先没DMA  其次这种共享内存的方式同步起来比较麻烦
我做的里面   有2块共享内存  一块是内核态共享它的网卡描述符   在内核中分配的空间  通过mmap以及nopage方法 映射给用户态
这个方法好像在坛子上都喷烂掉了   就是先用get_free_page 来分配连续空间
然后用字符设备中的mmap方法来返回物理页
·

  1. int netchar_mmap(struct file *file, struct vm_area_struct *vma)
  2. {
  3.         unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
  4.         unsigned long size = vma->vm_end - vma->vm_start;
  5.        
  6.         if (offset & ~PAGE_MASK) {
  7.                 printk("offset not aligned: %ld\n", offset);
  8.                 return -ENXIO;
  9.         }
  10.         if (size > PAGE_SIZE*page_num) {
  11.                 printk("size too big\n");
  12.                 return -ENXIO;
  13.         }       
  14.         /* do not want to have this area swapped out, lock it */
  15.         vma->vm_flags |= VM_LOCKED;
  16.         if (offset == 0) {
  17.                 vma->vm_ops = &netchar_vm_ops;
  18.                 /* call the open routine to increment the usage count */
  19.                 netchar_vma_open(vma);
  20.         } else {
  21.                 printk("offset out of range\n");
  22.                 return -ENXIO;
  23.         }
  24.        
  25.         return 0;
  26. }

  27. struct page *netchar_vma_nopage(struct vm_area_struct *vma, unsigned long address,
  28.                         int *type)
  29. {
  30.         unsigned long offset,kaddr;
  31.         struct page *pageptr;

  32.         printk("address in nopage is %lu\n",address);
  33.         offset = address - vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT);
  34.         kaddr=(unsigned long)desc_k_addr+offset;
  35.         pageptr=virt_to_page(kaddr);
  36.         get_page(pageptr);
  37.         return (pageptr);

  38. }

复制代码

用户态用字符设备的描述符就可以和内核态一样进行读写了
·

  1.         desc_buf=(struct e1000_rx_desc *)mmap(0,desc_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
复制代码




还有个也是在用户态分配内存  然后内核态也读取  也是个用烂掉的方法
先在用户态分配内存  用malloc可以 不过要做页对齐处理  所以一般用valloc或者mmap的匿名映射
我用第3种
·

  1. bufbase=(void *)mmap(0,bufsize,PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_SHARED,-1,0);

复制代码

然后用注册过的ioctl方法把bufbase传给内核态的字符驱动

在内核态读取地址并结合页表进行分析  得到每一页的物理地址(供网卡dma用)以及内核地址(内核态访问用)


  1. static int netchar_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
  2. {
  3.         int i;
  4.         unsigned long vaddr;
  5.         unsigned long svaddr;
  6.         struct page *pageptr;
  7.         switch(cmd)
  8.         {
  9.                 case IOCTL_GETADDR:
  10.                         vaddr=svaddr=arg;
  11.                         for(i=0;i<page_num;i++,vaddr+=PAGE_SIZE)
  12.                         {
  13.                                 pgd_t *pgd=pgd_offset(current->mm,(unsigned long)svaddr);                
  14.                                 pud_t *pud=pud_offset(pgd,(unsigned long)vaddr);
  15.                                 pmd_t *pmd=pmd_offset(pud,(unsigned long)vaddr);
  16.                                 pte_t *pte=pte_offset_map(pmd,(unsigned long)vaddr);
  17.                                 phy_addr_table[i]=(pte_val(*pte)&PAGE_MASK);
  18.                                 pageptr=pte_page(*pte);
  19.                                 void *tempaddr=page_address(pageptr);
  20.                                 kaddr_table[i]=tempaddr;
  21.                                 set_bit(PG_locked,&pageptr->flags);
  22.                                 atomic_inc(&pageptr->_count);
  23.                         }
  24.                         break;
  25.                 case IOCTL_CLEARADDR:
  26.                         vaddr=svaddr=arg;
  27.                         for(i=0;i<page_num;i++,vaddr+=PAGE_SIZE)
  28.                         {
  29.                                 pgd_t *pgd=pgd_offset(current->mm,(unsigned long)svaddr);
  30.                                 pud_t *pud=pud_offset(pgd,(unsigned long)vaddr);
  31.                                 pmd_t *pmd=pmd_offset(pud,(unsigned long)vaddr);
  32.                                 pte_t *pte=pte_offset_map(pmd,(unsigned long)vaddr);                               
  33.                                 pageptr=pte_page(*pte);
  34.                              clear_bit(PG_locked,&pageptr->flags);
  35.                                 atomic_dec(&pageptr->_count);
  36.                         }
  37.                         break;
  38.                 case IOCTL_STARTNIC:
  39.                         printk(KERN_INFO "%s - version %s\n",
  40.                               e1000_driver_string, e1000_driver_version);
  41.                         printk(KERN_INFO "%s\n", e1000_copyright);
  42.                         pci_register_driver(&e1000_driver);
  43.                         printk("after nic register\n");
  44.                         break;
  45.                
  46.         }       
  47.         return 0;
  48. }

复制代码


这样也可以保证内核态和用户态对同一地址空间的访问   这种方式一般用的多一点在零拷贝中  因为内核态分配内存毕竟有限制   用户态可以分配很大的内存

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
7 [报告]
发表于 2008-09-27 19:44 |只看该作者
拜读了~~~~~~~

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
8 [报告]
发表于 2008-09-28 09:48 |只看该作者

回复 #6 duanius 的帖子

惭愧。这方面的东西以前没怎么整过。不知道兄弟是否方便贴上来一些完整的代码,通过编译和运行兄台的代码来进一步学习。

论坛徽章:
0
9 [报告]
发表于 2008-09-28 10:00 |只看该作者
恩,不错。

论坛徽章:
0
10 [报告]
发表于 2008-09-28 12:44 |只看该作者
原帖由 Godbach 于 2008-9-28 09:48 发表
惭愧。这方面的东西以前没怎么整过。不知道兄弟是否方便贴上来一些完整的代码,通过编译和运行兄台的代码来进一步学习。

呃  要完整的比较困难  因为是在e1000驱动基础上改的 又加入了很多实时内核RTAI的api和机制  
所以代码比较多  而且要8254x网卡和实时内核的支持    我前面发的已经把最重要的几个函数剥离出来了 改改就可以单独运行  这玩意没啥技术含量   几行代码的事情

这里有个mmap机制的例子  非常好  我就是拿这个改的   http://www.kerneltravel.net/journal/v/mem.htm  最底下的例子  跑跑  改改  很不错的
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP