描述:
刚刚搭建好调试环境,就想把pg的启动过程梳理下,为后面启动数据库过程遇到的问题提供点帮助。
一、流程图



二、具体流程解释
backend/main/main.c:main()
判断是否是windows平台,如果是则安装crashdump句柄
<=include\port\win32_port.h:pgwin32_install_crashdump_handler()
*获取主进程的名称 <= port/path.c:get_progname() 如果名称为空,则会直接abort退出postmaster 并输出xxx:out of memory的错误
根据不同的平台使用其特殊启动方式
<= main.c:startup_hacks(progname)
保存原始的argc/argv值
<= backend\utils\misc\ps_status.c:save_ps_display_args()
*启动error和内存管理的子系统
<=backend\utils\mmgr\mcxt.c:MemoryContextInit()
后续postmaster主进程fork出的子进程都要调用此函数分配内存
设置区域信息LC_CTYPE和LC_COLLATE,如果已经是已经初始化的数据库,将会从$PGDATA/global/pg_control文件中的获取来覆盖设置
<= common\exec.c:set_pglocale_pgservice()
*设置LC_COLLATE,LC_CTYPE,LC_MONETARY,LC_NUMERIC,LC_TIME,LC_MESSAGES,删除LC_ALL参数
检测字符转换的bug,PostgreSQL 9.5及以上需要检测并更新OS packages
<= backend\utils\adt\pg_locale.c:check_strxfrm_bug()
如果启动命令中含有--help\-? --version\-v 将会给出信息后退出
*检测是否是root用户运行 check_root(progname)
检测参数中是否含有--fork,如果存在则调用函数开始fork子进程
<= backend\postmaster\postmaster.c:SubPostmasterMain()
如果是windows平台,初始化win32信号
<= backend\port\win32\signal.c:pgwin32_signal_initialize()
检测参数中是否含有--boot, 如果存在则调用
<= backend\bootstrap\bootstrap.c:AuxiliaryProcessMain() --兼容老版本代码
检测参数中是否含有--describe-config,如果存在则调用
<= backend\utils\misc\help_config.c:GucInfoMain() --输出配置信息
检测参数中是否含有--single,如果存在则调用
<=backend\postmaster\postmaster.c:PostgresMain(argc, argv,NULL,strdup(get_user_name_or_exit(progname))) --用于灾难恢复
*没有boot、describe-config、single三个参数,将进入postmaster主进程的main方法
<= backend\postmaster\postmaster.c:PostgresMain(argc, argv)
backend\postmaster\postmaster.c:PostgresMain()
*获取postmaster进程号和启动时间 getpid()
设置创建新文件的用户权限 umask(PG_MODE_MASK_OWNER)
获取随机id
<= srandom((unsigned int) (MyProcPid ^ MyStartTime))
*以Postmaster为名分配内存
<= include\utils\palloc.h:MemoryContextSwitchTo()
计算并检查作为安装一部分的文件的目录路径(根据postgres可执行文件自身的位置推断)
<= backend\postmaster\postmaster.c:getInstallationPaths()
初始化信号
<=backend\libpq\pqsignal.c:pqinitmask()
<=PG_SETMASK() pqsignal_no_restart() pqsignal()
初始化GUC选项
<= backend\utils\misc\guc.c :InitializeGUCOptions()
while 循环+switch 遍历获取命令行参数
错误参数选项将会导致启动进程退出,并输出错误日志和帮助
*从$PGDATA目录中读取配置文件,如果未读取到,将会退出启动进程
<= backend\utils\misc\guc.c :SelectConfigFiles()
*检测数据文件是否合理
backend\utils\init\miscinit.c :checkDataDir()
*检测控制文件pg_control是否存在于$PGDATA\global文件夹中
backend\postmaster\postmaster.c:checkControlFile()
切换工作区域到$PGDATA中
backend\utils\init\miscinit.c :ChangeToDataDir()
检查设置不正确的参数,例如:superuser_reserved_connections 、plus max_wal_senders must be less than max_connections
*在$PGDATA目录创建锁标识文件和postmaster.pid并写入PID
backend\utils\init\miscinit.c :CreateDataDirLockFile()
*读取pg_control文件,把其中的内容复制到共享内存
backend\access\transam\xlog.c: LocalProcessControlFile()
如果指定使用SSL,就调用secure_initialize()对ssl库进行处理
注册应用启动程序 ApplyLauncherRegister()
处理应该在postmaster启动时预加载的所有库
process_shared_preload_libraries()
根据配置文件初始化最大数量的后台进程 InitializeMaxBackends()
系统最大能接受的MaxBackends = MaxConnections + autovacuum_max_workers + 1 + max_worker_processes;如果设置的MaxBackends大于该值,则会报错too many backends configured
*for循环建立socket通道
*检查、创建监听(listenaddresses)处理完成后日志中会输出如下记录:
2020-05-04 11:08:31.251 HKT [23320] LOG: listening on IPv6 address "::1", port 5432
2020-05-04 11:08:31.257 HKT [23320] LOG: listening on IPv4 address "127.0.0.1", port 5432
*设置共享内存和信号量semaphores reset_shared(PostPortNumber)
估计设置可打开文件的数量 set_max_safe_fds()
设置堆叠深度检查的参考点 set_stack_base()
初始化管道(或窗口上的进程句柄),运行子进程唤醒主进程postmaster InitPostmasterDeathWatchHandle()
记录postmaster的参数选项 CreateOptsFile()
记录子进程中使用的非默认的参数
write_nondefault_variables(PGC_POSTMASTER)
根据请求是否创建额外的PID文件
删除旧的临时文件 RemovePgTempFiles()
删除promote、fallback_promote文件
RemovePromoteSignalFiles()
*根据配置启动日志记录收集子进程并输出PID
SysLoggerPID = SysLogger_Start()
*初始化stats收集子系统 pgstat_init()
*初始化autovacuum子系统 autovac_init()
*加载pg_hba.conf文件,获取失败将报错退出(could not load pg_hba.conf) load_hba()
*加载pg_ident.conf文件,获取失败无任何操作 load_ident()
*记录postmaster启动时间 PgStartTime = GetCurrentTimestamp()
*记录postmaster的状态到postmaster.pid 文件中,并允许pg_ctl知道当前状态
AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_STARTING)
*启动startup、checkpoint等后台进程
StartupPID = StartupDataBase()
*启动后台工作进程,为回滚和前滚数据做准备
maybe_start_bgworkers()
*postmaster主进程进入无限循环,监听来自客户端的连接请求,一旦有连接请求,则fork出一个进程与客户端进行交互。
ServerLoop()
如果ServerLoop() return 退出,postmaster主进程也将退出
ExitPostmaster(status != STATUS_OK)
总结:
启动分为两部分:
backend/main/main.c:main()检测系统环境和处理环境变量;
backend\postmaster\postmaster.c:PostgresMain() 启动postmaster主进程,获取记录主进程信息,分配内存,处理信号,打开监听和socket通道,启动各功能后台进程,进入ServerLoop()无限循环等待来自客户端的连接请求。




