问题描述
我的 Ncurses 应用程序正在循环键盘输入并将输出打印到屏幕。当我停止我的应用程序 (ctrl+z) 并稍后恢复它时,我注意到输入缓冲区有时包含不需要的字符。所以我想用flushinp()来丰富ncurses默认提供的SIGCONT处理程序,简化代码如下:
#include <csignal>
#include <curses.h>
#include <stdlib.h>
#include <fstream>
struct sigaction oldact,newact;
extern "C" void sigContHandler(int sig)
{
std::ofstream of("sc",std::ofstream::app);
of << "Handling cont " << std::endl;
flushinp();
//Call old handler,but crash as it is always 0
(oldact.sa_handler)(sig);
}
int main(void)
{
initscr();
cbreak();
noecho();
clear();
newact.sa_handler = sigContHandler;
newact.sa_flags = 0;
sigemptyset (&newact.sa_mask);
sigaction(SIGCONT,&newact,&oldact);
int c = 0;
while (c = getch()) {
if (c == 'q')
break;
mvaddch(0,c);
}
refresh();
endwin();
exit(0);
}
我无法调用旧的 SIGCONT 处理程序,因为它始终为 0。显然,如果我不向链信号处理程序添加代码,则会成功调用此默认处理程序。我不明白为什么我不能打电话给老处理程序,我做错了什么?
解决方法
我在 flushinp
的文档中没有发现任何表明它是信号安全的。由于信号是什么以及它们如何工作,默认情况下任何库函数——尤其是第三方库——都被默认为信号不安全,除非另有说明.因此,它们不能从异步信号处理程序中调用。
请注意,例如,signal-safety(7) manual page 显式枚举了唯一的信号安全的 C 库函数。除非该函数在列表中,否则不能从信号处理程序中调用它。 C++ 库类、模板和函数都不是信号安全的。
如果您的信号处理程序所做的所有操作都被称为原始处理程序(如果它不是 NULL,并且如果它是 NULL,则什么都不做),那就没问题了。但是 flushinp
的调用引入了未定义的行为,这就是该行的结尾。
在 Linux 上安全执行此操作的唯一方法是使用 a signal file descriptor,有效地将异步信号处理程序转换为文件描述符事件,并为标准输入实现多路复用非阻塞 poll
循环和信号文件描述符,与curses库集成。这是相当复杂的,并且需要相当多的工作。目前还不清楚您不需要的输入的来源是什么。输入不是凭空产生的,因此必须有一个原因,您可能希望首先朝那个方向看,而不是试图解决结果,而不是原因。