您的位置:首页 > 数据库

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

2015-07-23 22:01 501 查看
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 */
else
PostmasterMain(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_BACKEND
if (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.
 */
void
SubPostmasterMain(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 &&
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: