gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 10:59:50 24 4
gpt4 key购买 nike

为什么我的程序直到按 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 循环从标准输入读取字符并运行直到 SIGINT被捕获了。之后 keepRunning将设置为 0和循环应该结束并终止程序。但是,当我按 Ctrl+C 时,我的程序不再接受任何输入,但在按 ENTER 键之前,它不允许我在终端中键入任何命令。这是为什么?

最佳答案

这是因为fgetc()正在阻止执行,以及您选择处理 SIGINT 的方式 - fgetc()不会被 EINTR 打断(请参阅 @AnttiHaapala 的回答以获得进一步的解释)。所以只有在你按下回车键后,才会释放 fgetc() ,keepRunning 正在评估中。

终端也是缓冲的,所以只有当你按下回车键时,它才会将字符发送到 FILE *缓冲区并将被 fgetc() 读取逐个。这就是为什么它只有在按下回车后才存在,而不是其他键。

“解决”它的几个选项之一是使用非阻塞 stdin、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 non-blocking
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) {
/* non-blocking non-eof will return 0 AND EAGAIN errno */
printf("EOF reached\n");
keepRunning = 0;
} else if (errno != EAGAIN) {
perror("read");
keepRunning = 0;
}
}
}
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) & ~O_NONBLOCK);
exit(EXIT_SUCCESS);
}

另请注意,我没有使用 fgetc() .由于 FILE * 的缓冲性质,它不适用于非阻塞 IO。

上述程序仅用于教育目的,不得用于“生产”用途。有几个问题需要注意,例如:
  • 所有 libc/系统调用都需要进行错误测试。
  • 如果输出比输入慢(printf() 可能很容易变慢),可能会导致饥饿并且信号不会被捕获(只有在输入超过/更慢后,内循环才会退出)。
  • 性能/减少系统调用:
  • read()可以填充更大的缓冲区。
  • epoll_wait可以返回多个事件而不是 1 个。
  • 关于c - 在按 ENTER 之前捕获 SIGINT 后程序不会结束?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58886885/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com