sigwait
等待这些信号时,系统可以等待定时器信号(SIGALRM
),子进程结束信号(SIGCHLD
)以及用户中断信号(SIGINT
)来适当响应并决定是否继续执行查询或终止操作。sigalrm_sigchld
sigemptyset(&sigalrm_sigchld);
sigaddset(&sigalrm_sigchld, SIGCHLD);
sigaddset(&sigalrm_sigchld, SIGALRM);
- 目的:该信号集仅包含
SIGALRM
和SIGCHLD
信号。 - 作用:
SIGALRM
:定时器信号,定时查询执行。SIGCHLD
:子进程结束信号,通常与分页器进程相关。
- 使用场景:
- 阻塞信号:在定时器启动并在处理分页器时,使用
sigprocmask(SIG_BLOCK, &sigalrm_sigchld, NULL)
阻塞SIGALRM
和SIGCHLD
信号。这避免了定时器到时和子进程退出时立即触发不必要的信号处理。 - 信号等待:在
sigwait
中等待SIGALRM
和SIGCHLD
信号,确保定时器触发时可以执行查询,分页器退出时可以结束循环。
- 阻塞信号:在定时器启动并在处理分页器时,使用
sigint
sigemptyset(&sigint);
sigaddset(&sigint, SIGINT);
- 目的:该信号集仅包含
SIGINT
(用户中断信号)。 - 作用:
SIGINT
:用户发送的中断信号,通常是在终端按下Ctrl+C
时触发。
- 使用场景:
- 阻塞信号:在执行查询时,使用
sigprocmask(SIG_BLOCK, &sigint, NULL)
阻塞SIGINT
,避免在查询执行过程中用户中断查询。 - 信号等待:在
sigwait
中等待SIGINT
信号,用于捕获用户的中断请求并结束查询循环。 - 恢复信号处理:在处理完信号后,解除阻塞
SIGINT
,使得查询过程中的用户中断请求能被正确处理。
- 阻塞信号:在执行查询时,使用
为什么使用多个信号集?
-
精确控制信号的阻塞与等待:
- 使用不同的信号集可以精确控制哪些信号在某些阶段需要被阻塞,哪些信号需要在信号处理器中等待。例如,在定时器设置之前需要阻塞
SIGALRM
和SIGCHLD
,确保这些信号不会干扰程序的初始化过程。而SIGINT
只在查询执行后需要被阻塞,避免用户在执行查询时中断查询。
- 使用不同的信号集可以精确控制哪些信号在某些阶段需要被阻塞,哪些信号需要在信号处理器中等待。例如,在定时器设置之前需要阻塞
-
避免信号竞争条件:
- 在有多个信号可能同时到达的情况下,通过分开信号集,确保不同的信号按预期顺序被处理。例如,
SIGALRM
可以用于触发查询,而SIGCHLD
用于检测分页器是否结束。这些信号的处理需要分开,以免不同信号相互干扰。
- 在有多个信号可能同时到达的情况下,通过分开信号集,确保不同的信号按预期顺序被处理。例如,
-
信号等待机制:
- 使用
sigwait
等待多个信号时,通过精细的信号集管理,可以确保程序能正确响应定时器超时、分页器进程结束以及用户中断等事件,而不会错过或错误响应某些信号。
- 使用
小结一下,三个信号集的使用主要是为了:
- 精确控制信号的阻塞与解阻:保证在某些阶段屏蔽特定信号,以防止信号干扰正常的查询执行流程。
- 确保信号处理的正确性:通过分开处理
SIGALRM
、SIGCHLD
和SIGINT
,避免它们之间的冲突或竞态条件。 - 按预期顺序等待和处理信号:确保查询周期、分页器和用户中断信号的处理顺序不发生混乱,保证程序按预期行为进行。
通过这种方式,do_watch
函数能够稳健地进行定期查询并适应用户中断或分页器退出等事件。
上面的信号设置及处理比较复杂,而真正的query执行则内容篇幅讲解较少。下面是PSQLexecWatch
的逻辑处理,有兴趣的小伙伴可以自行前去理解 本文不再赘述:
// src/bin/psql/common.c
/*
* PSQLexecWatch
*
* This function is used for \watch command to send the query to
* the server and print out the result.
* 该函数用于\watch命令将查询发送到服务器并打印出结果。
*
* Returns 1 if the query executed successfully, 0 if it cannot be repeated,
* e.g., because of the interrupt, -1 on error.
* 如果查询成功执行则返回 1,如果无法重复(例如由于中断)则返回 0,如果出现错误则返回 -1。
*/
int
PSQLexecWatch(const char *query, const printQueryOpt *opt, FILE *printQueryFout, int min_rows)
{
bool timing = pset.timing;
double elapsed_msec = 0;
int res;
if (!pset.db)
{
pg_log_error("You are currently not connected to a database.");
return 0;
}
SetCancelConn(pset.db);
res = ExecQueryAndProcessResults(query, &elapsed_msec, NULL, true, min_rows, opt, printQueryFout);
ResetCancelConn();
/* Possible microtiming output */
if (timing)
PrintTiming(elapsed_msec);
return res;
}