为什么我的程序在按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键,它才允许我在终端中键入任何命令。这是为什么?
这是因为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配合使用。
问题在于,由signal
设置的信号将被设置为自动重启模式。通常,如果在阻塞时传递了信号,则系统调用以errno == EINTR
返回。