【问题标题】:handle SIGINT with scanf in loop在循环中使用 scanf 处理 SIGINT
【发布时间】:2016-06-28 07:32:32
【问题描述】:

我必须在 while 循环中接受用户的输入,然后采取一些行动。而且我还想在 ctrl+c 输入时退出我的代码。

void my_signal_handler(int sig)
{
    running = false;
    signal(sig, SIG_IGN);
}
int main(void)
{
    struct sigaction sa = {{0}};
    sa.sa_handler = &my_signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;

    if (sigaction(SIGINT, &sa, NULL) != 0)
    {
        fprintf(stderr, "sigaction error\n");
        return -1;
    }
    while(running)
    {
        printf("enter number: ");
        scanf("%d", &num);
        // take action based on  number
    }
}

此代码的问题是在按下 ctrl+c 后,它不会退出,但它会等待 scanf 的输入。因此,一旦我按下一个额外的键,程序就会退出(调用信号处理程序)。

按 ctrl+c 后如何删除向 scanf 提供输入的额外步骤?

【问题讨论】:

  • 您能否通过激励来改进您的问题(所以请编辑您的问题),好吗?
  • 顺便说一句,给定的代码无法编译。 scanf(%d", &num);中缺少双引号

标签: c signals scanf sigint


【解决方案1】:

您应该安装信号处理程序。至少,添加

signal(SIGINT, my_signal_handler);

在您的main之前while(running),但最好使用sigaction(2)

你也应该知道stdiobuffering;通常stdout 在它是终端时是行缓冲的(但请参阅setvbuf(3) 和朋友)。因此,您应该在循环内的scanf 之前调用fflush(3)(可能是fflush(NULL);),或者使用显式 \n 终止每个printf 格式控制字符串。

最后,scanf(3) 可能会失败并返回扫描项目的计数,您应该对其进行测试。

顺便说一句,你的main 是错误的,应该定义为int main(void) 或者最好是int main(int argc, char**argv)

但是(假设您使用的是 Linux),请仔细阅读 signal(7)(注意有关信号处理程序和异步信号安全函数的内容)和 POSIX signal.h documentation 并将您的 running 标志声明为

volatile sigatomic_t running;

(或者,在 C11 中,可能是 volatile _Atomic bool running;

volatile qualifier 非常重要。否则,允许编译器进行优化(例如,假装running总是为真)。

请注意,使用signal(2) 通常是个坏主意。首先,如果你真的需要信号处理,你最好使用sigaction(2)。那么在您的情况下,您对 signal(sig, SIG_IGN); 的调用是无用的(因为 running volatile 标志将在信号处理程序中更改)。最后,对于多路输入(&输出),你可以使用poll(2)来等待和测试stdin上是否有可用的输入(实际上STDIN_FILENO是0),并且更普遍地实现event loops。你可以使用(而不是我强烈推荐的poll)旧的和几乎过时的select(2),但你宁愿使用poll(2)。另请参阅 epoll(7)inotify(7),但您可能不需要它们。

请注意,在终端中,stdin 通常是line discipline 之后的 tty(请使用 isatty(3) 检查)(因此某些行缓冲发生在在内核中)。阅读tty demystified 页面。考虑使用 GNU readline 库和函数(或者可能是 ncurses),这可能是您真正需要的。

另请阅读Advanced Linux Programming,并养成阅读您正在使用的每个函数的文档的习惯。

【讨论】:

  • 实际上在 //signal handler configuration 行中,我已经为 sigaction 添加了适当的代码。我将再次更新代码以显示我的实际代码。
  • @BS 你能否展示这一行的代码示例“多路复用输入(和输出),你可以使用 poll(2) 来等待和测试标准输入上是否有一些可用的输入(实际上 STDIN_FILENO 是 0)。”
  • 这对我来说可能会花费太多时间(尤其是如果您对 Linux 编程不是很熟悉)。我提供了参考资料,尤其是 ALP 书
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-07
  • 1970-01-01
  • 2021-09-16
  • 1970-01-01
  • 2013-12-28
  • 2013-11-23
  • 2023-03-07
相关资源
最近更新 更多