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 &&
相关文章推荐
- 使用Power Designer工具进行数据库设计
- MySQL中的外键是什么、有什么作用
- Disque:Redis之父新开源的分布式内存作业队列
- oracle 插入时间类型无效 timestamp
- [转载] 使用Redis的Java客户端Jedis
- 将Excel数据表到数据库表
- SQL*loader实验
- SQL*loader实验
- MySQL索引类型一览 让MySQL高效运行起来
- 数据库设计
- communications link failure MySQL ibatis
- Ubuntu mysql 的Access denied for user 'root'@'localhost' (using password: YES错误解决方法
- 查看连接MYSQL数据库的IP信息
- 数据库分组函数
- 简学Oracle
- 【数据库】分组函数
- 【MySQL】(4)操作数据表中的记录
- oracle之关连接查询
- 一些mysql语句
- 批量插入数据 C# SqlBulkCopy使用