在LinuxC中如何在CTRL-C之后清除标准输出

在LinuxC中如何在CTRL-C之后清除标准输出,c,linux,stdout,sigint,C,Linux,Stdout,Sigint,我们不希望在用户中断后通过CTRL-C打印任何内容。我们已经尝试在sigInt信号处理程序中添加\uu fpurge以及fflush,但它不起作用 如何立即清除缓冲的标准输出值?我遇到了一些类似的线程,但没有我可以找到一个工作的解决方案 其他一些信息: 在sigInt信号处理程序中,即使在添加exit(0)之后,缓冲区内容也会被打印出来,但处理器会被终止 添加了出口(0)以缩小问题范围,我不想杀死处理器 我知道以上是预期行为,不知道如何避免 考虑这个已编辑的示例——已编辑;此操作不会退出进程:

我们不希望在用户中断后通过
CTRL-C
打印任何内容。我们已经尝试在sigInt信号处理程序中添加
\uu fpurge
以及
fflush
,但它不起作用

如何立即清除缓冲的标准输出值?我遇到了一些类似的线程,但没有我可以找到一个工作的解决方案

其他一些信息: 在sigInt信号处理程序中,即使在添加exit(0)之后,缓冲区内容也会被打印出来,但处理器会被终止


添加了出口(0)以缩小问题范围,我不想杀死处理器


我知道以上是预期行为,不知道如何避免

考虑这个已编辑的示例——已编辑;此操作不会退出进程:

#define  _POSIX_C_SOURCE 200809L /* For nanosleep() */
#include <unistd.h>
#include <stdlib.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <stdio.h>

static void exit_handler(int signum)
{
    int fd, result;

    /* If the standard streams are connected to a tty,
     * tell the kernel to discard already buffered data.
     * (That is, in kernel buffers. Not C library buffers.)
    */
    if (isatty(STDIN_FILENO))
        tcflush(STDIN_FILENO, TCIOFLUSH);
    if (isatty(STDOUT_FILENO))
        tcflush(STDOUT_FILENO, TCIOFLUSH);
    if (isatty(STDERR_FILENO))
        tcflush(STDERR_FILENO, TCIOFLUSH);

    /* Redirect standard streams to /dev/null,
     * so that nothing further is output.
     * This is a nasty thing to do, and a code analysis program
     * may complain about this; it is suspicious behaviour.
    */
    do {
        fd = open("/dev/null", O_RDWR);
    } while (fd == -1 && errno == EINTR);
    if (fd != -1) {
        if (fd != STDIN_FILENO)
            do {
                result = dup2(fd, STDIN_FILENO);
            } while (result == -1 && (errno == EINTR || errno == EBUSY));
        if (fd != STDOUT_FILENO)
            do {
                result = dup2(fd, STDOUT_FILENO);
            } while (result == -1 && (errno == EINTR || errno == EBUSY));
        if (fd != STDERR_FILENO)
            do {
                result = dup2(fd, STDERR_FILENO);
            } while (result == -1 && (errno == EINTR || errno == EBUSY));
        if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
            close(fd);
    }
}

static int install_exit_handler(const int signum)
{
    struct sigaction act;
    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = exit_handler;
    act.sa_flags = 0;
    if (sigaction(signum, &act, NULL) == -1)
        return errno;
    return 0;
}

