r2p2之DPDK学习记录
简单理解
DPDK解决的问题是网络IO的性能问题。网卡、CPU等硬件在高速发展,但是软件开发却没有跟上节奏,使得网络IO碰上瓶颈。又因为网络IO实现大都需要通过内核,所以很明显,内核是瓶颈所在。因此,提高网络IO性能的方法着手点就是避开内核。
DPDK做的就是让驱动运行在用户态,也就是说DPDK的代码大部分是放在用户态下的。除了中断无法在用户态下处理,所以把处理中断的代码放在内核下。实现DPDK的基石是UIO机制,使用UIO可以通过read感知中断,通过mmap实现和网卡的通讯。而DPDK核心优化是通过PMD驱动实现的。PMD是用户轮询程序,它主动询问设备是否需要处理。例如,它主动询问网卡接受队列是否有新的数据要接受,如果有,就取走报文进行处理,处理完毕再次循环。
传统的收发包的流程是这样,当网络设备接受到数据时,DMA模块会自动将数据保存起来并通过中断通知CPU来取。要发送数据时,内核会根据系统路由器表选择相应的网络接口进行数据传输,发送完之后,网卡驱动程序也会启动硬中断通知CPU释放数据缓存区中的数据包。
而DPDK使用了PMD驱动,该驱动使用无中断方式直接操作网卡的接收和发送队列(除了链路状态通知(啥东西?)仍必须采用中断方式以外)PMD驱动从网卡上接收到数据包后,会直接通过DMA方式传输到预分配的内存中,同时更新无锁环形队列中的数据包指针,不断轮询的应用程序很快就能感知收到数据包,并在预分配的内存地址上直接处理数据包。
DPDK内存数据结构
DPDK的工作就是对网络报文进行高速处理,而报文需要内存承载。但是如果每次处理报文时,还是通过频繁地调用malloc和free来申请和释放内存的话,那性能也优化不了多少。所以DPDK使用内存池来复制内存你申请释放。
DPDK会将内存封装在mbuf(struct rte_mbuf)结构体内。单个mbuf的结构定义如图所示:
DPDK对网络帧的封装是这样:将网络帧元数据(metadata)和帧本身存放在固定大小的同一段缓存中。metadata的一部分内容由DPDK的网卡驱动写入。
mbuf头部现在是占两个CacheLine,一般是基础性,频繁访问的数据放在第一个CacheLine字节中,而将功能性扩展的数据放在第二个CacheLine字节中。在mbuf报头中包含包处理所需的所有数据,如果单个mbuf存放不了,那么mbuf的前端还有指向下一个mbuf结构的指针(m->pkt.next)来形成帧链表结构。
headroom的起始地址保存在mbuf的buff_addr指针中。数据帧的起始指针可以通过调用rte_pktmbuf_mtod(mbuf)来获得。
创建的函数为rte_pktmbuf_alloc()或rte_ctrlmbuf_init()。这两个函数初始化了一些关键信息:mbuf类型,所属内存池,缓冲起始地址等。
释放一段mbuf实际上将其放回所属的内存池,原本缓存在其中的内容在被重新创建之前不会被初始化,依然保存在其中。(那可以使用吗?)
参考博客: