Почему Linux всегда выводит «^ C» при нажатии Ctrl + C?

Я изучал сигналы в Linux. И я сделал тестовую программу для захвата SIGINT.

#include <unistd.h>
#include <signal.h>
#include <iostream>
void signal_handler(int signal_no);
int main() {
  signal(SIGINT, signal_handler);
  for (int i = 0; i < 10; ++i) {
  std::cout << "I'm sleeping..." << std::endl;
  unsigned int one_ms = 1000;
  usleep(200* one_ms);
  }
  return 0;
}
void signal_handler(int signal_no) {
  if (signal_no == SIGINT)
    std::cout << "Oops, you pressed Ctrl+C!\n";
  return;
}

Хотя вывод выглядит так:

I'm sleeping...
I'm sleeping...
^COops, you pressed Ctrl+C!
I'm sleeping...
I'm sleeping...
^COops, you pressed Ctrl+C!
I'm sleeping...
^COops, you pressed Ctrl+C!
I'm sleeping...
^COops, you pressed Ctrl+C!
I'm sleeping...
^COops, you pressed Ctrl+C!
I'm sleeping...
I'm sleeping...
I'm sleeping...

Я понимаю, что при нажатии Ctrl + C процессы в группе процессов переднего плана все получают SIGINT (если ни один процесс не решит игнорировать его).

Так получается, что оболочка (bash) И экземпляр вышеуказанной программы получили сигнал? Откуда берется & quot; ^ C & quot; перед каждым "Упс" родом из?

ОС CentOS, а оболочка bash.

Ответы на вопрос(3)

