程序在按ENTER键之前捕获SIGINT后不会结束?

问题描述 投票:2回答:2

为什么我的程序在按Ctrl + C后在终端中按ENTER才结束?

这是我的代码:

static volatile sig_atomic_t keepRunning = 1;

void intHandler(int sig) 
{
    keepRunning = 0;
}

int main(int argc, char *argv[])
{
    signal(SIGINT, intHandler);

    int ch; 
    while((ch = fgetc(stdin)) && keepRunning)
    {
      ...
    }
    exit(EXIT_SUCCESS);
}

我已经设置了while循环以从stdin读取字符并运行直到捕获到SIGINT。之后,keepRunning将设置为0,循环应结束并终止程序。但是,当我按Ctrl + C时,我的程序不再接受任何输入,但是直到按ENTER键,它才允许我在终端中键入任何命令。这是为什么?

c volatile sigint signal-handling
2个回答
2
投票

这是因为fgetc()正在阻止执行。因此,只有在按下Enter键(释放fgetc())之后,keepRunning才会被评估。

终端也被缓冲,因此仅当您按Enter时,它将字符发送到FILE *缓冲区,并由fgetc()逐一读取。这就是为什么它仅在按Enter键之后才存在,而在其他键之后不存在的原因。

“解决”的几个选项之一是使用非阻塞标准输入,signalfd和epoll(如果使用linux:]]

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <error.h>

int main(int argc, char *argv[])
{
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);

    /* Block signals so that they aren't handled
       according to their default dispositions */
    sigprocmask(SIG_BLOCK, &mask, NULL); // need check

    // let's treat signal as fd, so we could add to epoll
    int sfd = signalfd(-1, &mask, 0); // need check

    int epfd = epoll_create(1); // need check

    // add signal to epoll
    struct epoll_event ev = { .events = EPOLLIN, .data.fd = sfd };
    epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &ev); // need check

    // Make STDIN nonblocking 
    fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);

    // add STDIN to epoll
    ev.data.fd = STDIN_FILENO;
    epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev); // need check

    char ch;
    int keepRunning = 1; // no need to synchronize anymore
    while(keepRunning) {
        epoll_wait(epfd, &ev, 1, -1); // need check, must be always 1
        if (ev.data.fd == sfd) {
            printf("signal caught\n");
            keepRunning = 0;
        } else {
            ssize_t r;
            while(r = read(STDIN_FILENO, &ch, 1) > 0) {
                printf("%c", ch);
            }
            if (r == 0 && errno == 0) { // nonblocking non-eof will return Resource temporarily unavailable errno
                printf("EOF reached\n");
                keepRunning = 0;
            } else if (errno != EAGAIN) {
                perror("read");
                keepRunning = 0;
            }
        }
    }
    exit(EXIT_SUCCESS);
}

还请注意,我没有使用fgetc()。由于FILE *的缓冲性质,因此无法与非阻塞IO配合使用。


0
投票

问题在于,由signal设置的信号将被设置为自动重启模式。通常,如果在阻塞时传递了信号,则系统调用以errno == EINTR返回。

© www.soinside.com 2019 - 2024. All rights reserved.