总体结构
自定向下分别是物理内存,memzone,mempool和pktmbuf。
启动时heap内存分配的API
关键的数据结构是:
struct malloc_heap跟踪空闲的空间。
struct malloc_elem作为基本的alloc/free的单元。每一个block都是2的指数大小。
代码是:
memzone_reserve_aligned_thread_unsafe
->malloc_heap_alloc
->malloc_heap_alloc_on_heap_id
-> heap_alloc
-> find_suitable_element
-> malloc_elem_free_list_index(size) // 在free list找到可用的.
https://doc.dpdk.org/guides-2.0/prog_guide/malloc_lib.html
rte_malloc相关的API
不推荐在实时核(RT core)使用,因为rte_malloc本身有spin lock。分配出来的内存是在同一个DPDK instance里共享的,使用的是hugepage的内存,比malloc性能更好,rte_malloc的方式比pool内存池的方式更慢一些。可以在配置的代码使用,或者启动时使用,而不是运行时大量收发包的代码。
类型 | malloc | rte_malloc |
huge page | 不强制 | 默认 |
numa node pin | 不强制 | 默认 |
访问IOVA | 否 | 是 |
支持IOVA连续内存 | 不强制 | 是 |
cache align分配 | 不强制 | 强制 |
分配时align | 是 | 是 |
多处理器共享 | 否 | 是 |
多进程安全 | 否 | 是 |
rte_malloc
->rte_malloc_socket
->malloc_socket
->malloc_heap_alloc(type, size, socket_arg, 0, align == 0 ? 1 : align, 0, false);
mempool相关的API
DPDK mempool是固定size的object,性能优化做了很多。
rte_mempool_create() // 分配N个可用size为elt_size的buffer,里面会调用rte_mempool_create_empty()
如果有1GB的page,先用1GB的,因为有RTE_MEMZONE_1GB。否则可以用其他的比如2MB的,因为有RTE_MEMZONE_SIZE_HINT_ONLY。
rte_mempool_get() // consumer uses this for geting one memory entity out of memory pool of N entities
rte_mempool_put() // return back (free) the memory entity back to mempool
如果要替换mempool相关的操作,可以用rte_mempool_set_ops_byname(mp, NAME, NULL)。
mempool manager的库是一个框架,独立的。
mempool_cache_init()用于分配cache。
rte_mempool_ops_table()会注册一系列函数。
rte_mempool_register_ops()会注册mempool的操作函数。
rte_mempool_generic_get()可以拿到多个object。
rte_mempool_create_empty
->rte_mempool_calc_obj_size
->rte_mcfg_mempool_write_lock
->rte_zmalloc("MEMPOOL_TAILQ_ENTRY", sizeof(*te), 0);
->rte_zmalloc_socket(type, size, align, SOCKET_ID_ANY);
->rte_memzone_reserve(mz_name, mempool_size, socket_id, mz_flags);
->rte_memzone_reserve_thread_safe()
->memzone_reserve_aligned_thread_unsafe()
// check alignment
// alloc memory from heap
// if no size constraints
->malloc_heap_alloc_biggest(NULL, socket_id, flags, align, contig);
// or
->malloc_heap_alloc(NULL, requested_len, socket_id, flags, align, bound, contig);
->malloc_elem *elem = malloc_elem_from_data(mz_addr)
/* fill the zone in config */
->mz_idx = rte_fbarray_find_next_free(arr, 0);
// fill memzone info, iova, addr, len, hugepage_sz, socket_id, flags.
->rte_mcfg_tailq_write_lock();
->TAILQ_INSERT_TAIL(mempool_list, te, next);
->rte_mcfg_tailq_write_unlock();
->rte_mcfg_mempool_write_unlock();
每个obj都有header和trailer,obj的size是elt_size。
mbuf相关的API
mbuf ==> eth packet,是针对以太网的包设计的,不建议作为本地的buffer使用。本地buffer可以用自定义的mempool的block,从而避免额外的mbuf header消耗。
rte_pktmbuf_pool_create_by_ops(), ops_name可以把operation替换掉,比如改为使用lf_stack。就是用了rte_mempool_stack.c里的stack方式,而不是默认的ring方式。dpdk/drivers/mempool/目录下有相关的处理函数。
rte_pktmbuf_raw_alloc是从pktmbuf_pool里取一个mbuf,因为是raw_alloc,所以不用初始化很多field,速度非常快。
rte_pktmbuf_pool_private()是私有的空间,size是data_room的大小。
data_len是每个mbuf的有效数据长度,buf_len是每个mbuf的总长度,pkt_len是多个mbuf的data_len之和。mbuf里的buf_len字段大小是u16,所以最大只支持64kB,除非使用mbuf chain,pkt_len最大可以达到u32,4GB。
mbuf external buffer
mbuf加上external data buffer,就像车子拉了一个后备的小拖车。
rte_pktmbuf_attach/detach_extbuf()
目前不在DPDK的主分支。
DPDK eal关于mem的配置参数
18.11后有这3个参数。
–single-file-segment,表示map的地址都放在一个文件里,节约文件句柄。
–socket-mem,表示这个socket一启动就分配这么多内存。动态模式下,这些内存是固定分配好的,app退出才会释放。
–socket-limit,这个socket最多只能分配这么多内存。legacy模式不支持。
IOMMU的IOVA模式
通过–iova=pa或者–iova=va即可选择。默认是PA模式。
IOVA as VA,可以确保不连续的PA地址段被连续使用。
IOVA as PA,虚拟地址连续的,物理地址也要连续。
有些驱动不支持iommu为iova=va的模式,可能是PMD的限制,也可能是有服务必须要用PA。
18.11版本后,在IOVA as VA的模式下,VA连续不一定IOVA也连续。ring,mempool,hash表只需要VA连续。
调试时显示内存情况
这些API可以用于调试,看DPDK的内存使用情况。
rte_dump_physmem_layout(stdout); // MEMORY_SEGMENTS,看用了几个hugepage
rte_memzone_dump(stdout); // MEMORY_ZONES,可以看zone间是否连续
rte_dump_tailq(stdout); // TAIL_QUEUES,RTE_RING和RTE_MEMPOOL的一般有first和last,其它的tail q一般只有last
/* MALLOC_STATS, 看每个heap,各自的alloc和free的size/count是多少。
Heap_size是总共的(=Free_size+Alloc_size)。
Free_size是可用的。
Alloc_size是已申请的。
Alloc_count是当前申请的(allocated and used),对应rte_malloc status BUSY。
Free_count是DPDK用rte_malloc申请过,又释放了的(allocated and freed),对应rte_free status FREE。总的list size是Alloc_count+Free_count
如果type是NULL,表示dump所有类型的memory。这里的heap id对应NUMA。比如heap id 1(从1开始)对NUMA 0(从0开始)。如果只有一个NUMA,那么就heap id 1的数据有效,后续的是空的。*/
rte_malloc_dump_stats(stdout, NULL);
rte_malloc_dump_heaps(stdout); // MALLOC_HEAPS,看哪些是FREE的,哪些是BUSY的。
rte_malloc_dump_stats_no_numa(stdout, "socket_mem"); // MALLOC_TOTAL_STATS,注意Free_size的也是被DPDK reserve的,不能被linux直接使用的。
原文链接:https://mp.weixin.qq.com/s/9gm8tJgtsPJ7BL8AJx4jvw
学习更多dpdk视频
Dpdk/网络协议栈/ vpp /OvS/DDos/NFV/虚拟化/高性能专家 学习地址: https://ke.qq.com/course/5066203?flowToken=1043799
DPDK开发学习资料和学习路线图分享有需要的可以自行添加学习交流q 君羊909332607备注(XMG) 获取