免费注册 查看新帖 |

Chinaunix

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

[网络子系统] 关于PACKET_MMAP映射的问题!!!好神奇!!!! [复制链接]

论坛徽章:
2
IT运维版块每日发帖之星
日期:2016-07-27 06:20:0015-16赛季CBA联赛之福建
日期:2017-03-23 18:32:41
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-11-22 15:09 |只看该作者 |倒序浏览
本帖最后由 HazeC 于 2016-11-22 15:11 编辑

问题产生的来源:最近一直想着内核把数据传递给应用层的方式怎么能快点。ksocket、netlink尝试之后都放弃了,于是打起来mmap的主意。
然后就卡壳了,卡壳的原因是内核传递给应用层的数据大小不是固定的,而我了解到的mmap函数实现里面调用的remap_pfn_range需要的是一块连续的内存。
但是如果是连续内存的话,内存大小就不可控制了。于是想起了用链表存储每个buf,然后remap_pfn_range把链表映射上去。然而看到remap_pfn_range的参数就悲剧了。
查找开源实现,PACKET_MMAP与PF_RING都是采用的mmap方式提交的数据。
首先查看的是PF_RING,看代码
  1. tot_mem = sizeof(FlowSlotInfo) + num_slots * the_slot_len;
  2.   if(tot_mem % PAGE_SIZE)
  3.     tot_mem += PAGE_SIZE - (tot_mem % PAGE_SIZE);

  4.   pfr->ring_memory = rvmalloc(tot_mem);

  5.   if(pfr->ring_memory != NULL) {
  6. #if defined(RING_DEBUG)
  7.     printk("[PF_RING] successfully allocated %lu bytes at 0x%08lx\n",
  8.            (unsigned long)tot_mem, (unsigned long)pfr->ring_memory);
  9. #endif
  10.   } else {
  11.     printk("[PF_RING] ERROR: not enough memory for ring\n");
  12.     return(-1);
  13.   }

  14.   // memset(pfr->ring_memory, 0, tot_mem); // rvmalloc does the memset already

  15.   pfr->slots_info = (FlowSlotInfo *) pfr->ring_memory;
  16.   pfr->ring_slots = (char *)(pfr->ring_memory + sizeof(FlowSlotInfo));

  17.   pfr->slots_info->version = RING_FLOWSLOT_VERSION;
  18.   pfr->slots_info->slot_len = the_slot_len;
  19.   pfr->slots_info->data_len = pfr->bucket_len;
  20.   pfr->slots_info->tot_slots =
  21.     (tot_mem - sizeof(FlowSlotInfo)) / the_slot_len;
  22.   pfr->slots_info->tot_mem = tot_mem;
  23.   pfr->slots_info->sample_rate = 1;
复制代码
PF_RING看样子也是把buf最后放到了一块固定内存里面实现了。
于是看PACKET_MMAP,看代码:

  1.         start = vma->vm_start;
  2.         for (rb = &po->rx_ring; rb <= &po->tx_ring; rb++) {
  3.                 if (rb->pg_vec == NULL)
  4.                         continue;

  5.                 for (i = 0; i < rb->pg_vec_len; i++) {
  6.                         struct page *page = virt_to_page(rb->pg_vec[i]);
  7.                         int pg_num;

  8.                         for (pg_num = 0; pg_num < rb->pg_vec_pages;
  9.                                         pg_num++, page++) {
  10.                                 err = vm_insert_page(vma, start, page);
  11.                                 if (unlikely(err))
  12.                                         goto out;
  13.                                 start += PAGE_SIZE;
  14.                         }
  15.                 }
  16.         }

  17.         atomic_inc(&po->mapped);
  18.         vma->vm_ops = &packet_mmap_ops;
复制代码

此处是PACKET_MMAP的mmap实现!!!vm_insert_page是把buf合并到了一个vma,但是最后的映射函数去哪了。。搜了下整个文件没有。。
为什么!!!

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
2 [报告]
发表于 2016-11-25 12:58 |只看该作者
vm_insert_page里面已经包含set_pte的操作了。

论坛徽章:
2
IT运维版块每日发帖之星
日期:2016-07-27 06:20:0015-16赛季CBA联赛之福建
日期:2017-03-23 18:32:41
3 [报告]
发表于 2016-11-28 16:25 |只看该作者
回复 2# nswcfd

