Рассмотрите этот отредактированный пример - отредактирован; этот не выходит из процесса:
#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;
}
Когда процесс получает сигнал SIGINT
, он сначала промыть все, что в терминале ядра буфера, а затем перенаправлять стандартные потоки /dev/null
(т.е. нигде).
Обратите внимание, что вам нужно будет убить процесс, отправив ему сигнал TERM или KILL (т. Е. killall ./yourprogname
в другом терминале).
Когда вы запускаете процесс многословный через удаленное соединение, достаточно много информации может быть в полете в любое время. Как локальная машина, так и удаленная машина, работающая с этим процессом, будут иметь свои буферы сокетов почти полными, поэтому latency может быть намного больше, чем обычно - я видел несколько вторых латентностей в этом случае даже в быстрых (GbE) локальных сетях.
Это означает, что распространение сигнала от локальной машины на удаленную машину займет измеримое время; в худшем случае - порядка секунд. Только тогда удаленный процесс остановит вывод данных. Все ожидающие данные все равно должны будут передаваться с удаленного компьютера на локальный компьютер, и это может занять довольно много времени. (Как правило, узким местом является сам терминал, а в большинстве случаев это быстрее минимизировать терминал, так что он не пытается оказывать какой-либо из текста он получает, только буферы его внутри.)
Это почему Ctrl + C не делает и не может остановить удаленный выход мгновенно.
В большинстве случаев вы будете использовать SSH-соединение с удаленной машиной. В протоколе также нет функции «очистки», которая может помочь здесь. Многие, включая меня, подумали об этом - по крайней мере, у моих пальцев колбасы случайно вкладка кровать к исполняемому файлу вместо выведенного файла с аналогичным именем и не только получила терминал, полный мусора, но и специальные символы в двоичном формате файлы иногда устанавливают состояние терминала (см., например, xterm control sequences, ANSI escape codes) на что-то неисправимое (т.е., Ctrl + Z затем reset
Ввод не возвращает терминал в рабочее состояние; если бы это произошло, kill -KILL %- ; fg
остановил бы ошибочную команду в Bash и вернул вам ваш терминал), и вам нужно разорвать соединение, которое также прекратит все процессы, запущенные с того же терминала, который удаленно удален в фоновом режиме.
Решение здесь заключается в использовании терминального мультиплексора, например GNU screen, который позволяет подключаться к удаленному компьютеру и отключать его от сети, не прерывая существующее соединение терминала. (Проще говоря, screen
ваш терминал аватар на удаленной машине.)
Вы пытались закрыть дескриптор файла? – Aconcagua
'exit()' не [async-signal-safe] (http://man7.org/linux/man-pages/man7/signal.7.html). Вместо этого вы должны использовать '_Exit()'. (Кроме того, версия библиотеки GNU C не сбрасывает потоки в '_Exit()', в то время как она работает на 'exit()', так что эта проблема также устраняет проблему. В противном случае вы могли бы перенаправить базовые дескрипторы на '/ dev/null/'-' open() 'и' dup2() 'являются безопасными для асинхронного сигнала, поэтому, если что-то покраснеть, будет сброшено на/dev/null вместо.) –
И скажите, какой компилятор вы используете, там возможно, будут различия ... – Aconcagua