nni_posix_resolv_sysinit(void) 实现了一个初始化函数 nni_posix_resolv_sysinit
,用于设置解析系统(resolver system)。它主要负责初始化解析线程池,用于并发处理域名解析请求。
源码:
cpp
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 的类型:
cpp
//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);
cpp
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]。甚至我们可以通过内存对齐算一下这个值。
cpp
//在文件/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]
初始化之后的值为:
cpp
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
。 - 确保解析线程数至少为 1。
这里不做详细分析,感兴趣的同学请分析并留下链接,大家一起学习。
3. 分配解析线程结构数组
分配 resolv_thrs 数组,大小为 resolv_num_thr ;
分配失败则返回内存不足
4. 初始化和启动解析线程
这里对 resolv_aios逐个(这里是4个,执行同样的操作)进行初始化。调用的函数是不是有点眼熟!nni_thr_init 。
这里的参数类型是 nni_thr (和前面一篇 nni_posix_global_pollq 初始化的成员变量是同一类型)。为方便查看,这里再次贴出nni_thr的类型定义:
cpp
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] 数组元素进行一次初始化操作,初始化之后的值是:
cpp
&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 );
初始化后的值是:
cpp
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列表的初始化、解析并发级别的设置、解析线程结构数组的分配以及解析线程的初始化和启动。整个过程确保解析系统能够高效地并发处理解析请求。
- resolv_worker的执行(待续。。。)