потому что (в Linux) ни ksh, ни tcsh не демонстрируют такого поведения, в то время как bash это делает. Небольшое возмущение показало, что виновна библиотека readline (которую использует bash) (см.https://unix.stackexchange.com/questions/333766/remove-c-when-ctrlc). Поведение контролируется с помощью «echo-control-характеров» в $ HOME / .inputrc. Установка его на «выключен» отключит поведение. Поведение уже запущенных процессов не изменяется с помощью этого параметра.

Решение Вопроса

который перехватывает ^ C и преобразует его в сигнал, отправляемый присоединенному процессу (который является оболочкой)stty intr ^B будет указывать водителю терминала перехватывать ^ B вместо этого. Это также драйвер терминала, который возвращает ^ C обратно в терминал.

Оболочка - это просто процесс, который находится на другом конце линии и получает его стандартный ввод от вашего терминала через драйвер терминала (такой как / dev / ttyX), и к нему также прикрепляются стандартный вывод (и stderr) к тому же тты.

Обратите внимание, что (если эхо включено), терминал отправляет нажатия клавишboth процесс (группа) и обратно в терминал. Команда stty является просто оболочкой для ioctl () для драйвера tty для процессов, «управляющих». TTY.

ОБНОВЛЕНИЕ: чтобы продемонстрировать, что оболочка не задействована, я создал следующую небольшую программу. Это должно быть выполнено его родительской оболочкой черезexec ./a.out (в любом случае, интерактивная оболочка, в любом случае, разветвляет дочернюю оболочку) Программа устанавливает ключ, который генерирует SIGINTR, на ^ B, выключает эхо и затем ожидает ввода от stdin.

#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

int thesignum = 0;
void handler(int signum);

void handler(int signum)
{ thesignum = signum;}

#define THE_KEY 2 /* ^B */

int main(void)
{
int rc;
struct termios mytermios;

rc = tcgetattr(0 , &mytermios);
printf("tcgetattr=%d\n", rc );

mytermios.c_cc[VINTR] = THE_KEY; /* set intr to ^B */
mytermios.c_lflag &= ~ECHO ; /* Dont echo */
rc = tcsetattr(0 , TCSANOW, &mytermios);
printf("tcsetattr(intr,%d) =%d\n", THE_KEY, rc );

printf("Setting handler()\n" );
signal(SIGINT, handler);

printf("entering pause()\n... type something followed by ^%c\n", '@'+THE_KEY );
rc = pause();
printf("Rc=%d: %d(%s), signum=%d\n", rc, errno , strerror(errno), thesignum );

// mytermios.c_cc[VINTR] = 3; /* reset intr to ^C */
mytermios.c_lflag |= ECHO ; /* Do echo */
rc = tcsetattr(0 , TCSANOW, &mytermios);
printf("tcsetattr(intr,%d) =%d\n", THE_KEY, rc );

return 0;
}

intr.sh:

#!/bin/sh
echo $
exec ./a.out
echo I am back.
 WiSaGaN04 июн. 2012 г., 09:19
Эта статья содержит очень полезное введение в TTY (терминал).linusakesson.net/programming/tty/index.php
 28 мая 2012 г., 13:56
@WiSaGaN Как один и тот же ответ может быть «почти правильным»? (прокомментируйте мне) и "просто неправильно" (комментарий к ОП).Of course Драйверы устройств участвуют при обмене данными с системой через периферийные устройства (как получилось, что вы не упомянули драйвер клавиатуры?) Предоставление более подробной информации не делает «почти правильным». ответ "совершенно неправильно". Если бы я перешел на уровень машинного языка и таблиц поиска, это сделало бы ваш ответ неверным? Едва. Я предоставил ссылки на книги, чтобы OP мог искать больше деталей по мере необходимости. ОП чувствовал, что мой ответ был полезным. Ваш текущий комментарий в лучшем случае противоречив и неискренен.
 27 мая 2012 г., 17:43
Забавно, что другой ответ (который совершенно неверен) все еще получает голоса, просто потому что он содержит ссылки на «Правильные книги».
 28 мая 2012 г., 14:02
Неправильная вещь в том, чтоthe shell is not involved, Преобразование из ^ C в INTR происходит в драйвере терминала. Оболочка является только рецептором (как и любой другой процесс). Stty нацелен не на оболочку, а на tty.
 28 мая 2012 г., 15:01
Кстати: «почти правильно» был вызван вежливостью на моей стороне. «Конечно же, водители устройства работают» это очень нечеткое утверждение. Я не упомянул драйвер клавиатуры, потому что он не участвует вgenerating the signal, только при конвертации скан-кодов в используемые символы. Там даже не должно быть клавиатуры; tty может ссылаться на модемную линию или даже на pty через telnet (в этом случае генерация INTR будет идти по совершенно иному пути, но все же будет выполняться драйвером pty). Поскольку вы ссылаетесь на APUE: глава 11 может быть полезно.

^Cэто тоже отражается (и в вашем случае перехватывается вашим обработчиком сигнала). Командаstty -echo может или не может быть полезным для вас в зависимости от ваших потребностей / ограничений, см.Страница man для stty для дополнительной информации.

Конечно, гораздо больше происходит на более низком уровне,anytime вы взаимодействуете с системой через драйверы периферийных устройств (например, драйвер клавиатуры, который вы используете для генерации сигнала ^ C, и драйвер терминала, который отображает все). Вы можете копать еще глубже на уровне ассемблера / машинного языка, регистров, справочных таблиц и т. Д. Если вы хотите более подробный, глубокий уровень понимания книг, приведенных ниже, это хорошее место для начала:

Дизайн ОС Unix хороший справочник для такого рода вещей. Еще две классические ссылки:Среда программирования Unix and Расширенное программирование в среде UNIX

Хорошее резюме здесь в этом вопросеКак Ctrl-C завершает дочерний процесс?

& quot; когда вы запускаете программу, напримерfindоболочка:

the shell fork itself and for the child set the default signal handling replace the child with the given command (e.g. with find) when you press CTRL-C, parent shell handle this signal but the child will receive it - with the default action - terminate. (the child can implement signal handling too)"
 WiSaGaN26 мая 2012 г., 14:34
Да,stty Команда действительно помогает. Спасибо вам обоим за объяснение!
 26 мая 2012 г., 14:26
Почти верно. Это терминал (драйвер), который перехватывает ^ C и преобразует его в сигнал, отправляемый присоединенному процессу (который является оболочкой)stty intr ^B будет указывать водителю терминала перехватывать ^ B вместо этого. Это также драйвер терминала, который возвращает ^ C обратно в терминал.
 WiSaGaN26 мая 2012 г., 14:05
Вы имеете в виду, что bash здесь также находится в группе процессов переднего плана, поэтому он получил сигнал, отправленный ядром?
 26 мая 2012 г., 14:08
@ WiSaGaN Я имею в виду, что вы используете клавиатуру для связи с вашей программой, а оболочка находится между вами и программой (т.е.interface) так что перехватывает это действие. Ваш обработчик сигналов определяетsubsequent действие, которое происходит после^C виден. Таким образом, вместо того, чтобы завершить программу, вы можете сделать что-то еще, например, напечатать сообщение в вашем случае.
 WiSaGaN26 мая 2012 г., 14:18
Таким образом, это фактически оболочка, отправляющая сигнал группе процессов переднего плана (ядро фактически реализует ее). Итак, лежащие в основе события, клавиатура поймала нажатие клавиш Ctrl + C и сигнализирует ЦПУ. Затем аппаратное прерывание вызвало обработчик прерывания, который сообщает, что оболочка Ctrl + C нажата. Затем сигнал отправляется оболочкой. Таким образом, нажатие клавиши «Ctrl + C» на «сигнал SIGINT»; преобразование производится оболочкой. Я прав?

Ваш ответ на вопрос