Postgres数据库的进程结构---菜鸟的PG分析之路

来源:互联网 发布:edg网络黄金 编辑:程序博客网 时间:2024/05/22 09:07
if (argc > 1 && strcmp(argv[1], "--boot") == 0)AuxiliaryProcessMain(argc, argv);/* does not return */else if (argc > 1 && strcmp(argv[1], "--describe-config") == 0)GucInfoMain();/* does not return */else if (argc > 1 && strcmp(argv[1], "--single") == 0)PostgresMain(argc, argv, NULL,/* no dbname */ strdup(get_user_name_or_exit(progname)));/* does not return */elsePostmasterMain(argc, argv);/* does not return */

  Postgres数据库所有服务进程的入口均位于src/backend/main目录下的main函数,在启动PG数据库的命令行参数中,只要没有指定--single选项,程序都会跳转到PostgresMain所在的分支进行启动;只有当指定了以single模式启动,才会执行PostgresMain分支。程序在进入PostmasterMain分支执行前,会首先对数据库的内存上下文管理进行初始化,具体包括为顶层的TopMemoryContext分配内存空间、将全局变量CurrentMemoryContext指向刚分配的TopMemoryContext、以及初始化ErrorContext。在完成这一些列的内存上下文的初始化工作后,程序进入到PostmasterMain分支执行。

在PostmasterMain函数的执行过程中,会首先设置全局变量IsPostmasterEnvironment = true, 表明系统处在多用户模式下;接着会为Postmaster进程分配执行的内存上下文PostmasterContext,并切换到PostmasterContext上下文中;接着注册一系列的信号处理函数:并接下来设置数据库的工作目录以及从配置文件中载入GUC全局变量等;在设置好监听的网络端口后,会首先启动系统日志进程syslogger 子进程,具体的启动也是通过fork 函数载入 Postgres文件并设置对应的命令行参数为"--forklog"完成的。

pqsignal(SIGHUP, SIGHUP_handler);/* reread config file and have * children do same */pqsignal(SIGINT, pmdie);/* send SIGTERM and shut down */pqsignal(SIGQUIT, pmdie);/* send SIGQUIT and die */pqsignal(SIGTERM, pmdie);/* wait for children and shut down */pqsignal(SIGALRM, SIG_IGN); /* ignored */pqsignal(SIGPIPE, SIG_IGN); /* ignored */pqsignal(SIGUSR1, sigusr1_handler); /* message from child process */pqsignal(SIGUSR2, dummy_handler);/* unused, reserve for children */pqsignal(SIGCHLD, reaper);/* handle child termination */
在启动完成系统日志进程syslogger 进程后,会调用StartDataBase启动其它的辅助子进程,StartDataBase实质上是一个宏定义函数,实质是调用StartChildProcess函数并给其传递形参StartupProcess。
/* * We're ready to rock and roll... */StartupPID = StartupDataBase();Assert(StartupPID != 0);pmState = PM_STARTUP;/* Some workers may be scheduled to start now */maybe_start_bgworker();status = ServerLoop();/* * ServerLoop probably shouldn't ever return, but if it does, close down. */ExitPostmaster(status != STATUS_OK);abort();/* not reached */
#define StartupDataBase() StartChildProcess(StartupProcess)

typedef enum{NotAnAuxProcess = -1,CheckerProcess = 0,BootstrapProcess,StartupProcess,BgWriterProcess,CheckpointerProcess,WalWriterProcess,WalReceiverProcess,NUM_AUXPROCTYPES/* Must be last! */} AuxProcType;

StartupProcess是AuxProcType枚举类型的一种,剩余其它枚举类型包括启动后台写进程BgWriter的BgWriterProcess、启动WalWriter预写式日志进程的WalWriterProcess等。在通过StartupDataBase() 实际fork其它辅助子进程的过程中,也是通过设置命令行参数为postgres --forkboot StartupProcess,并载入Postgres文件进行的。


