完整代码解读 ⬇
主要功能
aiocompleter.cpp
文件的整体功能是用于AIO 完成器线程完成预取(Prefetch
)和后端写入(BackWrite
)I/O操作
-
处理异步IO请求
-
AIO 完成器线程使用
Linux Native AIO
完成 AIO 请求。 -
单个 AIO 完成器线程为与特定 AIO 上下文和 I/O 优先级相关联的 AIO 队列提供服务。
-
-
启动与停止
-
启动
当
postmaster
启动子进程结束或是在执行归档恢复的开始,AIO完成器线程会立即启动,并且持续保持开启状态,直到postemaster
下达终止命令-
正常终止 -> SIGTERM指令:指示线程等待任何挂起的 AIO 并做好退出准备。
-
紧急终止 -> SIGQUIT指令:当AIO完成器线程意外退出,
postmaster
会将其视为“后端崩溃”状态,共享内存可能遭受损坏,此时通过SIGQUIT指令终止剩余的后端后启动恢复循环
-
-
停止
只能在所有工作线程或后台数据写(
bgwriter
)线程都已停止后,AIO完成器线程才能停止。
-
部分函数
1. Compltrfork_exec
功能:启动异步I/O Completer线程,但是在当前的实现中,OpenGauss
并不支持异步I/O,因此该函数只会发出错误消息表示不支持,并返回无效的线程ID。
在传统的设计中,进程启动时可以通过命令行参数和共享内存传递参数。而异步I/O线程是通过命令行参数传递 compltrIdx
(异步I/O线程的索引),并允许运行中的线程在全局上下文中根据这个参数在 compltrArray
中找到自己的描述符,而不会改变所有。Compltrfork_exec
函数通过格式化参数列表来准备执行异步I/O Completer线程的命令。然后使用 fork
和 exec
函数来创建一个新的线程来执行异步I/O Completer线程的处理。在这过程中,compltrIdx
会被转换为一个3字符的字符串,依次避免特殊处理该参数。
ThreadId Compltrfork_exec(int compltrIdx)
{
// 发出错误消息,表示不支持异步I/O
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("openGauss current do not support AIO")));
// 返回无效的线程ID
return InvalidTid;
}
2. AioCompltrStart
功能:设置 Aio Completer
线程描述符并启动所有 Aio
完成器线程,确保它们能够处理异步I/O请求并提供响应。
-
检查要启动的异步I/O Completer线程数量是否超过了最大限制。
-
初始化
compltrArray
线程数组。 -
遍历,处理每个要启动的异步I/O Completer线程:
-
为线程分配对应的
compltrDescp
元素参数,以确定线程的处理方式和参数。 -
创建I/O队列并填充上下文,以便监视和处理I/O事件。
-
为线程分配事件数组内存,以便存储异步I/O事件的信息。
-
-
调用
Compltrfork_exec
函数启动异步I/O Completer线程,并将线程标识符存储在线程描述符中。 -
将全局变量
AioCompltrReady
设置为true
,表示异步I/O Completer线程已经准备就绪。
如果在上述步骤中出现任何错误,函数将调用AioCompltrStop
函数停止已启动的线程并清理资源,然后返回错误码。
int AioCompltrStart(void)
{
int error = 0;// 错误码初始化
int try_times = 0;// 尝试次数初始化
//检查当前配置的 AIO Completer 线程数量是否超过了预定义的最大数量 MAX_AIOCOMPLTR_THREADS
if (AioCompltrThreads > MAX_AIOCOMPLTR_THREADS) {
error = 1;// 设置错误码
return error;// 返回错误码,中断函数执行
}
// 调用安全内存函数memset_s 初始化 compltrArray,将内存设置为零
errno_t rc = memset_s(&compltrArray,
sizeof(AioCompltrThread_t) * MAX_AIOCOMPLTR_THREADS,
0,
sizeof(AioCompltrThread_t) * MAX_AIOCOMPLTR_THREADS);
//错误检查,用于确保 memset_s 函数是否执行成功。如果 rc(返回值)不为零,表示内存初始化出现了错误
securec_check(rc, "\0", "\0");
//初始化 compltrArray 数组
for (int i = 0; i < AioCompltrThreads; i++) {
try_times = 0;// 尝试次数归零
/* Assign a template to the thread descriptor */
compltrArray[i].compltrDescp = AIOCOMPLTR_TEMPLATE(i);// 为线程描述符分配对应的模板
/* Create the i/o queue and fill in the context */
do {
// 使用 io_setup 函数创建异步 I/O 队列,参数为当前线程的最大事件数和队列上下文。
error = io_setup(compltrArray[i].compltrDescp->maxevents, &compltrArray[i].context);
// 如果成功创建队列或者遇到的错误不是 EAGAIN(表示资源暂时不可用),则跳出循环。
if (error == 0 || error != -EAGAIN) {
break;
}
try_times++; // 尝试次数增加
// 打印日志,记录当前尝试的线程ID、尝试次数和错误码。
ereport(LOG, (errmsg("AIO Startup, Completer thread id =%d try times=%d, error=%d", i, try_times, error)));
pg_usleep(100000L);// 延迟 100,000 微秒(0.1 秒),然后继续下一次尝试。
} while (try_times < 5);// 循环继续,直到成功创建队列或者尝试次数达到 5 次。
if (error != 0) {
goto AioCompltrStartError;// 发生错误时跳转到错误处理标签
}
/* 为线程分配事件数组的内存 用于存储事件数组
compltrArray[i].compltrDescp->max_nr 表示事件数组的最大大小,sizeof(struct io_event) 表示单个事件的大小。*/
compltrArray[i].eventsp = (io_event*)malloc(compltrArray[i].compltrDescp->max_nr * sizeof(struct io_event));
// 检查内存分配是否成功,失败则进入if语句内部
if (compltrArray[i].eventsp == (struct io_event*)NULL) {
/* malloc failed for some reason... */
error = 2;// 设置错误码为 2,表示内存分配失败
//打印日志,记录内存分配失败的详细信息,包括最大事件数和错误码。
ereport(LOG,
(errmsg("AIO Startup malloc io_event failed: max_nr(%d), %d",
compltrArray[i].compltrDescp->max_nr,
error)));
// 跳转到错误处理标签 AioCompltrStartError,执行错误处理操作
goto AioCompltrStartError;
}
//使用 Compltrfork_exec(i) 函数启动一个异步 I/O 完成线程,并将线程索引 i 作为参数传递给函数。函数返回线程的 ID。
compltrArray[i].tid = Compltrfork_exec(i);
// 检查线程是否成功启动。如果启动失败,会进入条件内部。
if (compltrArray[i].tid == (ThreadId)-1) {
/* starting a thread failed */
error = 3;// 设置错误码为 3,表示启动线程失败
// 打印日志,记录启动线程失败的详细信息,包括错误码。
ereport(LOG, (errmsg("Start AIO Completer thread failed: %d", error)));
goto AioCompltrStartError; // 发生错误时跳转到错误处理标签
}
};
AioCompltrReady = true;// AIO Completers 准备就绪
return 0;
//错误处理标签 AioCompltrStartError 标识错误处理的起始位置
AioCompltrStartError:
/*
* If anything went wrong, then stop any threads started
* and deallocate the resources.
*/
AioCompltrStop(SIGTERM);// 停止已启动的异步 I/O 完成线程并释放资源
ereport(LOG, (errmsg("AIO Startup Failed,error=%d", error)));// 记录启动失败的错误日志
return error;// 返回错误码
}