是的、代码看到了把不同快set到同一个vma里面,但问题是没看到把vma映射到应用层去。奇怪的是这一点

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
4 [报告]
发表于 2016-12-02 15:43 |只看该作者
本帖最后由 nswcfd 于 2016-12-02 15:51 编辑
  1. #define DRV_NAME        "mmap"
  2. #define DRV_VERSION        "0.1"

  3. #include <linux/module.h>
  4. #include <linux/kernel.h>
  5. #include <linux/mm.h>
  6. #include <linux/miscdevice.h>

  7. static int mmap_chr_close(struct inode *inode, struct file *file)
  8. {
  9.         printk("chr close\n");
  10.         return 0;
  11. }

  12. int nomap;
  13. module_param(nomap, int, 0);

  14. static struct page *page;

  15. static void mmap_open(struct vm_area_struct *vma)
  16. {
  17.         printk("mmap_open: vma->start=%lx\n", vma->vm_start);
  18. }

  19. static void mmap_close(struct vm_area_struct *vma)
  20. {
  21.         printk("mmap_close: vma->start=%lx\n", vma->vm_start);
  22. }

  23. static struct vm_operations_struct mmap_ops = {
  24.         .open = mmap_open,
  25.         .close = mmap_close,
  26. };

  27. static int mmap_chr_mmap(struct file *file, struct vm_area_struct *vma)
  28. {
  29.         int err = -ENODEV;
  30.         printk("vma->start=%lx, vma->end=%lx\n", vma->vm_start, vma->vm_end);
  31.         if (nomap || !page)        
  32.                 return -ENODEV;
  33.         printk("page=%p, address=%p\n", page, page_address(page));
  34.         *(int *)page_address(page) = current->pid;
  35.         err = vm_insert_page(vma, vma->vm_start, page);
  36.         if (err)
  37.                 return err;
  38.         vma->vm_ops = &mmap_ops;
  39.         return 0;
  40. }

  41. static struct file_operations mmap_fops = {
  42.         .owner        = THIS_MODULE,        
  43.         .llseek = no_llseek,
  44.         .release = mmap_chr_close,
  45.         .mmap = mmap_chr_mmap,
  46. };

  47. #define TUN_MINOR        200
  48. #define MMAP_MINOR        TUN_MINOR

  49. static struct miscdevice mmap_miscdev = {
  50.         .minor = MMAP_MINOR,
  51.         .name = "mmap",
  52.         .fops = &mmap_fops,
  53. };

  54. static int __init mmap_init(void)
  55. {
  56.         int ret = 0;
  57.         page = alloc_page(GFP_KERNEL);
  58.         printk("page=%p, addr=%p\n", page, page_address(page));
  59.         ret = misc_register(&mmap_miscdev);
  60.         if (ret)
  61.                 printk(KERN_ERR "mmap: Can't register misc device %d\n", MMAP_MINOR);
  62.         return ret;
  63. }

  64. static void mmap_cleanup(void)
  65. {
  66.         if (page)
  67.                 __free_page(page);
  68.         misc_deregister(&mmap_miscdev);  
  69. }

  70. module_init(mmap_init);
  71. module_exit(mmap_cleanup);
  72. MODULE_LICENSE("GPL");
  73. MODULE_ALIAS_MISCDEV(MMAP_MINOR);
复制代码

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <fcntl.h>
  4. #include <sys/mman.h>

  5. int main()
  6. {
  7.         int fd = open("mmap_dev", O_RDWR);
  8.         printf("fd=%d\n", fd);
  9.         if (fd < 0)
  10.                 return 1;
  11.         char *p = mmap(0, 10, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
  12.         printf("mmap=%p\n", p);
  13.         if (p != MAP_FAILED)
  14.                 printf("map[0]=%d, pid=%d\n", *(int *)p, getpid());
  15. }
复制代码


# ./mmap_test
fd=3
<4>vma->start=2ad8378f7000, vma->end=2ad8378f8000
<4>page=ffff810000b21b78, address=ffff810012d59000
mmap=0x2ad8378f7000
map[0]=4557, pid=4557
<4>mmap_close: vma->start=2ad8378f7000
<4>chr close

注意kernel执行fops->mmap的参数(vma->start)和用户态mmap返回的数值是一样的。
也就是说,在响应mmap syscall的时候,内核先分配vma(分配用户态线性地址),然后调用fops->mmap回调(关键步骤set_pte映射页表),最后给用户态返回vma->start。

论坛徽章:
2
IT运维版块每日发帖之星
日期:2016-07-27 06:20:0015-16赛季CBA联赛之福建
日期:2017-03-23 18:32:41
5 [报告]
发表于 2016-12-30 13:50 |只看该作者
回复 4# nswcfd

万分感谢。刚看到您的回复,“在响应mmap syscall的时候,内核先分配vma(分配用户态线性地址),然后调用fops->mmap回调(关键步骤set_pte映射页表),最后给用户态返回vma->start。” 这句话解决了疑惑。
现有个问题是如果我把多个buf合并到一个vma的时候,af_packet.c中如下所示伪代码
buf1\buf2\buf3f
start = vma->start
foreach(i in 3)
page = virt_to_page(bufi)
vm_insert_page(vma ,start ,page)
start += buf_size;

这段话的意思是不是将多个buf对应的页插入到一个vma里面去,当用户层调用mmap的时候的时候,用户层获取到的内存是连续的,这个连续的空间就是vma的作用?
换句话说 vma这个实现了把内核多个非连续的内存空间合并成了一个连续的用户空间?

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
6 [报告]
发表于 2016-12-30 20:02 |只看该作者
应该是这样理解吧。
可以看看当用户态的mmap超过一个page的时候,底层的这些callback的调用次数。

论坛徽章:
2
IT运维版块每日发帖之星
日期:2016-07-27 06:20:0015-16赛季CBA联赛之福建
日期:2017-03-23 18:32:41
7 [报告]
发表于 2017-01-03 10:41 |只看该作者
回复 6# nswcfd

万分感谢 解答了疑惑
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP