- UNIX / Linux: 3 Ways to Send Signal to Processes
- 1. Send Signal to a Process Using Kill
- 2. Send Signal to a Process from Another Process
- 3. Send Signal to a Process from Keyboard
- Send signal to process from command line
- 3 Answers 3
- Функция обработчик сигналов
- Блокирование сигналов
- sigwait
- Посыл сигнала
- Пример использования сигналов
UNIX / Linux: 3 Ways to Send Signal to Processes
Question: How do I send signal to another process? Can you explain me all available options to send signals to a process in UNIX / Linux environment? Answer: You can send various signals to processes using one of the methods explains in this article.
1. Send Signal to a Process Using Kill
Use kill command to send a signal to a process. For example, if you want to send USR1 signal to the process “a.out”, do the following.
$ ps -C a.out PID TTY TIME CMD 3699 pts/1 00:00:00 a.out $ kill -s USR1 3699
2. Send Signal to a Process from Another Process
You can use the UNIX system call kill (from a C program) to send signal from one process to another. The following C code snippet shows how to use the kill command. Kill system call takes two arguments: 1) the PID (process id) of the process that needs to be signalled 2) the signal that needs to be send to the process. Kill command returns 0 when it is successful.
3. Send Signal to a Process from Keyboard
When a process is running on the terminal, you can send signal to that process from the keyboard by using some specific combination of keys. The following are couple of examples.
- SIGINT (Ctrl + C) – You know this already. Pressing Ctrl + C kills the running foreground process. This sends the SIGINT to the process to kill it.
- You can send SIGQUIT signal to a process by pressing Ctrl + \ or Ctrl + Y
You can view the key mappings that sends specific signal to a process using the “stty -a” command as shown below.
$ stty -a | grep intr intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?;
Send signal to process from command line
@EugeneSh. I’d say a command that would run a function in the code would be perfect. I’d be abble to create a function to send the signal without a problem. I’d still want to know what kill does though
3 Answers 3
As far as I understood your question you want to signal a process by its name, not by its PID. This can easily be achieved by combining the two commands:
kill -s signal $(ps -C executable)
Does it kill the process that signals?
kill can kill. It doesn’t necessarily.
The command kill sends the specified signal to the specified processes or process groups.
That means, the kill command is used to **send any signal in general.
If it kills a process it means that it’s similar to do exit(0) , or does the process resume after the signal is sent back?
The SIGKILL signal is used to cause immediate program termination. It cannot be handled or ignored, and is therefore always fatal. It is also not possible to block this signal.
If a process receives the SIGKILL signal, it terminates immediately (no destructors called, no cleanup done). The only processes that do not terminate are uninterruptible processes.
A full list of signals available on Linux is found here.
so I can use kill without a problem, and it will not pause or kill any process, as long as I don’t use the SIGKILL signal?
However, I have 3 processes, so I think your answer proposal would send the signal to all 3 of them, not 1 specific
@AndréAlmeida kill only sends signals. There are other signals such as SIGSTOP or SIGTERM , which stop/terminate programs. You need to look each signal’s function up to be sure what it does.
Here is a concrete example I use from time to time:
I’d like to send a signal to a specific proccess from the command line. I’d like to not need to print the PID of the process I want to signal, but rather use a way that the code understands which process should be signaled.
The killall command meets those criteria, if you have it available to you. It allows you to specify the process(es) to signal based on their names. All of the following comments apply equally to the effects of delivering a signal via killall and delivering one via kill .
Other than that I’d like to understand exacly what the kill command does.
Does it kill the process that signals?
It delivers the specified signal. That’s it. Some signals it can deliver will have the effect of killing the process.
Does it kill the process where it’s called?
It delivers a signal only to the process you specify.
If it kills a process it means that it’s similar to do exit(0), or does the process resume after the signal is sent back?
Processes can handle some signals without terminating. Exactly what happens when a process receives one of those signals depends on the process. If a process does not provide a handler for a signal whose default action is to terminate the process, then the process will die, never to resume. The effect is somewhat like that of calling the exit() function or returning from main() , but the termination is abrupt, without calling exit handlers or such.
Функция обработчик сигналов
Данная функция вызывается, когда процесс (или нить) получает неблокируемый сигнал. Дефолтный обработчик завершает наш процесс (нить). Но мы можем сами определить обработчики для интересующих нас сигналов. Следует очень осторожно относится к написанию обработчика сигналов, это не просто функция, выполняющаяся по коллбеку, происходит прерывание текущего потока выполнения без какой либо подготовительной работы, таким образом глобальные объекты могут находится в неконсистентном состоянии. Автор не берется приводить свод правил, так как сам их не знает, и призывает последовать совету Kobolog (надеюсь он не против, что я ссылаюсь на него) и изучить хотя бы вот этот материал FAQ.
sighandler_t signal(int signum, sighandler_t handler);
- функция не блокирует получение других сигналов пока выполняется текущий обработчик, он будет прерван и начнет выполняться новый обработчик
- после первого получения сигнала (для которого мы установили свой обработчик), его обработчик будет сброшен на SIG_DFL
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
- sa_handler — аналогичен sighandler_t в функции signal
- sa_mask — маска сигналов который будут блокированы пока выполняется наш обработчик. + по дефолту блокируется и сам полученный сигнал
- sa_flags — позволяет задать дополнительные действия при обработке сигнала о которых лучше почитать тут
struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = hdl; sigset_t set; sigemptyset(&set); sigaddset(&set, SIGUSR1); sigaddset(&set, SIGUSR2); act.sa_mask = set; sigaction(SIGUSR1, &act, 0); sigaction(SIGUSR2, &act, 0);
Здесь мы установили наш обработчик для сигналов SIGUSR1 и SUGUSR2, а также указали, что необходимо блокировать эти же сигналы пока выполняется обработчик.
С обработчиком сигналов есть один не очень удобный момент, он устанавливается на весь процесс и все порожденные нити сразу. Мы не имеет возможность для каждой нити установить свой обработчик сигналов.
Но при этом следует понимать что когда сигнал адресуется процессу, обработчик вызывается именно для главной нити (представляющей процесс). Если же сигнал адресуется для нити, то обработчик вызывается из контекста этой нити. См пример 1.
Блокирование сигналов
Для того, чтобы заблокировать некоторый сигналы для процесса, необходимо добавить их в маску сигналов данного процесса. Для этого используется функция
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
Мы можем к уже существующей маске сигналов добавить новые сигналы (SIG_BLOCK), можем из этой маски убрать часть сигналов (SIG_UNBLOCK), а так же установить полностью нашу маску сигналов (SIG_SETMASK).
Для работы с маской сигналов внутри нити используется функция
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset);
которая позволяет сделать все тоже, но уже для каждой нити в отдельности.
Невозможно заблокировать сигналы SIGKILL или SIGSTOP при помощи этих функций. Попытки это сделать будут игнорироваться.
sigwait
Данная функция позволяет приостановить выполнении процесса (или нити) до получения нужного сигнала (или одного из маски сигналов). Особенностью этой функции является то, что при получении сигнала не будет вызвана функции обработчик сигнала. См. пример 2.
Посыл сигнала
int kill(pid_t pid, int sig); int raise(int sig);
С первой все понятно. Вторая нужна для того, чтобы послать сигнал самому себе, и по сути равносильна kill(getpid(), signal). Функция getpid() возвращает PID текущего процесса.
Для того, чтобы послать сигнал отдельной нити, используется функция
int pthread_kill(pthread_t thread, int sig);
Пример использования сигналов
Все, что я описал выше, не дает ответа на вопрос «Зачем мне использовать сигналы». Теперь я хотел бы привести реальный пример использования сигналов и где без них попросту не обойтись.
Представьте, что вы хотите читать или писать какие-то данные в какое то устройство, но это может привести к блокированию. Ну например, чтение в случае работы с сокетами. Или может быть запись в пайп. Вы можете вынести это в отдельный поток, чтобы не блокировать основную работу. Но что делать когда вам нужно завершить приложение? Как корректно прервать блокирующую операцию IO? Можно было бы задавать таймаут, но это не очень хорошее решение. Для этого есть более удобные средства: функции pselect и ppoll. Разница между ними исключительно в юзабельности, поведение у них одинаковое. В первую очередь эти функции нужны для мультиплексирования работы с IO (select/poll). Префикс ‘p’ в начале функции указывает на то, что данная функция может быть корректно прервана сигналом.
Итак, сформулируем требование:
Необходимо разработать приложение, открывающее сокет (для простоты UDP) и выполняющее в потоке операцию чтения. Данное приложение должно корректно без задержек завершаться по требованию пользователя.
Функция треда выглядит вот так
void* blocking_read(void* arg) < if(stop) < // не успели стартовать, а нас уже прикрыли ? std::cout // Блокируем сигнал SIGINT sigset_t set, orig; sigemptyset(&set); sigaddset(&set, SIGINT); sigemptyset(&orig); pthread_sigmask(SIG_BLOCK, &set, &orig); if(stop) < // пока мы устанавливали блокировку сигнала он уже произошол // возвращаем все как было и выходим std::cout // Здесь нас не могут прервать сигналом SIGINT std::cout // Мы либо считали данные, либо произошла какаято ошибка. Но мы не получали // сигнала о завершении работы и продолжаем работать "по плану" close(sockfd); pthread_exit((void *)0); >
- проверяем, что пока стартовал тред его еще не пожелали завершить
- блокируем завершающий сигнал
- проверяем, что пока блокировали, нас не пожелали завершить
- вызываем ppoll передавая в качестве последнего параметра маску сигналов по которой ждется сигнал
- после выхода из ppoll проверяем что вышли не из за сигнала о завершении
Устанавливаем наш обработчик для SIGINT, и когда нужно завершить дочерний поток шлем ему этот сигнал.
Полный листинг см. пример 3.
На мой взгляд, недостатком данного способа является то, что в случае нескольких потоков мы можем завершить их только все сразу. Нет возможности устанавливать свой обработчик сигналов для каждого треда. Таким образом, нет возможности реализовать полноценное межпоточное взаимодействие через сигналы. Linux way это не предусматривает.
PS. Исходные коды разместил на сервисе PasteBin (ссылку не даю, а то еще за рекламу посчитают).
PPS. Прошу простить за обилие ошибок. Язык, слабая моя сторона. Спасибо, всем кто помог их исправить.
Данная статья не претендует на полное (и глубокое) описание работы с сигналами и нацелена в первую очередь на тех, кто до этого момента не сталкивались с понятием «сигнал». Для более глубоко понимания работы сигналов автор призывает обратиться в более компетентные источники и ознакомиться с конструктивной критикой в комментариях.