PostgreSQL的信号处理机制 原作者:张文升 创作时间:2015-12-01 18:18:18+08 |
doudou586 发布于2016-12-02 18:18:18 评论: 3 浏览: 9914 顶: 1064 踩: 1072 |
在linux中使用signal()和sigaction()函数安装信号。PostgreSQL定义了pgsignal函数实现信号的安装。
pqsigfunc pqsignal(int signo, pqsigfunc func) { #if !defined(HAVE_POSIX_SIGNALS) return signal(signo, func); #else 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; #endif }
如果当前的系统不遵循POSIX标准,例如一些早期的Unix系统,PG使用signal安装信号和信号处理程序,signal的第一个参数指定信号值,第二个参数指定针对前面信号值的处理程序,也可以忽略该信号(参数设为SIG_IGN);
对于遵循POSIX标准的操作系统,则使用sigaction安装信号。上面的代码定义了两个结构为sigaction的实例act和oact。act的sa_handler可以是用户自定义的处理函数外,还可以为SIG_DFL(缺省处理方式),也可以为SIG_IGN(忽略信号)。
sigaction函数的第一个signo参数指定信号值,第二个参数&act是指向结构sigaction的一个实例的指针;第三个参数&oact指向的对象不为空,用来保存原来对相应信号的处理。 pgsignal返回的是pgsigfunc,pgsigfunc只是一个简单的函数指针:
typedef void (*pqsigfunc) (int signo);
等价于:
void pgsigfunc(int signo);
PostgreSQL在PostmasterMain入口函数中注册信号处理函数,也可以称为安装信号。
/* PostgresMain.c line577:*/ pqsignal(SIGHUP, SIGHUP_handler); /* reread config file */ 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 */ pqsignal(SIGTTIN, SIG_IGN); /* ignored */ pqsignal(SIGTTOU, SIG_IGN); /* ignored */ /* ignore SIGXFSZ, so that ulimit violations work like disk full */ #ifdef SIGXFSZ pqsignal(SIGXFSZ, SIG_IGN); /* ignored */ #endif
我把这段代码总结了一张图表,方便了解PostgreSQL对系统信号的变更。
在PostmasterMain函数中更改这12个信号的默认处理方式为6种信号处理程序。
这个精简的PostmasterMain函数只描述了信号的处理部分。
int main(int argc,char *argv[]) { int status = 0; pqinitmask(); PG_SETMASK(&BlockSig); pqsignal(SIG_x, SIG_x_handler); status = ServerLoop(); return status; }
PostmasterMain通过以下步骤完成信号的处理:
pginitmask()
pginitmask()函数用来设置阻塞信号集(sig set)和不阻塞信号集,这里有三个信号集:UnBlockSig,BlockSig,StartupBlockSig。
void pqinitmask(void) { /* 清除UnBlockSig信号集中的所有信号*/ sigemptyset(&UnBlockSig); /* 初始化BlockSig指向的信号集,使其包括所有信号 */ sigfillset(&BlockSig); sigfillset(&StartupBlockSig); /* 增删特定的信号,这里我用SIG_x标识了特定的信号 */ sigdelset(&BlockSig, SIG_x); sigdelset(&StartupBlockSig, SIG_x); }
PG_SETMASK()函数把BlockSig信号集中的信号全部屏蔽。PG_SETMASK()为不同的OS采用了不同的处理方式,谁让PG是世界上支持操作系统最多的数据库系统呢。在类UNIX中,它只是sigsetmask(),在windows中它是pgsigsetmask()。
#ifndef WIN32 // for *nix #define PG_SETMASK(mask) sigsetmask(*((int*)(mask))) #else // for windows #define PG_SETMASK(mask) pgsigsetmask(*((int*)(mask))) int pgsigsetmask(int mask); #endif pgsignal()
安装表中描述的各种信号。
以上就是PG对信号的处理。这里主要讨论了在Linux系统的处理,对于windows系统,则是模拟一个类似lunix信号的消息队列出来,在src/backend/port/win32/signal.c的代码,是在win32中模拟linux信号的一些函数,关于windows相关的内容,我没有深入了解了。
附上一个最精简的PostmasterMain的实现,只安装了一个SIGHUP信号。
#include#include #include #ifdef HAVE_SIGPROCMASK sigset_t UnBlockSig, BlockSig, StartupBlockSig; #else int UnBlockSig, BlockSig, StartupBlockSig; #endif #ifndef WIN32 // for *nix #define PG_SETMASK(mask) sigsetmask(*((int*)(mask))) #else // for windows #define PG_SETMASK(mask) pgsigsetmask(*((int*)(mask))) int pgsigsetmask(int mask); #endif #define sigmask(sig) ( 1 << ((sig)-1) ) //void pqinitmask(void); void pqinitmask(void) { #ifdef HAVE_SIGPROCMASK #else UnBlockSig = 0; BlockSig = sigmask(SIGQUIT) | sigmask(SIGTERM) | sigmask(SIGALRM) | sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGUSR1) | sigmask(SIGUSR2) | sigmask(SIGCHLD) | sigmask(SIGWINCH) | sigmask(SIGFPE); StartupBlockSig = sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGUSR1) | sigmask(SIGUSR2) | sigmask(SIGCHLD) | sigmask(SIGWINCH) | sigmask(SIGFPE); #endif } static void SIGHUP_handler(SIGNAL_ARGS) { // do sth. } typedef void (*pqsigfunc) (int signo); pqsigfunc pqsignal(int signo, pqsigfunc func) { #if !defined(HAVE_POSIX_SIGNALS) return signal(signo, func); #else 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; #endif } static int ServerLoop() { int status = 0; for(;;){ // ..do sth. } return status; } int main(int argc,char *argv[]) { int status = 0; pqinitmask(); PG_SETMASK(&BlockSig); pqsignal(SIGHUP, SIGHUP_handler); printf("current pid is : %d\n",getpid()); status = ServerLoop(); return status; }
编译
zhangwensheng@x220:~$ gcc -g pgsignal.c -o pgsignal
运行
zhangwensheng@x220:~$ ./pgsignal current pid is : 16009
在当前终端按下终止符或者在其他终端向他发出不同信号进行观察和实验。
zhangwensheng@x220:~$ kill -SIGHUP 16009
《PostgreSQL数据库内核分析(彭智勇 彭煜玮)》