PostgreSQL shared_preload_libraries 动态库的加载逻辑分析

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() 里面,这个函数比较短,逻辑也很简单,如下:

  1. 通过循环遍历全局变量 file_list,找出当前要安装的扩展是否已安装,如果已安装,直接返回该扩展的 handle。如果没有安装,进行下一步。
  2. 检查要安装的扩展,其文件是否可以访问,不能访问则报错退出。检查是否为已安装扩展的软链接或者硬链接,如果是,则直接返回该已安装扩展的 handle。
  3. 调用 dlopen 加载动态库文件,dlopen 返回的就是一个动态库的 handle。
  4. 调用 dlsym 获取动态库里的函数 Pg_magic_func,执行该函数,获取 Pg_magic_struct 结构体数据,并与标准的 magic_data 进行对比,进行动态库兼容性测试。
  5. 调用 dlsym 获取动态库里的函数 _PG_init,如果有该函数,则执行该函数,进行动态库的初始化。
  6. 将动态库 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

文章评论

0条评论