Во-первых, извините, что я не смог привести сокращенный пример. На данный момент это было выше моих сил. Особенно мои коды, которые передают файловые дескрипторы, работали не совсем корректно. Я думаю, что у меня есть только хорошее понимание того, как код работает на высоком уровне.
По сути, вопрос заключается в том, если в следующем сложном примере конечный пользователь вводит Ctrl + C, какой процесс получает SIGINT и как это происходит.
Приложение работает в интерфейсе командной строки (CLI, в дальнейшем). Пользователь запускает клиент, который фактически отправляет команду на сервер, распечатывает некоторые ответы и завершает работу. Сервер по запросу находит подходящий рабочий исполняемый файл и fork-and-exec исполняемый файл и ожидает его. Затем сервер формирует ответ и отправляет его обратно клиенту.
Однако есть некоторые сложности. Клиент запускает сервер, если серверный процесс еще не запущен — на каждого пользователя приходится один серверный процесс. Когда сервер отмечен fork-and-exec, в строке сразу после fork() будет следующее:
if (pid == 0) {
daemon(0, 0);
// do more set up and exec
}
Другая сложность, которая может быть более важной, заключается в том, что когда клиент отправляет запрос через сокет unix (который выглядит как @server_name), клиент отправляет три файловых дескриптора для стандартного ввода-вывода, используя такие методы, как этот .
Когда сервер получает исполняемый файл рабочего процесса, сервер перенаправляет стандартный ввод-вывод рабочего процесса на три файловых дескриптора, полученных от клиента:
// just after fork(), in the child process' code
auto new_fd = fcntl(received_fd, F_DUPFD_CLOEXEC, 3);
dup2(new_fd, channel); // channel seems to be 0, 1, or 2
Этот фрагмент кода выполняется для всех трех файловых дескрипторов соответственно. (Исполняемый рабочий файл снова создает кучу процессов, но не передает fork-and-exec своим потомкам.)
Вопрос в том, что произойдет, если конечный пользователь введет STDIN в терминал. Я подумал, что оболочка Ctrl + C берет его, генерирует и отправляет Bash процессам, которые имеют определенный идентификатор сеанса, возможно, такой же, как прямой дочерний процесс оболочки bash или сам: клиент, в примере.
Однако похоже, что рабочий исполняемый файл получает сигнал, и я не могу подтвердить, получает ли клиент сигнал. Я не думаю, что серверный процесс получает сигнал, но не могу это подтвердить. Как такое могло произойти?
Если SIGINT сначала берет Bash и доставляет его каким-либо процессам, я подумал, что сервер был отсоединен от Ctrl+C (то есть Bash) и не имеет ничего общего с процессом bash. Я думал, что сервер и, следовательно, рабочие процессы имеют разные идентификаторы сеанса, и это выглядело так, когда я запускал команду daemon(0, 0).
Понятно, что пользовательский ввод с клавиатуры (ps -o или yes и т. д.) может быть доставлен в рабочий процесс. Я не уверен, как no можно доставить в рабочий процесс, просто эффективно разделив стандартный ввод. Я хотел бы понять, как это работает.
%P.S.Спасибо за ответы и комментарии! Ответ был действительно полезным. Звучало так, будто клиент должен получить сигнал, а рабочий процесс должен быть остановлен другим механизмом. Исходя из этого, я мог глубже изучить код. Оказалось, что клиент действительно ловит сигнал и умирает. Это разрывает соединение сокета. Сервер определяет, когда fd сломан, и сигнализирует соответствующему рабочему процессу. Поэтому рабочий процесс выглядел как получение сигнала от терминала.
@stark Я не думаю, что это правильно.
Сигнал отправляет не Bash, а драйвер tty. Он отправляет его в группу процессов переднего плана, то есть все процессы в группе переднего плана получают его.
Какой бы процесс ни выполнял чтение, он получит прерывание ctrl-C