0

用信号和叉子做作业,信号有问题。

我创建了函数:

void trata_sinal_int() {
    char op[2];

    printf("\nTerminate? (y/n)\n");

    scanf("%s", op);

    if (op[0] == 'y') {
        printf("Bye Bye\n");
        exit(0);
    }

}

主要我有:

signal(SIGINT, trata_sinal_int);

当我运行它并按下CTRL ^C该函数时void trata_sinal_int(),我得到了消息。

如果我按y程序按预期结束,但如果我按n程序仍然结束。不是他按之前的样子CTRL ^C

这应该发生吗?

4

2 回答 2

5

这取决于您遵守的标准,但标准 C 不允许您做更多的事情,只能修改类型变量或从信号处理程序volatile sig_atomic_t调用_Exit(或abort()或)。signal()POSIX 宽松得多。您的信号处理程序中的代码充满了用户交互,甚至超出了 POSIX 允许的范围。通常,您希望您的信号处理函数小而苗条。

请注意,信号处理函数应为:

void trata_sinal_int(int signum)
{

这允许您在没有类型不匹配的强制转换或编译器警告的情况下进行编译。该signal()函数可以在调用时将信号处理程序重置为默认行为;经典地,有必要在信号处理程序中恢复信号处理程序:

    signal(signum, trata_sinal_int);

到目前为止,这一切都是非常通用和微不足道的。

当您键入 时Control-C,系统确实会返回到最初接收信号时的大致位置。但是,接下来会发生什么取决于它的位置(在处理程序内部必须非常小心的原因之一)。例如,如果它正在处理 内部的空闲列表指针malloc(),它会返回那里,但如果你malloc()在处理程序内部重新调用,那么所有的地狱都可能会崩溃。如果你在一个系统调用中,那么你的调用可能会被中断(返回一个错误指示和errno == EINTR),或者它可能会从中断的地方继续。否则,它应该回到计算运行的地方。


这是您的代码(固定版本)内置到测试台中。该pause()函数在返回之前等待一个信号。

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

static void trata_sinal_int(int signum)
{
    char op[2];

    signal(signum, trata_sinal_int);

    printf("\nTerminate? (y/n)\n");
    scanf("%s", op);

    if (op[0] == 'y')
    {
        printf("Bye Bye\n");
        exit(0);
    }
}

int main(void)
{
    signal(SIGINT, trata_sinal_int);
    for (int i = 0; i < 3; i++)
    {
        printf("Pausing\n");
        pause();
        printf("Continuing\n");
    }
    printf("Exiting\n");
    return(0);
}

我真的应该指出,这scanf()根本不是很安全。大小为 2 的缓冲区是缓冲区溢出的公开邀请。我也不是错误检查系统调用。

我在 BSD 衍生产品 Mac OS X 10.7.5 上进行了测试。很有signal()可能在这个平台上不需要重置,因为 BSD 很久以前就引入了“可靠信号”(POSIX 之前)。


ISO/IEC 9899:2011 §7.14.1.1signal功能

¶5 如果信号的出现不是调用abortorraise函数的结果,则如果信号处理程序引用具有静态或线程存储持续时间的任何对象,该对象不是无锁原子对象,则行为未定义,而不是通过赋值到声明为 的对象volatile sig_atomic_t,或者信号处理程序调用标准库中的任何函数,而不是abort函数、_Exit函数、 quick_exit函数或signal函数,其第一个参数等于与导致调用的信号对应的信号编号处理程序。此外,如果对signal函数的这种调用导致SIG_ERR返回,则 errno 的值是不确定的。252)

252)如果任何信号由异步信号处理程序生成,则行为未定义。

引用quick_exit()是 C2011 中的新内容;它们在 C1999 中不存在。

POSIX 2008

Signal Concepts部分非常详细地介绍了 POSIX 下的信号处理程序中允许和不允许的内容。

于 2012-10-13T15:35:47.267 回答
2

首先,您的信号处理程序并不完全是异步信号安全的。实际上,在您的情况下,这可能不是问题,因为我假设 main() 在等待信号时基本上什么都不做。但无论如何它肯定是不正确的。

至于程序退出的原因,由于无效使用 FILE* 函数(如 printf、sscanf 等)而不计算信号处理程序中的 segfault:s,当收到信号时,您正在执行的任何系统调用(或者,大多数)都会被打断EAGAIN

如果您使用sleep()main 之类的东西来等待信号发生,它将被中断。您应该手动重新启动它。

为避免这种情况,您可能希望使用更便携sigaction的界面而不是signal. 如果没有别的,这允许您表明您希望重新启动系统调用。

信号处理程序中不允许that FILE *使用函数(以及大多数其他使用全局状态的函数,例如malloc和)的原因是,当信号到达时,您可能正处于同一状态的另一个操作的中间。free

这可能会导致段错误或其他未定义的操作。

实现这一点的正常“技巧”是拥有一个自管道:信号处理程序将向管道写入一个字节,您的主循环将看到这一点(通常通过等待poll或类似的东西)然后对其采取行动。

如果您绝对想在信号处理程序中进行用户交互,则必须使用write()and read(),而不是FILE*函数。

于 2012-10-13T15:34:46.393 回答