PostgreSQL 支持安装扩展(extension),有些扩展的安装,需要在配置文件 postgresql.conf 中提前配置,并在 PG 主程序启动时提前加载,比如 timescaledb,如下:
shared_preload_libraries = 'timescaledb'
这类扩展实际上就是一个 .so 动态库,PG 主程序在启动时,提前将这些动态库加载到进程中。
PG 主进程 postmaster 在启动时,调用 internal_load_library() 函数,进行动态库的加载,函数调用关系如下:
main()
->PostmasterMain()
->process_shared_preload_libraries()
->load_libraries()
->load_file()
->internal_load_library()
可以看出,加载一个扩展(extenstion)的核心代码位于函数 internal_load_library() 里面,这个函数比较短,逻辑也很简单,如下:
- 通过循环遍历全局变量 file_list,找出当前要安装的扩展是否已安装,如果已安装,直接返回该扩展的 handle。如果没有安装,进行下一步。
- 检查要安装的扩展,其文件是否可以访问,不能访问则报错退出。检查是否为已安装扩展的软链接或者硬链接,如果是,则直接返回该已安装扩展的 handle。
- 调用 dlopen 加载动态库文件,dlopen 返回的就是一个动态库的 handle。
- 调用 dlsym 获取动态库里的函数 Pg_magic_func,执行该函数,获取 Pg_magic_struct 结构体数据,并与标准的 magic_data 进行对比,进行动态库兼容性测试。
- 调用 dlsym 获取动态库里的函数 _PG_init,如果有该函数,则执行该函数,进行动态库的初始化。
- 将动态库 handle 对应的结构体加入到全局 file_list 链表中。
file_list 结构如下,file_tail 指向 file_list 链表的最后一个节点。
# src/backend/utils/fmgr/dfmgr.c
typedef struct df_files
{
struct df_files *next; /* List link */
dev_t device; /* Device file is on */
#ifndef WIN32 /* ensures we never again depend on this under
* win32 */
ino_t inode; /* Inode number of file */
#endif
void *handle; /* a handle for pg_dl* functions */
char filename[FLEXIBLE_ARRAY_MEMBER]; /* Full pathname of file */
} DynamicFileList;
static DynamicFileList *file_list = NULL;
static DynamicFileList *file_tail = NULL;
注:本文涉及到源码版本为 PG 14