使用scanf in循环处理SIGINT

时间:2016-06-28 07:32:33

标签: c signals scanf sigint

我必须在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提供输入的额外步骤?

1 个答案:

答案 0 :(得分:1)

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

signal(SIGINT, my_signal_handler);
}之前<{1>} 内的

,但最好使用sigaction(2)

您还应该知道 stdio buffering;通常main在终端时是行缓冲的(但请参阅setvbuf(3)和朋友)。因此,您应该在循环内的while(running)之前调用fflush(3)(可能为stdout),或者使用显式fflush(NULL);格式控制字符串> scanf

最后,scanf(3)可能会失败,并且会返回您应该测试的已扫描项目的数量。

顺便说一句,您的printf错误,应定义为\n或最好main

但是(假设您使用的是Linux),请仔细阅读signal(7)(注意有关信号处理程序和异步信号安全功能的说法)和POSIX signal.h documentation并声明您的int main(void)标记为

int main(int argc, char**argv)

(或者,在C11中,作为running

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

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

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

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