• 信号等待:在 sigwait 等待这些信号时,系统可以等待定时器信号(SIGALRM),子进程结束信号(SIGCHLD)以及用户中断信号(SIGINT)来适当响应并决定是否继续执行查询或终止操作。
  • Unblock这三个信号。

    1. sigalrm_sigchld
    sigemptyset(&sigalrm_sigchld);
    sigaddset(&sigalrm_sigchld, SIGCHLD);
    sigaddset(&sigalrm_sigchld, SIGALRM);
    
    • 目的:该信号集仅包含 SIGALRMSIGCHLD 信号。
    • 作用:
      • SIGALRM:定时器信号,定时查询执行。
      • SIGCHLD:子进程结束信号,通常与分页器进程相关。
    • 使用场景:
      • 阻塞信号:在定时器启动并在处理分页器时,使用 sigprocmask(SIG_BLOCK, &sigalrm_sigchld, NULL) 阻塞 SIGALRMSIGCHLD 信号。这避免了定时器到时和子进程退出时立即触发不必要的信号处理。
      • 信号等待:在 sigwait 中等待 SIGALRMSIGCHLD 信号,确保定时器触发时可以执行查询,分页器退出时可以结束循环。

    1. sigint
    sigemptyset(&sigint);
    sigaddset(&sigint, SIGINT);
    
    • 目的:该信号集仅包含 SIGINT(用户中断信号)。
    • 作用:
      • SIGINT:用户发送的中断信号,通常是在终端按下 Ctrl+C 时触发。
    • 使用场景:
      • 阻塞信号:在执行查询时,使用 sigprocmask(SIG_BLOCK, &sigint, NULL) 阻塞 SIGINT,避免在查询执行过程中用户中断查询。
      • 信号等待:在 sigwait 中等待 SIGINT 信号,用于捕获用户的中断请求并结束查询循环。
      • 恢复信号处理:在处理完信号后,解除阻塞 SIGINT,使得查询过程中的用户中断请求能被正确处理。

    为什么使用多个信号集?

    1. 精确控制信号的阻塞与等待:

      • 使用不同的信号集可以精确控制哪些信号在某些阶段需要被阻塞,哪些信号需要在信号处理器中等待。例如,在定时器设置之前需要阻塞 SIGALRMSIGCHLD,确保这些信号不会干扰程序的初始化过程。而 SIGINT 只在查询执行后需要被阻塞,避免用户在执行查询时中断查询。
    2. 避免信号竞争条件:

      • 在有多个信号可能同时到达的情况下,通过分开信号集,确保不同的信号按预期顺序被处理。例如,SIGALRM 可以用于触发查询,而 SIGCHLD 用于检测分页器是否结束。这些信号的处理需要分开,以免不同信号相互干扰。
    3. 信号等待机制:

      • 使用 sigwait 等待多个信号时,通过精细的信号集管理,可以确保程序能正确响应定时器超时、分页器进程结束以及用户中断等事件,而不会错过或错误响应某些信号。

    小结一下,三个信号集的使用主要是为了:

    • 精确控制信号的阻塞与解阻:保证在某些阶段屏蔽特定信号,以防止信号干扰正常的查询执行流程。
    • 确保信号处理的正确性:通过分开处理 SIGALRMSIGCHLDSIGINT,避免它们之间的冲突或竞态条件。
    • 按预期顺序等待和处理信号:确保查询周期、分页器和用户中断信号的处理顺序不发生混乱,保证程序按预期行为进行。

    通过这种方式,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;
    }
    
    01-21 10:14