#define StartupDataBase()StartChildProcess(StartupProcess)#define StartBackgroundWriter() StartChildProcess(BgWriterProcess)#define StartCheckpointer()StartChildProcess(CheckpointerProcess)#define StartWalWriter()StartChildProcess(WalWriterProcess)#define StartWalReceiver()StartChildProcess(WalReceiverProcess)
StartChildProcess也是通过载入Postgres可执行文件并设置对应的命令行参数来工作的,
snprintf(typebuf, sizeof(typebuf), "-x%d", type);av[ac++] = typebuf;


在启动完全部的子进程后,PostmasterMain函数会进入到ServerLoop函数中,循环检测其它辅助进程的执行状态,但发现某个子进程意外退出时,重新启动对应的字进程。

/* If we have lost the log collector, try to start a new one */if (SysLoggerPID == 0 && Logging_collector)SysLoggerPID = SysLogger_Start();/* * If no background writer process is running, and we are not in a * state that prevents it, start one.  It doesn't matter if this * fails, we'll just try again later.  Likewise for the checkpointer. */if (pmState == PM_RUN || pmState == PM_RECOVERY ||pmState == PM_HOT_STANDBY){if (CheckpointerPID == 0)CheckpointerPID = StartCheckpointer();if (BgWriterPID == 0)BgWriterPID = StartBackgroundWriter();}

回到Postgres程序的入口main函数,前面分析过当需要启动系统日志子进程是会设置postgres的命令行参数为"--forklog",  启动其它辅助子进程会设置命令行参数为“-forkboot”;所以在启动辅助子进程时,Postgres会进入到SubPostmasterMain(argc, argv);分支执行下去。而SubPostmasterMain函数的相关注释及函数体如下。

#ifdef EXEC_BACKENDif (argc > 1 && strncmp(argv[1], "--fork", 6) == 0)SubPostmasterMain(argc, argv);/* does not return */#endif

/* * SubPostmasterMain -- Get the fork/exec'd process into a state equivalent *<span style="white-space:pre"></span>to what it would be if we'd simply forked on Unix, and then *<span style="white-space:pre"></span>dispatch to the appropriate place. * * The first two command line arguments are expected to be "--forkFOO" * (where FOO indicates which postmaster child we are to become), and * the name of a variables file that we can read to load data that would * have been inherited by fork() on Unix.  Remaining arguments go to the * subprocess FooMain() routine. */voidSubPostmasterMain(int argc, char *argv[]){}

SubPostmasterMain函数会做一些共享内存的相关设置工作,然后根据命令行参数分别进入相应代码的执行分支。"--forkbackend" 分支用于启动用户服务子进程;其它分支"--forkboot" 、 "--forkavlauncher""--forkavworker"、"--forkbgworker"、"--forkcol"、"--forklog"也是类似的。 通过代码发现"--forkboot" 分支只是进行了事务执行环境和日志管理系统的初始化,并没有启动其他的子进程,因而辅助子进程的启动应该是在postmasrer的ServerLoop中进行的。ServerLoop会循环检测辅助子进程的状态,当发现子进程没有运行时启动相应的子进程。

/* If we have lost the log collector, try to start a new one */if (SysLoggerPID == 0 && Logging_collector)SysLoggerPID = SysLogger_Start();/* * If no background writer process is running, and we are not in a * state that prevents it, start one.  It doesn't matter if this * fails, we'll just try again later.  Likewise for the checkpointer. */if (pmState == PM_RUN || pmState == PM_RECOVERY ||pmState == PM_HOT_STANDBY){if (CheckpointerPID == 0)CheckpointerPID = StartCheckpointer();if (BgWriterPID == 0)BgWriterPID = StartBackgroundWriter();}/* * Likewise, if we have lost the walwriter process, try to start a new * one.  But this is needed only in normal operation (else we cannot * be writing any new WAL). */if (WalWriterPID == 0 && pmState == PM_RUN)WalWriterPID = StartWalWriter();/* * If we have lost the autovacuum launcher, try to start a new one. We * don't want autovacuum to run in binary upgrade mode because * autovacuum might update relfrozenxid for empty tables before the * physical files are put in place. */if (!IsBinaryUpgrade && AutoVacPID == 0 &&





0 0
原创粉丝点击