gdb前的准备
如果想要调试到dpdk源码的内部,则需要在编译的时候指定一些命令行参数:
meson -Dexamples=all -Dbuildtype=debug build
这样我们就可以进入到 ./build/app进行gdb调试了:
无情gdb
rte_cpu_is_supported
dpdk运行时会检查cpu是否支持,它通过cpuid将cpu的具体信息存放在4个寄存器中,这里feat leaf为1即EBX=1存放处理器签名(Processor Signiture)和功能(Feature)位。dpdk实现建立一个fearture_map,即检查哪个寄存器的某一位来查看cpu是否支持dpdk功能。
__atomic_compare_exchange_n
原子操作,run_once置1
pthread_self
获取主线程的线程id,他肯定是主线程,因为还没创建别的线程呢。
rte_eal_cpu_init
这一步是读取cpu信息,并填充到每一个lcore_config[lcoreid]中,我的虚拟机是1个真实CPU虚拟出的4个逻辑cpu,numa有1个节点node0,包含cpu0,cpu1,cpu2,cpu3。
可见DPDK的逻辑核赋值为4个,其中coreid即实体cpu id只有一个,且numa node也只有一个。dpdk实现默认情况下,dpdk逻辑核映射到CPU中为一一对应,因此cpuset分别1,2,4,8 -》cpu0,cpu1,cpu2,cpu3->lcore0, lcore1,lcore2,lcore3。后面未用到的lcore的index会置为-1。
eal_parse_args
这一步是进行命令行解析,我一般运行时加的时会用 -c f -n 4指定可以被dpdk接管的CPU以及内存读写采用4通道。命令行解析的关键函数为
opt = getopt_long(argc, argvopt, eal_short_options, eal_long_options, &option_index))
它通过事先指定的短命令和长命令进行解析,其中optionindex为解析到长option在eallong_options数组的索引,opt为解析到短option字符即c和n:
- 对于c指定cpu掩码来说,首先看是否与之前的已经设为服务核,接着调用eal_parse_coremask,解析得到新的ldcore[],再用新值去更新update_lcore_config,如果dpdk逻辑核为-1对应内部配置逻辑核应该ROLE_OFF。如果是缺省的,那么会调用eal_auto_detect_cores获取可用的逻辑核,关键函数为pthread_getaffinity_np可以获取当前线程可以被哪些CPU调度,并默认lcoreid0为master core.
- 对于n指定内存通道数时,实现很简单conf->force_nchannel = atoi(optarg);
- 接着会创建运行时目录,run_dir
XDG_RUNTIME_DIR:指向用户专用用户可写目录的路径,该目录与计算机上的用户登录时间绑定。它是在用户首次登录时自动创建的,并在用户的最终注销时被删除。如果用户登录一次,然后再次注销,然后再次登录,则目录之间的内容将丢失
- DPDK的线程分为控制线程和数据线程,控制线程默认绑定到MASTER核上,除非仍有CPU可用,主要逻辑为首先将之前绑定的cpuset和控制线程的cpuset做或运算,然后取反,接着与当前线程的调度cpuset作与运算。
eal_plugins_init
插件机制,就是动态库,首先会通过NOLOAD加载.so测试当前运行程序是否是动态链接库加载dpdk,如果是静态库加载直接返回,否则将会加载插件,插件的路径可以由-d参数指定,也可以由RTE_EAL_PMD_PATH(/usr/local/lib64/dpdk/pmds-21.0)指定,函数会加载-d指定的so或者将指定路径下满足条件的插件加入管理(eal_plugin_add),并通过dlopen加载
eal_trace_init
这个函数是对一些事件或变量进行跟踪,一般用于多线程同步,等待时间测量等。DPDK使用的是CTF格式 ,为了记录非常频繁的底层操作,一般开销为20个时钟周期。使用option OPT_TRACE_NUM和OPT_TRACE_DIR_NUM可以开启这个功能,这部分实现太复杂了,后续debug dpdk-test时专门写一个跟踪器的分析。我这边直接return掉。
rte_config_init
创建/var/run/dpdk/rte/config文件,并将主进程的虚拟空间地址(void *) 0x100000000(Linux kernel uses a really high address as starting address for serving mmaps calls) mmap该文件中,映射的配置大小为sysconf(_SC_PAGE_SIZE)的倍数,这也是内存对齐。最后会把这个地址记录下来,后续从进程也会参照这个使用一样的逻辑地址。
关键库函数:
sysconf 函数用来获取系统执行的配置信息。例如页大小、最大页数、cpu个数、打开句柄的最大个数等等。
ftruncate()会将参数fd指定的文件大小改为参数length指定的大小。参数fd为已打开的文件描述词,而且必须是以写入模式打开的文件。
fcntl函数功能依据cmd的值的不同而不同,fcntl(mem_cfg_fd, F_SETLK, &wr_lock)设置文件读写锁。
mmap将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系,函数返回被映射区的指针
rte_eal_intr_init
这个函数是创建处理中断的线程,这个线程也是绑定在master core上,它首先创建一个pipe用来后续与slave 线程通信,然后epoll add监听这个fd,直到有数据时才会处理。另外,uio device file descriptor 也会加入到监听,这个就是在用户空间模拟设备中断处理,我这边没有配置设备直接跳过。中断线程初始化和亲和性绑定之后设置了线程屏障,refcnt=2,即等待中断线程创建完毕后主线程才继续rte_eal_int()剩下函数。
pipe函数来创建管道用于进程间通信,fd参数返回两个文件描述符,fd[0]指向管道的读端,fd[1]指向管道的写端。fd[1]的输出是fd[0]的输入。
epoll_create(); // 创建监听红黑树
epoll_ctl(); // 向书上添加监听fd
epoll_wait(); // 有监听fd事件发送--->返回监听满足数组--->判断返回数组元素
barrier:多个线程约定一个栅栏,只有当所有的线程都达到这个栅栏时,栅栏才会放行,否则到达此处的线程将被阻塞
rte_eal_alarm_init
简单的函数,timerfd_create创建文件描述符来通知定时器是否到期
rte_eal_timer_init
简单的函数,获取cpu时钟运行频率(tsc_hz)
memory相关
- rte_mp_channel_init
unlink:执行unlink()函数并不一定会真正的删除文件,它先会检查文件系统中此文件的连接数是否为1,如果不是1说明此文件还有其他链接对象,因此只对此文件的连接数进行减1操作。若连接数为1,并且在此时没有任何进程打开该文件,此内容才会真正地被删除掉
flock:文件锁是一种文件读写机制,在任何特定的时间只允许一个进程访问一个文件
socket:多进程通信,sockert()->bind()->connect()->accept()->read()/write()
新建unix socket用于主从,从从进程通信,并新建mp处理线程监听消息并根据msg name进行相应的action(msg, s->sun_path)处理。
- eal_hugepage_info_init
读取巨页内存信息
- rte_eal_memzone_init
DPDK学习记录9 - 内存初始化2之rte_eal_memzone_init_jeawayfox的博客-CSDN博客_dpdk eal
rte_eal_memzone_init主要就是调用了rte_fbarray_init。fbarry_init用来创建一块内存,存放一组element,每个element的大小相同。
这组element的长度为RTE_MAX_MEMZONE=2560个,每个element的大小是sizeof(struct rte_memzone)=72bytes,申请并初始化成功之后挂到rte_config->mem_config->memzones下,且命名为“memzone”。
更新全局变量next_baseaddr(之前mmap地址是0x1000000000),作为下次进入申请时的baseaddr。
- rte_eal_memory_init
dpdk内存管理——内存初始化-lvyilong316-ChinaUnix博客
mmap系统调用可以设置为共享的映射,dpdk的内存共享就依赖于此,在这多个进程中,分为两种角色,第一种是主进程(RTE_PROC_PRIMARY),第二种是从进程(RTE_PROC_SECONDARY)。主进程只有一个,必须在从进程之前启动,负责执行DPDK库环境的初始化,从进程attach到主进程初始化的DPDK上,主进程先mmap hugetlbfs文件,构建内存管理相关结构将这些结构存入hugetlbfs上的配置文件rte_config,然后其他进程mmap rte_config文件,获取内存管理结构,dpdk采用了一定的技巧,使得最终同样的共享物理内存在不同进程内部对应的虚拟地址是完全一样的,意味着一个进程内部的基于dpdk的共享数据和指向这些共享数据的指针,可以在不同进程间通用。
这块有点复杂啊,后面再看吧,怪我过分菜-_-|||。
- rte_eal_malloc_heap_init
DPDK学习记录12 - 内存初始化5之rte_eal_malloc_heap_init_jeawayfox的博客-CSDN博客
DPDK : 解析内存初始化的过程_pcokk的博客-CSDN博客_eal_legacy_hugepage_init
用于多进程环境下的内存分配,其中每一个socket会对应一个heap
rte_eal_log_init
设置日志写入函数和日志流,用于写入标准错误输出(终端)或syslog.conf配置的日志路径中
openlog:此函数用来打开一个到系统日志记录程序的连接,打开之后就可以用syslog或vsyslog函数向系统日志里添加信息了
fopencookie:此函数定义文件流的写入,关闭等函数
for_each_worker_thread_create
这个会在后面解析example:dpdk-helloworld分析
设备扫描
- rte_bus_scan
- rte_bus_probe
首先进行总线注册(dpaa,fslmc,ifpga,pci,vdev,vmbus),然后遍历各个总线上的设备和驱动,接着以具体型号的设备以参数形式进行驱动绑定,完成在用户空间的文件接口注册。
以PCI 设备举例,
pci_scan_one (扫描/sys/bus/pci/devices目录下的设备)
-eal_parse_sysfs_value (/* get device id */) -》
-pci_get_kernel_driver_by_path(扫描/sys/bus/pci/devices/driver目录下的设备驱动) -》
rte_pci_match(检查驱动知否支持设备)-》
rte_pci_map_device (设备分配资源) -》
dr->probe(设备驱动绑定)
Reference
dpdk_lcore_note_DPDK_lcore_学习笔记_Andrew Yang's Note-CSDN博客
DPDK — IGB_UIO,与 UIO Framework 进行交互的内核模块 - 走看看 (zoukankan.com)
Intel CPU的CPUID指令(转载)_weixin_34337265的博客-CSDN博客
PCIe总线_yundanfengqing_nuc的专栏-CSDN博客
集成存储器控制器(IMC)功能_zdx19880830的专栏-CSDN博客_imc控制器
/proc/cpuinfo 文件分析(查看CPU信息)_GoodIdea-CSDN博客_cpuinfo怎么看
多核多线程——pthread_setaffinity_np,cpulimit分析CPU资源对应用程序的影响_weixin_34268579的博客-CSDN博客
DPDK跟踪库:trace library_RToax-CSDN博客
内存序列-memor order_字节跳动 内推找我-CSDN博客_memory_order_relaxed
Linux fcntl函数详解 - 夕相待 - 博客园 (cnblogs.com)
DPDK之内存管理 - AISEED - 博客园 (cnblogs.com)
DPDK 中断机制 eal_intr_handle_interrupts_ZP1015-CSDN博客
如果这篇文章说不清epoll的本质,那就过来掐死我吧! (1) - 知乎 (zhihu.com)
进程间的通信方式——pipe(管道)_sky_Mata的博客-CSDN博客_管道通信
Linux编程- pthread_barrier_xxx介绍_Ailson Jack的专栏-CSDN博客
epoll原理详解及epoll反应堆模型_青萍之末的博客-CSDN博客_epoll
IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇)_智障大师 的专栏-CSDN博客_同步 异步 阻塞 非阻塞
阻塞、非阻塞、异步、同步以及select/poll和epoll_李春喜的专栏-CSDN博客_epoll 同步非阻塞
linux timerfd_create说明简单翻译_Amrf的博客-CSDN博客
linux日志系统介绍 —— syslog(),openlog(),closelog()_wangyuling1234567890的专栏-CSDN博客_openlog
fopencookie函数的使用说明_insswer的专栏-CSDN博客
总线、设备、驱动模型_笔记专栏-CSDN博客_总线设备驱动模型
DPDK内存篇(一): 基本概念_weixin_37097605的博客-CSDN博客