在 ncurses 中链接信号处理程序

问题描述

我的 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库集成。这是相当复杂的,并且需要相当多的工作。目前还不清楚您不需要的输入的来源是什么。输入不是凭空产生的,因此必须有一个原因,您可能希望首先朝那个方向看,而不是试图解决结果,而不是原因。