【问题标题】:Ctrl-C eaten by getchar()Ctrl-C 被 getchar() 吃掉
【发布时间】:2011-11-02 09:45:25
【问题描述】:

我一直在寻找解决我的问题的方法很长一段时间,这就是我求助于你的原因:

考虑这段代码:

static char done = 0;
static void sigHandler(void)
{
    done = 1;
}

int user_input()
{
    return (getchar() == 'q') ? 0 : 1;
}

int main(void)
{
    signal(SIGTERM, sigHandler);
    signal(SIGINT, sigHandler);
    while (user_input() != 0 && !done)
        usleep(1000);
    printf("exiting\n");
    return 0;
}

预期行为: 当用户输入 q 然后进入时程序退出。如果按下 CTRL+C,它会被 sigHandler 函数捕获,该函数将标志 'done' 设置为 1 并退出程序。

观察到的行为: CTRL+C 字符被 getchar() 调用吃掉,并且永远不会执行 sigHandler 函数。当CTRL+C再按回车键,调用sigHandler函数,程序退出。

有更多经验和知识的人可以帮助我吗?

感谢您的意见:)

【问题讨论】:

  • 即使那是我一开始不想做的事情,我已经通过不使用标志而直接退出处理程序来解决我的问题。

标签: c unix signals getchar


【解决方案1】:

有一种方法可以在不诉诸丑陋黑客的情况下中止呼叫(与 Paul R 所说的相反)。您应该使用 sigaction() 并将 sa_flags 设置为 0 而不是 signal()

此外,signal(2) 手册说:

避免使用:改用sigaction(2)。

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

static char done = 0;
static void sigHandler(int signum)
{
    done = 1;
}

int user_input()
{
    return (getchar() == 'q') ? 0 : 1;
}

int main(void)
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(struct sigaction));
    sa.sa_handler = sigHandler;
    sa.sa_flags = 0;// not SA_RESTART!;

    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);

    while (user_input() != 0 && !done)
        usleep(1000);
    printf("exiting\n");
    return 0;
}

通常,在捕获和处理信号后,大多数(我不确定是否不是全部)系统调用将重新启动。这样,在处理完 sigint 信号后,您的 getchar 函数将继续执行,就好像什么都没发生一样。 您可以通过使用 sa_flags=0 调用 sigaction 来更改此行为。

这样,在处理完 SIGINT 之后,getchar 将返回 -1 并且 errno 将设置为“Interrupted system call”(我现在不记得常量名称了)。

您还必须重写您的 user_input() 函数来处理返回 -1 时的情况。

【讨论】:

  • 如果您在 sa_flags 中设置 SA_RESTART,那么系统调用应该重新启动。实际上,这通常没有正确实现,因此最好不要设置 SA_RESTART 并期望系统调用可能因信号而终止。但是,getchar 不是系统调用,它不会作为信号的结果提前返回。
  • @William Pursell:虽然 getchar 显然不是系统调用,但对系统调用 read(由编号 __NR_read 标识)的调用是在 getchar 中的某处进行的。如果没有这个调用,无论高级函数名称如何,您都无法从标准输入读取任何内容。并且该系统调用将或不会根据 sigaction 标志重新启动。如果仍有疑问,只需编译我发布的代码,看看它是否真的有效! :P
  • 是的,getchar 调用已读取。但是 getchar 在收到信号后不会返回,因为 getchar 会检查读取是否因信号而失败并重新启动它。 SA_RESTART 与 getchar 是否返回完全无关。
【解决方案2】:

代码实际上按预期工作 - 在您从 user_input() 返回之前,您不会测试 done 标志,这就是为什么您需要在 control-C 之后输入一个额外的字符。

如果您想在获得 control-C 时中止对 getchar 的调用,那么您可能不得不做一些丑陋的事情,例如使用setjmp/longjmp

【讨论】:

  • 谢谢。我认为会有一个比在获取 control-C 时中止调用更聪明的方法
【解决方案3】:

Ctrl-C 字符被getchar() 调用吃掉,sigHandler 函数永远不会执行。

Ctrl-C 不被getchar吃掉;它会导致传递信号并运行sigHandler。这将设置done 并返回。 只有这样才会调用getchar,它吃掉换行符,之后done被检查,所以程序退出。

顺便说一句,信号处理程序接受 int 参数,而不是 void

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-28
    • 1970-01-01
    • 1970-01-01
    • 2014-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多