【问题标题】:C + UNIX, siglongjmp and sigsetjmpC + UNIX、siglongjmp 和 sigsetjmp
【发布时间】:2014-01-06 00:30:29
【问题描述】:

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

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

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 perfect\n",n);
    n++;
    }
}

void status(signum) 
int signum;
{

    alarm(0); /* shutoff alarm */

    if (signum == SIGINT) printf("Interrupt ");
    if (signum == SIGALRM) printf("Timer ");

    printf("processing %d\n",n);

    alarm(20);  /*restart alarm*/
}   

void query() {siglongjmp(jmpenv,1);}

我的问题是:

  1. 为什么会调用 void status();和无效查询();一开始主要是?
  2. 在 if 语句检查 begin == 0 并决定退出后,它遵循名为“sigprocmask(SIG_UNBLOCK, &mask, NULL);”的行为什么我退出后还要解锁?

【问题讨论】:

  • 你到底在做什么写 K&R 风格(非原型)的函数定义?除了使用原型符号之外,永远不要编写新代码! (至少,你需要解释为什么这可能是必要的;支持 POSIX 的平台没有不支持原型的 C 编译器,所以你必须非常有说服力地谈论。)
  • @JonathanLeffler 我没有编写函数。这是书中的代码,我正在努力理解它。

标签: c linux unix system


【解决方案1】:
  1. 这两行将函数 status()query() 声明为返回 void。他们没有具体说明他们采用什么论据。在现代 C 中,在另一个函数中声明函数是令人厌恶的(并且在没有完整原型的情况下声明它们也是令人厌恶的——但这似乎是另一天的讨论,因为它不是你的代码)。函数应该在其他函数之外声明,如果它们在另一个文件中定义或使用,它们应该在头文件中声明。如果它们在当前文件中定义并且未在任何其他文件中使用,则应将它们声明并定义为static 函数。

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

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

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


仔细阅读函数的手册页:

Chris Dodd 是正确的,当他 comments 认为第二个参数为 0 的 sigsetjmp() 不保存当前信号掩码时。请注意,mask 的值在一个局部变量中,该变量在调用setjmp() 之后被修改并且没有标记为volatile,因此当setjmp() 返回一个非零值时它的值是不确定的(参见注意事项在setjmp() 手册页中)。

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

【讨论】:

  • 如果我们穿越到 70 年代,你的回答是真的吗?
  • 到 70 年代 — 这个问题没有实际意义,因为在 K&R 第一版和第七版 UNIX™(我学习 C 和 Unix;系统是一个混合版本,主要是第 7 版,带有一些 System III 增强功能,IIRC)。由于 POSIX 标准化,这些功能出现在 80 年代末或 90 年代初——POSIX 的第一个版本于 1988 年发布。
  • sigaction()及相关功能在70年代也不存在; sigprocmask() 也没有。它们也是 POSIX 发明(合理化)。
  • @JonathanLeffler 非常感谢。我很感激。 :)
  • 我不确定您所说的“退出信号”是什么意思……但sigsetjmp() 函数可能会返回两次或更多次。第一次它会返回0;其他时候,当执行query() 中的siglongjmp() 调用时,它将返回一个非零值,这发生在向程序发送SIGQUIT 信号时。当sigsetjmp() 返回一个非零值时,代码将扫描begin 的新值,然后再次调用信号处理代码,最后从新指定的起点开始计算完美数。
猜你喜欢
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-12
  • 2011-12-10
  • 1970-01-01
相关资源
最近更新 更多