nni_posix_resolv_sysinit(void) 实现了一个初始化函数 nni_posix_resolv_sysinit
,用于设置解析系统(resolver system)。它主要负责初始化解析线程池,用于并发处理域名解析请求。
源码:
int nni_posix_resolv_sysinit(void) { resolv_fini = false; nni_aio_list_init(&resolv_aios); #ifndef NNG_RESOLV_CONCURRENCY #define NNG_RESOLV_CONCURRENCY 4 #endif resolv_num_thr = (int) nni_init_get_param( NNG_INIT_NUM_RESOLVER_THREADS, NNG_RESOLV_CONCURRENCY); if (resolv_num_thr < 1) { resolv_num_thr = 1; } // no limit on the maximum for now nni_init_set_effective(NNG_INIT_NUM_RESOLVER_THREADS, resolv_num_thr); resolv_thrs = NNI_ALLOC_STRUCTS(resolv_thrs, resolv_num_thr); if (resolv_thrs == NULL) { return (NNG_ENOMEM); } for (int i = 0; i < resolv_num_thr; i++) { int rv = nni_thr_init(&resolv_thrs[i], resolv_worker, NULL); if (rv != 0) { nni_posix_resolv_sysfini(); return (rv); } } for (int i = 0; i < resolv_num_thr; i++) { nni_thr_run(&resolv_thrs[i]); } return (0); }
过程分析:
1. 对全局变量resolv_aios初始化:
这里首先贴一下resolv_aios 的类型:
//nni_list_node 的类型定义: typedef struct nni_list_node { struct nni_list_node *ln_next; struct nni_list_node *ln_prev; } nni_list_node; //nni_list 的类型定义: typedef struct nni_list { struct nni_list_node ll_head; size_t ll_offset; } nni_list; //resolv_aios 的定义 static nni_list resolv_aios;
基本流程:首先初始化全局变量 resolv_fini = false; 紧接着初始化了全局变量 resolv_aios
这里简单插一句初始化的过程:nni_aio_list_init(&resolv_aios);
void nni_aio_list_init(nni_list *list) { NNI_LIST_INIT(list, nni_aio, a_prov_node); } #define NNI_LIST_INIT(list, type, field) \ nni_list_init_offset(list, offsetof(type, field)) void nni_list_init_offset(nni_list *list, size_t offset) { list->ll_offset = offset; list->ll_head.ln_next = &list->ll_head; list->ll_head.ln_prev = &list->ll_head; }
简单分析一下就是,nni_aio_list_init()函数对全局变量 resolv_aios 的初始化操作。而nni_aio_list_init 的定义是一个宏:NNI_LIST_INIT,这个是通用的宏定义,其他初始化也会用到。在这个宏里,传入了3个参数:
第一个就是我们的全局变量的地址:&resolv_aios
第二个是 type类型,这里是 nni_aio 这个结构体
第三个是 a_prov_node
在宏里调用了函数 nni_list_init_offset (list, offsetof(type, field))这里的type就是 nni_aio 而 field是 a_prov_node。 offsetof是一个内核中常用的宏,用于计算结构体类型距离结构体首地址的偏移量。我们可以在代码中加一行计算这个值并输出:offserof aio -> a_prov_node=[408]。甚至我们可以通过内存对齐算一下这个值。
//在文件/src/platform/posix/posix_resolv_gai.c的480行添加下面的代码 printf("offserof aio -> a_prov_node=[%d]\n", offsetof(nni_aio, a_prov_node)); //我们将看到输出是 offserof aio -> a_prov_node=[408]
初始化之后的值为:
resolv_aios->ll_ofset = 408; resolv_aios->ll_head.ln_next = &resolv_aios->ll_head; resolv_aios->ll_head.ln_prev = &resolv_aios->ll_head;
当然我们也可以直接在初始化完成后进行直接输出,经过验证:resolv_aios.ll_offset=[408];其实就是对全局变量 resolv_aios 的指针及 ll_offset 字段进行了初始化。结果如上。
resolv_fini
被设置为 false
,表示解析系统还未完成。nni_aio_list_init
函数初始化一个异步I/O列表 resolv_aios
,用于存储待处理的解析请求。2. 解析并发级别设置
NNG_RESOLV_CONCURRENCY
,则将其定义为 4。nni_init_get_param
获取解析线程数,默认值为 NNG_RESOLV_CONCURRENCY
。这里不做详细分析,感兴趣的同学请分析并留下链接,大家一起学习。
3. 分配解析线程结构数组
分配 resolv_thrs 数组,大小为 resolv_num_thr ;
分配失败则返回内存不足
4. 初始化和启动解析线程
这里对 resolv_aios逐个(这里是4个,执行同样的操作)进行初始化。调用的函数是不是有点眼熟!nni_thr_init 。
这里的参数类型是 nni_thr (和前面一篇 nni_posix_global_pollq 初始化的成员变量是同一类型)。为方便查看,这里再次贴出nni_thr的类型定义:
struct nni_thr { nni_plat_thr thr; nni_plat_mtx mtx; nni_plat_cv cv; nni_thr_func fn; void * arg; int start; int stop; int done; int init; }; //上面结构体的成员变量 struct nni_plat_thr { pthread_t tid; void (*func)(void *); void *arg; };
在 nni_thr_init 函数中,对每个resolv_thrs[i] 数组元素进行一次初始化操作,初始化之后的值是:
&resolv_thrs[i]->done = 0; &resolv_thrs[i]->start = 0; &resolv_thrs[i]->stop = 0; &resolv_thrs[i]->fn = resolv_worker; &resolv_thrs[i]->arg = NULL;
同样,对thr->thr 也会进行初始化,执行函数nni_plat_thr_init (&thr->thr, nni_thr_wrap, thr );
初始化后的值是:
nni_thr的成员变量 nni_plat_thr nni_thr->nni_plat_thr->func = nni_thr_wrap; nni_thr->nni_plat_thr->arg = nni_thr;
接下来创建一个线程, 线程的id 就是 nni_thr->nni_plat_thr->tid,线程属性沿用了前面初始化的线程属性。 执行线程函数 nni_plat_thr_main。在线程函数中,会执行函数
nni_thr->func(nni_thr->arg);这里就执行nni_thr初始化的函数 resolv_worker,回顾上面的初始化过程可知,参数是NULL。
这段代码通过设置并发解析线程池来初始化解析系统,用于处理异步域名解析请求。初始化过程中包括全局变量和AIO列表的初始化、解析并发级别的设置、解析线程结构数组的分配以及解析线程的初始化和启动。整个过程确保解析系统能够高效地并发处理解析请求。
5. resolv_worker的执行(待续。。。)