一、 Postmaster与Postgres进程的关系
pg使用的是多进程架构,众多进程之中,最为重要的就是在前一节main函数中出现的Postmaster与Postgres进程。
- 守护进程Postmaster:负责PG的启动和关闭;监听和接收客户端连接请求,为其派生服务进程Postgres
- 服务进程Postgres:接收并执行客户端发送的命令,完成客户端的各种数据库操作并返回执行结果
在Linux中,Postmaster仅是Postgres进程的一个符号链接;在windows中,Postmaster是Postgres的一个拷贝。因此,pg中的所有核心功能,几乎都是Postgres进程完成的。
从main函数的调用中也可以看到,单用户模式下不需要Postmaster进程,只需要它的本体Postgres进程即可。
二、 守护进程Postmaster执行流程
1. 主要作用
Postmaster文件夹下包括下列文件:
由此很明显能看出Postmaster进程的主要用途 —— 派生子进程。这既包括前面提到的Postgres进程,也包括大量的后台进程。
它还负责中断、数据库启停等操作的处理,Postmaster进程本身并不直接处理这些事件,而是指派子进程在适当的事件处理。同时,它要在数据库崩溃时重启系统。
Postmaster及其子进程通过共享内存和信号库(在启动时初始化)进行通信,这种多进程设计使其稳定性更强,即使某个后台进程崩溃也不会影响系统中其他进程的工作,Postmaster只需要重置共享内存即可从单个后台进程的崩溃中恢复(只是原理上是这样,实际上通过os命令kill pg的用户进程,都有可能整个pg实例崩溃)。
2. 代码核心流程
这部分源码位于postmaster.c文件,由于内容太长,我们先只看初始化内存及信号处理设置的部分。
三、 PostmasterMain(1) —— 内存初始化与切换
在main函数中,调用MemoryContextInit函数首先创建了顶层上下文TopMemoryContext和错误恢复处理的上下文ErrorContext。
/** MemoryContextInit* Start up the memory-context subsystem.** This must be called before creating contexts or allocating memory in* contexts. TopMemoryContext and ErrorContext are initialized here;* other contexts must be created afterwards.*/
void
MemoryContextInit(void)
{AssertState(TopMemoryContext == NULL);/** First, initialize TopMemoryContext, which is the parent of all others.*/TopMemoryContext = AllocSetContextCreate((MemoryContext) NULL,"TopMemoryContext",ALLOCSET_DEFAULT_SIZES);/** Not having any other place to point CurrentMemoryContext, make it point to TopMemoryContext. Caller should change this soon!*/CurrentMemoryContext = TopMemoryContext;/** Initialize ErrorContext*/ErrorContext = AllocSetContextCreate(TopMemoryContext,"ErrorContext",8 * 1024,8 * 1024,8 * 1024);MemoryContextAllowInCriticalSection(ErrorContext, true);
}
PostmasterMain中,调用AllocSetContextCreate()函数创建用于Postmaster工作的内存上下文PostmasterContext,并调用函数MemoryContextSwitchTo()将当前上下文切换到此处。这样若是在Postmaster模块若是出现内存相关的问题,不会影响到其余模块。
/** Postmaster main entry point*/
void
PostmasterMain(int argc, char *argv[])
{int opt;int status;char *userDoption = NULL;bool listen_addr_saved = false;int i;char *output_config_variable = NULL;InitProcessGlobals();PostmasterPid = MyProcPid;IsPostmasterEnvironment = true;/** 在checkDataDir()函数执行前不创建任何文件,但先用umask命令用来设置限制新建文件权限的掩码*/umask(PG_MODE_MASK_OWNER);/** 初始化内存上下文。TopMemoryContext是所有内存上下文的根,直到pg退出才会释放,后面会为各类操作单独创建内存上下文,避免内存泄漏问题。*/PostmasterContext = AllocSetContextCreate(TopMemoryContext,"Postmaster",ALLOCSET_DEFAULT_SIZES);MemoryContextSwitchTo(PostmasterContext);
四、 PostmasterMain(2) —— 信号处理函数设置
1. 原理简介
信号是os响应某些状况而产生的事件,它可以明确由一个进程发给另一个进程,用这种方法传递信息或协调操作。进程可以自定义信号处理函数来处理信号,pg就是充分利用了这一点。
进程有权响应或屏蔽信号(SIGKILL和SIGSTOP不能屏蔽):
- BlockSig:要屏蔽的信号集
- UnBlockSig:不希望屏蔽的信号集
- AutoBlockSig:在进行用户连接认证时需要屏蔽的信号集
在设置响应信号的处理函数之前,要先通过PG_SETMASK函数把这些信号全部屏蔽,然后通过pgsignal函数为感兴趣的信号设置处理函数。
2. 常见信号处理函数及信号功能
① 处理函数SIGHUP_handler:处理SIGHUP信号。当配置文件发生改变时会产生SIGHUP信号,重读postgresql.conf文件,然后向子进程发送相同的信号,并重新装载pg_hba.conf和pg_ident.conf
② 处理函数pmdie:处理SIGTERM,SIGINT,SIGQUIT三种信号,这就是刚学关闭pg时的三种模式
- SIGTERM:smart shutdown(类似oracle shutdown normal)
- SIGINT:fast shutdown(类似oracle shutdown immediate)
- SIGQUIT:immediate shutdown(类似oracle shutdown abort)
③ 处理函数reaper:当系统中有子进程退出时,子进程会给postmaster进程发送一个SIGCHLD信号,主进程收到后调用reaper函数清理退出的子进程(不同的进程有各自处理方式)。
其他主要信号量如下:
3. 相关源码
/* Initialize paths to installation files,获取安装路径 */getInstallationPaths(argv[0]);/** Set up signal handlers for the postmaster process. 信号处理函数设置*//* 在设置响应信号的处理函数之前,要先通过PG_SETMASK函数把这些信号全部屏蔽,然后通过pgsignal函数为感兴趣的信号设置处理函数 */pqinitmask();PG_SETMASK(&BlockSig); pqsignal_pm(SIGHUP, SIGHUP_handler); /* reread config file and have children do same */pqsignal_pm(SIGINT, pmdie); /* send SIGTERM and shut down */pqsignal_pm(SIGQUIT, pmdie); /* send SIGQUIT and die */pqsignal_pm(SIGTERM, pmdie); /* wait for children and shut down */pqsignal_pm(SIGALRM, SIG_IGN); /* ignored */pqsignal_pm(SIGPIPE, SIG_IGN); /* ignored */pqsignal_pm(SIGUSR1, sigusr1_handler); /* message from child process */pqsignal_pm(SIGUSR2, dummy_handler); /* unused, reserve for children */pqsignal_pm(SIGCHLD, reaper); /* handle child termination */#ifdef SIGURGpqsignal_pm(SIGURG, SIG_IGN); /* ignored */
#endif#ifdef SIGTTINpqsignal_pm(SIGTTIN, SIG_IGN); /* ignored */
#endif
#ifdef SIGTTOUpqsignal_pm(SIGTTOU, SIG_IGN); /* ignored */
#endif/* ignore SIGXFSZ, so that ulimit violations work like disk full */
#ifdef SIGXFSZpqsignal_pm(SIGXFSZ, SIG_IGN); /* ignored */
#endif
后面是读取并设置GUC参数的部分,下节继续学习。
参考
《PostgreSQL数据库内核分析》第二章
http://www.javashuo.com/article/p-mmvwlzmf-ka.html