1 (08.05.2014 20:51:34 отредактировано drBatty)

В Linux всё файлы.

Точнее почти всё. Есть ещё процессы. Процессом называют код в памяти, который выполняется процессором. В каждый момент времени может выполняться только один процесс(на одном физическом ядре CPU), но Linux умеет одновременно выполнять несколько процессов даже на одноядерном CPU. Для этого один процесс останавливается, и запускается другой.

Поэтому нужен какой то механизм обмена сообщениями между работающими процессами. Часто используются временные файлы, но это иногда не очень удобно. Потому есть ещё и сигналы.

Сигналом называется сообщение, которое один процесс может отправить другому. Обмен сигналами обеспечивается ядром GNU/Linux. В текущей реализации сигнал — небольшое целое число от 1 до 64. Числа не нужно запоминать, т.к. у каждого числа есть имя. Список сигналов можно посмотреть так:

 Консоль:
$ kill -l
1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
6) SIGABRT     7) SIGBUS     af SIGFPE     9) SIGKILL    10) SIGUSR1
11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3
38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8
43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12
53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7
58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2
63) SIGRTMAX-1    64) SIGRTMAX   

Этот список вообще говоря может быть разным в разных Linux'ах.

Программы отправляют сигналы системной функцией kill(2), но сигналы можно отправлять и из терминала, командой kill(1).

Для отправки нужно обязательно знать PID (уникальный номер) процесса. Если сигнал не указать, команда kill отправляет сигнал SIGTERM, который у меня имеет номер 15. Получив такой сигнал программа аккуратно завершается, подчищая за собой ресурсы (закрывает рабочие файлы, удаляет временные, стирает с экрана свои окошки, и освобождает занятую память). Иногда этот сигнал не срабатывает. Тогда можно применить сигнал SIGINT, который эквивалентен CTRL+C (т.е. при нажатии в оболочке CTRL+C эта оболочка посылает SIGINT процессу), этот сигнал более жёсткий, нежели SIGTERM, и приложение завершается не корректно. Мусор за ним приходится чистить руками.

Особый сигнал это SIGKILL. Он НЕ доходит до приложения, а обрабатывается самим ядром. Приложение потому не может игнорировать и/или обрабатывать по своему сигнал SIGKILL.

Также есть команда killall, которая позволяет послать сигнал всем процессам с заданным именем.

Сигналы можно посылать только своим процессам, до чужих они не дойдут. Конечно root может посылать сигналы и чужим процессам.

PS: да, когда процесс завершается не как обычно(сам по себе), а по сигналу завершения(SIGINT/SIGTERM), то код возврата равен 128+сигнал. Например:

 Консоль:
drb@amilo:~$ sleep 1h
^C
drb@amilo:~$ echo $?
130

130=128+2, 2 в моей системе SIGINT, а я нажал CTRL+C.

Таким образом, родительский процесс может узнать, почему завершился дочерний процесс.

Сигналы можно обрабатывать не только в программах на C и прочих нормальных ЯП, но и в bash скриптах, для этого есть ключевое слово trap, которое позволяет назначить свой обработчик для любого сигнала(кроме SIGKILL конечно) Подробнее man bash под спойлером

+ открыть спойлер

SIGNALS
       When bash is interactive, in the absence of any traps, it ignores SIGTERM (so that kill 0 does
       not  kill an interactive shell), and SIGINT is caught and handled (so that the wait builtin is
       interruptible).  In all cases, bash ignores SIGQUIT.   If  job  control  is  in  effect,  bash
       ignores SIGTTIN, SIGTTOU, and SIGTSTP.

       Non-builtin commands run by bash have signal handlers set to the values inherited by the shell
       from its parent.  When job control is not in effect, asynchronous commands ignore  SIGINT  and
       SIGQUIT  in addition to these inherited handlers.  Commands run as a result of command substi-
       tution ignore the keyboard-generated job control signals SIGTTIN, SIGTTOU, and SIGTSTP.

       The shell exits by default upon receipt of a SIGHUP.  Before  exiting,  an  interactive  shell
       resends  the  SIGHUP to all jobs, running or stopped.  Stopped jobs are sent SIGCONT to ensure
       that they receive the SIGHUP.  To prevent the shell from sending the signal  to  a  particular
       job,  it should be removed from the jobs table with the disown builtin (see SHELL BUILTIN COM-
       MANDS below) or marked to not receive SIGHUP using disown -h.

       If the huponexit shell option has been set with shopt, bash sends a SIGHUP to all jobs when an
       interactive login shell exits.

       If  bash  is waiting for a command to complete and receives a signal for which a trap has been
       set, the trap will not be executed until the command completes.  When bash is waiting  for  an
       asynchronous command via the wait builtin, the reception of a signal for which a trap has been
       set will cause the wait builtin to return immediately with an exit status  greater  than  128,
       immediately after which the trap is executed.

Зомби.

О них я как-то забыл. Впрочем, в них нет ничего интересного, кроме названия. Как я уже писал выше, завершённый процесс отдаёт родительскому код возврата, говорящий о том, как дочерний процесс завершился. Здесь кроется противоречие: процесса уже нет, а его код ещё есть. Для разрешения этого противоречия существует механизм "зомби", процесс всё же завершается, но в таблице процессов  его запись не стирается. Там и храниться код возврата, пока его не снимет родитель системной функцией wait(2).

Что-бы родитель обратил на это внимание, ядро отсылает ему сигнал SIGCHLD. Но если родитель не обращает внимание на сигнал, и не выполняет wait(2), то зомби так и висит до завершения родителя. Убить его нельзя, т.к. процесс-зомби уже завершён. Число процессов ограничено(его можно изменить bash командой ulimit), и лишние зомби могут сделать невозможной нормальную работу с системой. К счастью, это бывает только на кривом быдлокоде, когда программист запускает процессы, но забывает отслеживать их завершение. Т.ч. это больше для программистов.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

2

drBatty пишет:

Таким образом, родительский процесс может узнать, почему завершился дочерний процесс.

Док, а "зомби"?
А вообще - спасибо за статью.