С zsh Вы могли сделать что-то как:
#! /bin/zsh -
(($+ARG0_SET)) || ARG0_SET= ARGV0="#Running on 10.10.45.10" exec zsh "$0" "$@"
ps -f -p "$$"
Который, когда выполненный дает:
~$ ./a
UID PID PPID C STIME TTY TIME CMD
chazelas 20157 8822 0 21:51 pts/1 00:00:00 #Running on 10.10.45.10 ./a
ksh93
эквивалентный был бы:
#! /bin/ksh93 -
((${#ARG0_SET})) || ARG0_SET=yes exec -a "#Running on 10.10.45.10" ksh93 "$0" "$@"
ps -f -p "$$"
Первое, что нужно понять, как работает CTRL+C.
При нажатии CTRL+C терминальный эмулятор посылает символ ETX (конец текста / 0x03).
TTY настроен таким образом, что при получении этого символа он посылает SIGINT в группу процессов на переднем плане терминала. Эту конфигурацию можно просмотреть, сделав stty
и посмотрев на intr = ^C;
.
Спецификация POSIX гласит, что при получении INTR он должен послать SIGINT в группу процессов переднего плана этого терминала.
Итак, теперь вопрос в том, как определить, что такое группа процессов переднего плана? Группа процессов переднего плана - это просто группа процессов, которые будут получать любые сигналы, генерируемые клавиатурой (SIGTSTOP, SIGINT и т.д.).
Простейший способ определения идентификатора группы процессов - использовать ps
:
ps ax -O tpgid
Второй столбец будет идентификатором группы процессов.
Теперь, когда мы знаем, что такое ID группы процесса, нам нужно смоделировать поведение POSIX при отправке сигнала всей группе.
Это можно сделать с помощью kill
, поставив -
перед идентификатором группы.
Например, если идентификатор вашей группы процесса 1234, вы используете:
kill -INT -1234
Таким образом, вышеописанная схема моделирует CTRL+C как ручной процесс. Но что, если вы знаете номер TTY и хотите смоделировать CTRL+C для этого терминала?
Это становится очень просто.
Предположим, что $tty
- это терминал, на который вы хотите нацелиться (это можно сделать, запустив tty | sed 's#^/dev/##'
в терминале).
kill -INT -$(ps h -t $tty -o tpgid | uniq)
Это отправит SIGINT на любую группу процесса переднего плана из $tty
.
Как говорит Винс17, для этого нет причин.
Когда вы набираете последовательность клавиш, генерирующих сигнал (например, Ctrl+C),
сигнал посылается всем процессам, которые подключены к терминалу (связаны с ним).
Подобного механизма для сигналов, генерируемых kill
, не существует.
Однако команда типа
kill -SIGINT -12345
будет посылать сигнал всем процессам, входящим в группу процессов 12345; см. kill(1). и убить(2) . Дети оболочки обычно входят в группу процессов оболочки. (по крайней мере, если они не асинхронные), так что отправка сигнала на отрицательный PID оболочки может сделать то, что вы хотите.
Как указывает Винс17, это не работает для интерактивных оболочек. Вот альтернатива, которая может работать:
kill -SIGINT -$(echo $(ps -pPID_of_shell o tpgid=))
ps -pPID_of_shell
получает информацию о процессе в оболочке.
o tpgid=
сообщает ps
, что выводится только идентификатор группы терминальных процессов, без заголовка.
Если он меньше 10000, то ps
выводит его с опережающим(ыми) пробелом(ами);
$(echo ...)
представляет собой быструю уловку для снятия опережающих (и опережающих) пробелов.
Я заставил это работать в беглом тестировании на машине Debian.
Нет причин распространять ЗАПИСЬ
на ребенка. Более того, спецификация system()
POSIX гласит: "Функция system() должна игнорировать сигналы SIGINT и SIGQUIT и блокировать сигнал SIGCHLD, ожидая завершения команды"
Если оболочка распространяла полученный сигнал SIGINT
, например, следуя реальному Ctrl-C, это означало бы, что дочерний процесс получал сигнал SIGINT
дважды, что могло бы привести к нежелательному поведению.
Кажется, что так и должно быть:
Из журнала изменений selinux-policy-3.12.1-139:
- Разрешить ввод systemd_cronjob_t через, bin_t
Do у вас есть какие-либо ошибки в /var/log/audit/audit.log
, относящиеся к mariadb
? Быстрая и простая проверка состоит в том, чтобы setenforce = 0
и запустить задания cron
. Если им лучше, то это был SELinux, вызвавший проблему.
Если в системе имеется systemd, для этого можно использовать события таймеров. Просто определите новую службу , которая должна содержать команду/задачу, которую необходимо выполнить, а затем создайте событие таймера с параметром OnUnitActiveSec
:
[Unit]
Description=daily + 1 hour task
[Timer]
OnUnitActiveSec=25h # run 25 hours after service was last started
AccuracySec=10min
[Install]
WantedBy=timers.target
Используйте то же имя для файлов, за исключением того, что вместо .service
используется .timer
.
Синтез:
job.service
в каталоге /etc/systemd/system/
. systemctl status job.service
. job.timer
в /etc/systemd/system/
. Заполните его необходимой информацией:
[Единица измерения]
Описание = ежедневная + 1 часовая задача
[Таймер]
OnUnitActiveSec = 25h # запуск через 25 часов после последнего запуска службы
Рс = 10мин
[Установить]
PaydBy = timers.target
systemctl list-timers
Вопрос содержит свой ответ. Отправка SIGINT
в процесс cat
с помощью kill
является идеальным моделированием того, что происходит при нажатии ^ C
.
Более точно, символ прерывания ( ^ C
по умолчанию) посылает SIGINT
каждому процессу в основной группе процессов терминала. Если бы вместо cat
выполнялась более сложная команда, включающая несколько процессов, для достижения того же эффекта, что и ^ C
, необходимо было бы убить группу процессов.
При выполнении любой внешней команды без оператора фонового режима и
оболочка создает новую группу процессов для этой команды и уведомляет терминал о том, что эта группа процессов теперь находится в основном режиме. Оболочка по-прежнему находится в собственной группе процессов, которая больше не находится на переднем плане. Затем оболочка ожидает выхода команды.
Вот где вы, кажется, стали жертвой распространенного заблуждения: идея о том, что оболочка делает что-то, чтобы облегчить взаимодействие между ее детским процессом (ами) и терминалом. Это просто неправда. После выполнения работы по настройке (создание процесса, настройка терминального режима, создание каналов и перенаправление других дескрипторов файлов и выполнение целевой программы) оболочка просто ожидает . То, что вы вводите в cat
не проходит через оболочку,нормальный вход или генерирующий сигнал специальный символ, например ^ C
. Процесс cat
имеет прямой доступ к терминалу через собственные файловые дескрипторы, а терминал имеет возможность посылать сигналы непосредственно процессу cat
, поскольку он является основной группой процессов. Снаряд сошел с пути.
После завершения процесса cat
оболочка будет уведомлена, поскольку она является родительским объектом процесса cat
. Затем оболочка становится активной и снова ставит себя на передний план.
Вот упражнение, чтобы улучшить ваше понимание.
В ответ на запрос оболочки в новом терминале выполните следующую команду:
exec cat
Ключевое слово exec
вызывает выполнение оболочки cat
без создания дочернего процесса. Оболочка заменена на кошку
. PID, ранее принадлежавший оболочке, теперь является PID cat
. Проверьте это с помощью ps
в другом терминале. Введите несколько случайных строк и посмотрите, что cat
повторяет их обратно вам, доказывая, что он все еще ведет себя нормально, несмотря на отсутствие процесса оболочки в качестве родительского. Что произойдет, когда вы нажмете ^ C
сейчас?
Ответ:
SIGINT доставляется в процесс cat, который умирает. Потому что это был единственный процесс на терминале, сеанс заканчивается, как если бы вы сказали «выход» в командной строке. Фактически кот некоторое время был вашей оболочкой.
setpgid
Минимальный пример группы процессов POSIX C
Это может быть проще для понимания на минимальном работоспособном примере базового API.
Это иллюстрирует, как сигнал передается дочернему процессу, если дочерний процесс не изменил свою группу процессов с помощью setpgid
.
main.c
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
volatile sig_atomic_t is_child = 0;
void signal_handler(int sig) {
char parent_str[] = "sigint parent\n";
char child_str[] = "sigint child\n";
signal(sig, signal_handler);
if (sig == SIGINT) {
if (is_child) {
write(STDOUT_FILENO, child_str, sizeof(child_str) - 1);
} else {
write(STDOUT_FILENO, parent_str, sizeof(parent_str) - 1);
}
}
}
int main(int argc, char **argv) {
pid_t pid, pgid;
(void)argv;
signal(SIGINT, signal_handler);
signal(SIGUSR1, signal_handler);
pid = fork();
assert(pid != -1);
if (pid == 0) {
is_child = 1;
if (argc > 1) {
/* Change the pgid.
* The new one is guaranteed to be different than the previous, which was equal to the parent's,
* because `man setpgid` says:
* > the child has its own unique process ID, and this PID does not match
* > the ID of any existing process group (setpgid(2)) or session.
*/
setpgid(0, 0);
}
printf("child pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)getpgid(0));
assert(kill(getppid(), SIGUSR1) == 0);
while (1);
exit(EXIT_SUCCESS);
}
/* Wait until the child sends a SIGUSR1. */
pause();
pgid = getpgid(0);
printf("parent pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)pgid);
/* man kill explains that negative first argument means to send a signal to a process group. */
kill(-pgid, SIGINT);
while (1);
}
Компилировать с помощью:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -Wpedantic -o setpgid setpgid.c
Запуск безsetpgid
Без каких-либо аргументов CLI setpgid
не выполняется:
./setpgid
Возможный исход:
child pid, pgid = 28250, 28249
parent pid, pgid = 28249, 28249
sigint parent
sigint child
и программа зависает.
Как мы видим, pgid обоих процессов один и тот же, так как он наследуется через fork
.
Затем всякий раз, когда вы нажимаете:
Ctrl + C
Повторно выводится:
sigint parent
sigint child
Это показывает, как:
kill(-pgid, SIGINT)
Выйти из программы, отправив разные сигналы обоим процессам, например. SIGQUIT с Ctrl + \
.
Выполнить сsetpgid
Если вы запускаете с аргументом, например,.:
./setpgid 1
затем дочерний элемент меняет свой pgid, и теперь каждый раз печатается только одна сигинт только от родителя:
child pid, pgid = 16470, 16470
parent pid, pgid = 16469, 16469
sigint parent
И теперь, всякий раз, когда вы нажимаете:
Ctrl + C
только родитель также получает сигнал:
sigint parent
Вы по-прежнему можете убить родителя с помощью SIGQUIT:
Ctrl + \
Однако у дочернего элемента теперь другой PGID, и он не получает этот сигнал! Это видно из:
ps aux | grep setpgid
Вам придется убить его явным образом с помощью:
kill -9 16470
Это проясняет, почему существуют сигнальные группы :, иначе мы получили бы кучу процессов, оставшихся для ручной очистки все время.
Протестировано на Ubuntu 18.04.