dpdk是通过许多不同的纬度来加速包处理的,其中主要包括:
hugepage大页内存(进程使用的是虚拟地址,一般页表(4k)能映射的虚拟地址空间有限,使用大页能减少换页次数提高cache命中,通过mmap把大页映射到用户态的虚拟地址空间有用过mmap的都知道这是实现共享内存的手段,所以dpdk还支持多进程共享内存)
cache预取 (每次预读当前数据相邻前后的数据),批量操作数据,cache line对齐(通过浪费一点内存将要操作的数据对齐)
接管了网卡用户态驱动使用轮询而不是网卡中断
将网卡rx tx队列映射到用户态空间实现真正的零拷贝(传统堆栈至少也得一次拷贝,因为队列空间在内核而内核和用户态使用不同的地址空间)(传统堆栈为了支持通用性,例如ipx等其他网络,将包处理过程分了很多层次,层之间的接口标准统一数据结构就需要转换,无形中带来了巨大的成本,如osi七层模型而实用的就是tcp/ip四层模型)
线程绑定cpu
支持NUMA,不同的core属于不同的node,每个node有自己的mempool减少冲突
无锁环形队列(冲突发生时也是一次cas的开销)
dpdk通过tools/dpdk-setup.sh的脚本,通过编译、挂载内核模块, 绑定网卡(先把网卡ifconfig down),设置hugepage后就可以使用了。
在内核模块igb加载时,会注册pci设备驱动
static struct pci_driver igbuio_pci_driver = {
.name = "igb_uio",
.id_table = NULL,
.probe = igbuio_pci_probe,
.remove = igbuio_pci_remove,
};
在绑定网卡时,会调用igbuio_pci_probe,使用用户态驱动uio接管网卡(中断处理、mmap映射设备内存到用户空间)
系统启动时,bios会将设备总线地址信息记录在/sys/bus/pci/devices,dpdk程序启动时会去这里扫描pci设备,根据不同类型的NIC有对应的初始化流程。在后面配置队列的时候会把用户态的队列内存地址通过硬件指令交给NIC,从而实现零拷贝。
如果NIC收到包,会做标记,轮询的时候通过标记取数据包
while (nb_rx < nb_pkts) {
/*
* The order of operations here is important as the DD status
* bit must not be read after any other descriptor fields.
* rx_ring and rxdp are pointing to volatile data so the order
* of accesses cannot be reordered by the compiler. If they were
* not volatile, they could be reordered which could lead to
* using invalid descriptor fields when read from rxd.
*/
rxdp = &rx_ring[rx_id];
staterr = rxdp->wb.upper.status_error;
if (! (staterr & rte_cpu_to_le_32(E1000_RXD_STAT_DD)))
break;
rxd = *rxdp;
发包的轮询就是轮询发包结束的硬件标志位,硬件发包完成会写回标志位,驱动发现后再释放对应的描述符和缓冲块。
KNI
通过创建一个虚拟网卡,将收到的包丢给协议栈
/* 发送skb到协议栈 */
/* Call netif interface */
netif_receive_skb(skb);
POWER
在负载小的时候没有必要使用轮询模式,这时可以打开网卡中断 使用eventfd epoll通知用户层
Ring
无锁环形队列的核心就是操作头尾索引,先将头尾索引赋给临时变量,再把尾索引往后跳n个位置,利用cas判断头如果还是在原来的位置就指向尾否则就重复这个过程,然后在操作中间跳过的n个元素就是安全的了,此时头尾索引应该指向同一个位置,如果不同应该是有别的线程也在操作,重复等待即可。(这里有个细节,索引是只加不减的,因为是环形队列索引又是unsigned 32bits,所以每次取数据前把索引模队列长度-1, uint32_t mask; /**< Mask (size-1) of ring. */即可)
Windows下使用vmware虚拟机的时候出现EAL: Error reading from file descriptor,根据网上的说法打了patch还是不行,后来尝试挂载内核模块的时候不加载vfio模块就可以了