C + UNIX, siglongjmp and sigsetjmp



我有一个来自(旧)教科书的程序,旨在说明在UNIX上使用POSIX信号。该程序运行一个计算循环,从固定点开始计算完美数字。

  • 时间报警信号用于定期打印状态。
  • 中断信号用于按需状态。
  • 退出信号用于重置测试间隔(或终止)。

void perfect(int);
sigjmp_buf jmpenv; /* environment saved by setjmp*/
int n; /* global variable indicating current test point */
int main() {
    int begin; /* starting point for next search*/
        /* interrupt routines*/
    void status();
    void query();
    sigset_t mask;
    struct sigaction action;

    if (sigsetjmp(jmpenv,0)) {
        printf("Enter search starting point (0 to terminate): ");
        scanf("%d",&begin);
        if (begin==0) exit(0);
        sigprocmask(SIG_UNBLOCK, &mask, NULL);
        }
    else begin=2;
    /* Status Routine will handle timer and INTR */
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGALRM);
    sigaddset(&mask, SIGQUIT);
    action.sa_flags=0;
    action.sa_mask=mask;
    action.sa_handler=status;
    sigaction(SIGINT,&action,NULL);
    sigaction(SIGALRM,&action,NULL);
    action.sa_handler=query;
    sigaction(SIGQUIT,&action,NULL);
    /* start alarm clock */
    alarm(20);
    perfect(begin);
}
void perfect(start) 
    int start;
{
    int i,sum;
    n=start;
while (1) {
    sum=1;
    for (i=2;i<n;i++)
        if (!(n%i)) sum+=i;
    if (sum==n) printf("%d is perfectn",n);
    n++;
    }
}
void status(signum) 
int signum;
{
    alarm(0); /* shutoff alarm */
    if (signum == SIGINT) printf("Interrupt ");
    if (signum == SIGALRM) printf("Timer ");
    printf("processing %dn",n);
    alarm(20);  /*restart alarm*/
}   
void query() {siglongjmp(jmpenv,1);}

我的问题是:

  1. 为什么它首先调用 void status(); 和 void query();
  2. 在 if 语句检查开始 == 0 并决定退出后,它遵循一个名为"sigprocmask(SIG_UNBLOCK, &mask, NULL);"的行为什么我必须在退出后取消阻止?
  1. 这两行将函数status()query()声明为返回 void。他们没有具体说明他们采取的论点。在现代 C 中,在另一个函数中声明函数是诅咒(在没有完整原型的情况下声明它们也是诅咒——但这似乎是另一天的讨论,因为它不是你的代码)。 函数应在其他函数外部声明,如果它们在另一个文件中定义或使用,则应在标头中声明。如果它们是在当前文件中定义的,而不是在任何其他文件中使用,则应将它们声明并定义为static函数。

  2. 变量begin以非正统方式初始化。它在第一次通过代码时设置为 2;从sigsetjmp()返回非零后,由用户输入设置。 它也有可能被setjmp()的东西破坏,因为它没有被标记为volatile。 规则是深奥的。

    但是,目的是如果begin为零,则程序退出。 否则,它将继续。 该sigprocmask()旨在解锁任何被屏蔽的信号。 我不清楚是否有必要。当您从信号处理程序返回时,即使您通过siglongjmp()退出,我认为阻塞的信号也应该被解锁。

请注意,在信号处理程序中调用printf()会调用未定义的行为。 很有可能会没事的,但不能保证。 有一个可以调用的函数列表,无论是在 POSIX 标准中还是在 SO 的其他问题中(我知道我之前已经给出了这个列表)。


仔细阅读手册页以了解这些功能:

  • sigsetjmp()
  • siglongjmp()
  • setjmp()
  • longjmp()

Chris Dodd 评论说,第二个参数为 0 的 sigsetjmp() 不会保存当前的信号掩码,这是正确的。请注意,mask 的值位于一个局部变量中,该变量在调用 setjmp() 后被修改,并且未标记为volatile,因此当 setjmp() 返回非零值时,其值是不确定的(请参阅setjmp()手册页中的警告)。

sigsetjmp()手册页的基本原理部分很有趣,并提到了4.2 BSD(1982年发布)中出现的类似功能,所以我关于它们在70年代不存在的评论仍然有效(K&R 1st版和7th edition UNIX™分别于1978年和1979年发布)。 sig*名称是AFAICT,由POSIX发明(BSD系统包括_setjmp()_longjmp())。

最新更新