信号
postgresql是多进程模型,其选择使用信号来触发一系列实例级别、进程级别的动作。
信号注册,postmaster进程可以通过pqsignal_pm函数来注册信号处理函数,其他进程通过pqsignal函数来注册信号处理函数:
typedef void (*pqsigfunc) (int signo);
pqsigfunc
pqsignal_pm(int signo, pqsigfunc func)
{
struct sigaction act, oact;
act.sa_handler = func;
if (func == SIG_IGN || func == SIG_DFL)
{
/* in these cases, act the same as pqsignal() */
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
}
else
{
act.sa_mask = BlockSig;
act.sa_flags = 0;
}
#ifdef SA_NOCLDSTOP
if (signo == SIGCHLD)
act.sa_flags |= SA_NOCLDSTOP;
#endif
if (sigaction(signo, &act, &oact) < 0)
return SIG_ERR;
return oact.sa_handler;
}
pqsigfunc
pqsignal(int signo, pqsigfunc func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
#ifdef SA_NOCLDSTOP
if (signo == SIGCHLD)
act.sa_flags |= SA_NOCLDSTOP;
#endif
if (sigaction(signo, &act, &oact) < 0)
return SIG_ERR;
return oact.sa_handler;
}
pqsignal_pm函数比pqsignal函数多了BlockSig的设置,该设置可以在陷入到sigaction函数的过程中,屏蔽掉BlockSig中注册的信号,用来防止在sigaction函数中响应信号导致重复陷入sigaction函数,最终函数栈被打满的情景。
BlockSig在函数pqinitmask内进行初始化,该函数同时初始化了StartupBlockSig,屏蔽了几乎全部的信号,除了:SIGTRAP、SIGABRT、SIGILL、SIGFPE、SIGSEGV、SIGBUS、SIGSYS、SIGCONT、SIGQUIT、SIGTERM、SIGALRM。
信号处理函数一般是轻量级的,比如:设置一个标记位、记录一种状态,标记位起作用的地方一般在每个进程的主循环逻辑函数中。
共享内存
postmaster进程启动过程中,函数reset_shared会申请并attach一段共享内存,共享内存的大小等于各个子模块、插件等注册的size的和,共享内存申请完后,也是按照顺序分配给各个子模块、插件等。
- postmaster静态申请的共享内存大小
...
size = 100000; //至少100000字节
size = add_size(size, PGSemaphoreShmemSize(numSemas));
//一些原子操作指令如果不支持的话,pg将会使用信号量来模拟,有名信号量需要放到共享内存中。另外,每个Proc结构体中包含一个信号量,用于lwlock锁释放后被其他进程唤醒后加锁。
size = add_size(size, SpinlockSemaSize());
//貌似前边计算的信号量中包含sinlock的