关注过零拷贝网卡抓包的朋友都知道,零拷贝里面很重要的一点就是,用户程序直接可以取得来自内核的包,这样就去掉了层层拷贝,同时也不占用服务器的cpu。这里大致的说一下我这边对这个的理解,
结合我现在能运行的程序对其进行一个简单的描述。
1、首先我们需要在dev下面建立一个虚拟设备,名字叫做zcmem,这里有两个方法可以建立,第一个方法是直接mknod指令创建设备,第二种是使用 misc_register函数创建设备。此次我是使用第二种方法建立的设备。
这个虚拟设备就变成了用户程序和内核之间的连接纽带。内核会往这个地方写数据,用户程序从这里取数据。
2、在内核层次我们会定义一个全局的struct file_operation, 大致会定义出来一下这些成员函数。然后将这个全局变量赋值给第一步中的 struct miscdev的fops成员。
.open = zc_open, //当用户程序执行open("/dev/zcmem", O_RDWR) 的时候会调用此函数,同时还会生成一个struct file指针, 我们可以再这里给file的private_data赋值。然后在下面所示函数中将对应的结构取出来
.release = zc_release, //释放函数
.unlocked_ioctl = zc_unlocked_ioctl, //处理用户程序中的ioctl指令
.mmap = zc_mmap, //在这个里面做映射,将内核中申请的内存映射到用户空间
3、用户进程打开fd =open("/dev/zcmem", O_RDWR),利用ioctl发送控制信息
4、利用dev_get_by_name将真正的网卡设备取出来和你建立的虚拟设备绑定在一起,其实也就是为之后的remap_pfn_range映射做准备,并且在内核空间中申请内存。
5、用户程序里面使用mmap映射mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 注意,这里的fd是步骤3中打开fd。
注:这里会做一下操作:1)在进程的虚拟空间查找一块vma 2)对vma进行映射 3)调用驱动程序中的mmap函数,就是上面的zc_mmap。4)将vma插入到vma链表中
6、驱动程序调用zc_mmap函数,注意此函数的第二个参数就是5中生成的vma结构体指针struct vm_area_struct,此结构包括了映射范围。内核空间使用remap_pfn_range将在内核申请的空间映射到此结构对应的范围即可.
经过以上几步,内核就将数据写到用户程序可以直接取得地方了。
阅读(1512) | 评论(0) | 转发(0) |