int main(void)
{
    if (install_exit_handler(SIGINT)) {
        fprintf(stderr, "Cannot install signal handler: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    while (1) {
        struct timespec t = { .tv_sec = 0, .tv_nsec = 200000000L };

        printf("Output\n");
        fflush(stdout);

        nanosleep(&t, NULL);
    }

    /* Never reached. */
    return EXIT_SUCCESS;
}
#为nanosleep()定义_POSIX_C_SOURCE 200809L/**/
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
静态void exit_处理程序(int signum)
{
int-fd,结果;
/*如果标准流连接到tty,
*告诉内核放弃已经缓冲的数据。
*(也就是说,在内核缓冲区中,而不是在C库缓冲区中。)
*/
如果(isatty(标准文件号))
tcflush(标准文件号,TCIOFLUSH);
if(isatty(标准文件号))
tcflush(标准文件号,TCIOFLUSH);
如果(isatty(标准文件号))
tcflush(标准文件号,TCIOFLUSH);
/*将标准流重定向到/dev/null,
*这样就没有更多的输出了。
*这是一件令人讨厌的事情,而且是一个代码分析程序
*可能会抱怨这一点;这是可疑的行为。
*/
做{
fd=打开(“/dev/null”,O_RDWR);
}而(fd=-1&&errno==EINTR);
如果(fd!=-1){
如果(fd!=标准文件号)
做{
结果=dup2(fd,标准文件号);
}而(result=-1&&(errno==EINTR | | errno==EBUSY));
如果(fd!=标准输出文件号)
做{
结果=dup2(fd,标准输出文件号);
}而(result=-1&&(errno==EINTR | | errno==EBUSY));
如果(fd!=标准文件号)
做{
结果=dup2(fd,标准文件号);
}而(result=-1&&(errno==EINTR | | errno==EBUSY));
如果(fd!=STDIN\u文件号和&fd!=STDOUT\u文件号和&fd!=STDERR\u文件号)
关闭(fd);
}
}
静态int安装\退出\处理程序(const int signum)
{
结构动作法;
memset(&act,0,sizeof act);
sigemptyset(和act.sa_面具);
act.sa_handler=退出_handler;
act.sa_标志=0;
if(sigaction(signum,&act,NULL)=-1)
返回errno;
返回0;
}
内部主(空)
{
if(安装\退出\处理程序(SIGINT)){
fprintf(stderr,“无法安装信号处理程序:%s。\n”,strerror(errno));
返回退出失败;
}
而(1){
结构timespec t={.tv_sec=0.tv_nsec=2000000000l};
printf(“输出\n”);
fflush(stdout);
奈米睡眠(&t,空);
}
/*从未接触过*/
返回退出成功;
}
当进程接收到
SIGINT
信号时,它将首先刷新内核终端缓冲区中的任何内容,然后将标准流重定向到
/dev/null
(即无处)

请注意,您需要通过向进程发送术语或终止信号来终止进程(即,在另一个终端中,
killall./yourprogname


当您在远程连接上运行详细的进程时,可能会有大量信息一直在传输中。运行该进程的本地计算机和远程计算机的套接字缓冲区几乎都已满,因此可能比通常情况下大得多——在这种情况下,即使在快速(GbE)本地网络上,我也看到了几秒钟的延迟

这意味着将信号从本地机器传播到远程机器需要一段可测量的时间;在最坏的情况下,以秒为单位。只有这样,远程进程才会停止输出数据。所有挂起的数据仍然必须从远程机器传输到本地机器,这可能需要相当长的时间。(通常,瓶颈是终端本身;在大多数情况下,最小化终端会更快,这样它就不会试图呈现接收到的任何文本,只会在内部对其进行缓冲。)

这就是为什么Ctrl+C不会也不能立即停止远程输出

在大多数情况下,您将使用SSH连接到远程机器。协议也没有“清除”功能,这可能会有所帮助。包括我在内的许多人都考虑过这一点——至少我的香肠手指意外地将标签切换到可执行文件,而不是类似命名的输出文件,不仅使终端充满垃圾,而且二进制文件中的特殊字符有时会将终端状态(例如)设置为无法恢复的状态(即,Ctrl+Z后跟
reset
Enter不会将终端重置回工作状态;如果重置,则,
kill-kill%-;fg
将停止Bash中的错误命令,并使您的终端返回),您需要断开连接,这也将终止从后台远程运行的同一终端启动的所有进程


这里的解决方案是使用终端多路复用器,例如,它允许您连接到远程机器并断开与远程机器的连接,而不中断现有的终端连接。(简单地说,
screen
是您在远程机器上的终端化身。)问题在于Posix和Linux库都没有声明信号处理函数中的
fpurge
\u fpurge
是安全的。正如DevSolar所解释的,C语言itsel没有为标准库声明许多安全函数(至少
\u退出
,但是Posix明确地允许
关闭
写入
。因此,您可以始终关闭应该关闭的底层文件描述符
void handler(int sig) {
    static char msg[] = "Interrupted";
    write(2, msg, sizeof(msg) - 1);  // carefully use stderr here
    close(1);  // foo is displayed if this line is commented out
    _Exit(1);
}
int main() {
    signal(SIGINT, handler);
    printf("bar");
    sleep(15);
    return 0;
}
$ ./foo
^CInterrupted with 2
$