在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
$