暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL源码启动过程

DBA懒人笔记 2021-04-12
2586

描述:

        刚刚搭建好调试环境,就想把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()无限循环等待来自客户端的连接请求。

文章转载自DBA懒人笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论