Библиотека сайта rus-linux.net
Исследуем процессы. Часть 2
Автор: Станислав Лапшанский,
slapsh@slapsh.pp.ru
Опубликовано: 2.7.2002
2002, Издательский дом "КОМПЬЮТЕРРА" | http://www.computerra.ru/
Журнал "СОФТЕРРА" | http://www.softerra.ru/
Этот материал Вы всегда сможете найти по его постоянному адресу:═ http://www.softerra.ru/freeos/18735/
Статья является переводом текста Dru Lavigne, опубликованного по адресу: http://www.onlamp.com/pub/a/bsd/2000/11/22/FreeBSD_Basics.html.
В первой части статьи мы узнали что представляют из себя процессы, а так же как посмотреть список запущенных процессов на вашей FreeBSD. В этой части мы узнаем как процессы общаются между собой, как вы можете что-нибудь передать процессу и зачем вам это может понадобиться.
Ход общения процессов называется межпроцессным взаимодействием. Процессы не имеют права просто передавать что-нибудь кому-нибудь. В FreeBSD существует 31 предопределенное сообщение. Эти сообщения называются сигналами. Вы можете посмотреть список сигналов набрав:
kill -l HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM URG STOP TSTP CONT CHLD TTIN TTOU IO XCPU XFSZ VTALRM PROF WINCH INFO USR1 USR2
Каждый сигнал имеет номер, и в этом списке они перечислены в порядку номеров. Таким образом HUP=1, INT=2 и т.д. Процесс может посылать эти сигналы другим процессам. Пользователи могут тоже самое.
Название сигнала | # | Действие по умолчанию | Описание |
---|---|---|---|
*HUP | 1 | уничтожить процесс | оборвалась связь с терминалом |
*INT | 2 | уничтожить процесс | прерывание программы |
*QUIT | 3 | создать дамп памяти | выход из программы |
ILL | 4 | создать дамп памяти | запрещенная инструкция |
TRAP | 5 | создать дамп памяти | отладочное прерывание |
ABRT | 6 | создать дамп памяти | вызов функции abort |
EMT | 7 | создать дамп памяти | была выполнена эмулируемая инструкция |
FPE | 8 | создать дамп памяти | исключение при операциях с плавающей точкой |
*KILL | 9 | уничтожить процесс | убить программу |
BUS | 10 | создать дамп памяти | ошибка на шине |
SEGV | 11 | создать дамп памяти | нарушение сегментации |
SYS | 12 | создать дамп памяти | вызов несуществующей системной программы |
PIPE | 13 | уничтожить процесс | запись в канал при отсутствии чтения |
ALRM | 14 | уничтожить процесс | истек таймер реального времени |
*TERM | 15 | уничтожить процесс | программный сигнал на уничтожение процесса |
URG | 16 | игнорирование сигнала | неотложное условие |
*STOP | 17 | остановить процесс | останов (не может игнорироваться) |
*TSTP | 18 | остановить процесс | сигнал стоп с клавиатуры |
CONT | 19 | игнорирование сигнала | продолжить после останова |
CHLD | 20 | игнорирование сигнала | статус порожденного процесса изменился |
TTIN | 21 | остановить процесс | попытка фонового чтения |
TTOU | 22 | остановить процесс | попытка фоновой записи |
IO | 23 | игнорирование сигнала | ввод/вывод возможен |
XCPU | 24 | уничтожить процесс | исчерпан лимит процессорного времени |
XFSZ | 25 | уничтожить процесс | исчерпан лимит на размер файла |
VTALRM | 26 | уничтожить процесс | сигнал от виртуального таймера |
PROF | 27 | уничтожить процесс | сигнал от таймера профайлера |
WINCH | 28 | игнорирование сигнала | изменение размеров окна |
INFO | 29 | игнорирование сигнала | запрос статуса с клавиатуры |
USR1 | 30 | уничтожить процесс | Определенный пользователем сигнал 1 |
USR2 | 31 | уничтожить процесс | Определенный пользователем сигнал 2 |
Некоторые из сигналов использовались пользователями столь часто, что получили свои клавиатурные сокращения. Для просмотра этих сокращений посмотрите четыре последние строки вывода команды stty -e:
stty -e discard dsusp eof eol eol2 erase intr kill lnext ^O ^Y ^D <undef> <undef> ^H ^C ^U ^V min quit reprint start status stop susp time werase 1 ^\ ^R ^Q ^T ^S ^Z 0 ^W
Символ "^" означает что вы должны нажать клавишу "ctrl", а затем указанную за ним букву. Обратите внимание, что три сигнала были привязаны к управляющим последовательностям:
- ^C привязан к сигналу INT (сигнал 2)
- ^\ привязан к сигналу QUIT (сигнал 3)
- ^Z привязан к сигналу TSTP (сигнал 18, хотя здесь он называется susp)
Не путайте слово "kill" в выводе команды stty с сигналом KILL (сигнал 9). Комбинация ^U удаляет строку, а не шлет сигнал номер 9. Для того что бы в этом убедиться, напечатайте в командной оболочке длинную строку, а затем нажмите ^U.
Но как послать сигнал, который не имеет соответствующей комбинации клавиш? Используйте для этого команду kill.
whatis kill kill(1) - terminate or signal a process (уничтожение или сигнализация процессу) kill(2) - send signal to a process (послать сигнал процессу)
Есть пара способов использования команды kill. Если вы просто напечатаете:
kill PID
то по умолчанию процессу с идентификатором PID будет послан сигнал TERM. Если вы хотите послать какой-нибудь другой сигнал, то в команде укажите его название или номер:
kill -название_сигнала PID
или
kill -номер_сигнала PID
Таким образом команды
kill PID kill -TERM PID kill -15 PID
эквивалентны. Не забывайте, что в UNIX имеет значение регистр набранных команд, если вы напечатаете:
kill -term PID
то получите следующее сообщение об ошибке:
term: Unknown signal; kill -l lists all signals.
Итак теперь мы знаем о каждом из 31 возможных сообщений, а так же можем посылать их различным процессам. Давайте рассмотрим причины, по которым вам может потребоваться послать процессу сигнал. Когда вы прорабатываете какой-нибудь вопрос используя FreeBSD Handbook или другое руководство, их авторы часто обучают как и что менять в тех или иных конфигурационных файлах, а затем говорят вам о необходимости послать сигнал HUP. Дело в том, что большинство процессов прочитывают свои конфигурационные файлы только при первоначальном запуске. Сигнал HUP говорит процессу, что он должен прекратить выполнение. После того как процесс перезапустится, он перечитает конфигурационные файлы и внесенные в них изменения вступят в силу. Аналогичным образом, когда вы выходите командой logout из терминала, сигнал HUP рассылается всем процессам, которые были запущены на этом терминале. Это значит, что все процессы которые выполнялись на этом терминале будут остановлены.
Иногда вы можете запустить процесс и захотеть его остановки, до того, как он завершится в штатном режиме. Например в приливе вдохновения вы можете решить, что вам необходимо посмотреть имена всех файлов в вашей системе. Это можно сделать написав следующее:
find / -print | more
Однако, вероятнее всего вы быстро утомитесь нажимать пробел и поймете, что на самом деле вам вовсе не хочется в данный момент просматривать список всех ваших файлов. Другими словами вам захочется подать прерывающий сигнал. Один из путей сделать это, нажать на терминале "Ctrl+C":
^C
То, что на вашем терминале появится приглашение интерпретатора команд, свидетельствует о том, что посланный вами сигнал INT сработал.
Опять выполните ту же самую команду find, но в этот раз пошлите сигнал 3, нажав на клавиатуре "Ctrl+\":
^
Теперь, перед тем, как вы получите приглашение интерпретатора команд, вы увидите следующее сообщение:
Quit (core dumped) Выход (сохранен "посмертный" дамп памяти)
Если вы воспользуйтесь комбинацией Alt+F1, что бы посмотреть сообщения системной консоли, там вы увидите сообщение примерно следующего содержания:
Nov 19 13:50:09 genisis /kernel: pid 806 (find), uid 1001: exited on signal 3 Nov 19 13:50:09 genisis /kernel: pid 807 (more), uid 1001: exited on signal 3 (core dumped)
Если теперь вы вернетесь на предыдущий терминал и посмотрите список файлов в каталоге, среди прочего вы обязательно найдете файл more.core. Обычно вам никогда не потребуется посылать процессу сигнал номер 3, если конечно вы не программист, который знает как использовать отладчик ядра. Я включил этот пример в статью для того что бы показать разницу между сигналами 2 и 3. Удаляйте core-файлы без опаски.
Межпроцессное взаимодействие (в оригинале используется термин "межпроцессные коммуникации" - прим. переводчика) практически ни чем не отличается от любых других видов коммуникаций: вы или какой-нибудь процесс можете послать сигнал в надежде на определенный результат, однако процесс получающий этот сигнал распоряжается с ним "на свое усмотрение". Помните, что процессы - это всего лишь запущенные программы. Большинство программ используют процедуры "обработки сигналов", применяемые для того, что бы решать что и как надо делать с поступившими сигналами в данный момент времени. Обычно, если вы посылаете процессу какой-нибудь из сигналов останавливающих выполнение программы, процедура обработки сигналов этого процесса пытается корректно закрыть все используемые программой файлы для предотвращения потери данных после останова. В некоторых случаях обработчик сигналов может просто проигнорировать поступивший процессу сигнал и отклонить запрос на останов программы (обычно так поступают зависшие программы, которые на момент поступления сигнала уже не работают должным образом - прим. переводчика).
Однако (к счастью - прим. переводчика), некоторые сигналы не могут быть проигнорированы программой. Это например девятый и семнадцатый сигналы. Представим, что вы хотите остановить процесс который вы некоторое время назад запустили. Воспользовавшись связкой команды ps и grep, вы узнали PID процесса, а затем при помощи команды kill послали ему сигнал TERM, а затем решили проверить остановлен ли процесс повторив команду ps:
ps | grep processname kill PID ps | grep processname
Однако при повторе команды ps вы опять обнаружили этот процесс в списке, а это значит, что по каким-то причинам сигнал TERM был проигнорирован. Любая из этих двух команд исправит ситуацию:
kill -9 PIDили kill -KILL PID
Если вы теперь повторите команду ps, то вы должны будете получить пустой список, что свидетельствует об успешном останове процесса.
Вы можете спросить: "Почему бы всегда не посылать процессам сигнал 9, если он не может быть игнорирован?". Дело в том, что сигнал 9 на самом деле просто "убивает" процесс, не давая ему времени на корректное сохранение всех обработанных данных, что означает, что при применении сигнала 9 могут быть потеряны данные (никогда не применяйте сигнал номер 9 без крайней на то необходимости - прим. переводчика). Намного лучше попробовать для начала послать процессу какой-нибудь другой сигнал останова, а сигнал номер 9 иметь "про запас" для процессов упрямо игнорирующих другие сигналы. Не забывайте так же, что если вы работаете от имени обычного пользователя, то вы сможете посылать сигналы только процессам, владельцем которых являетесь. Суперпользователь root может посылать сигналы любым процессам.
Может возникнуть ситуация, когда вам захочется остановить все принадлежащие вам процессы. Результаты этого действия будут отличаться в зависимости от того находитесь ли вы в системе как обычный или как суперпользователь.
Продемонстрируем это. Войдите в систему на другом терминале и введите команду ps:
ps PID TT STAT TIME COMMAND 316 v0 Ss 0:00.39 -csh (csh) 957 v0 R+ 0:00.00 ps 317 v1 Is+ 0:00.20 -csh (csh) 915 v2 Is 0:00.12 -csh (csh) 941 v2 I+ 0:00.09 lynx 942 v2 Z+ 0:00.00 (lynx) 913 v3 Is 0:00.12 -csh (csh) 946 v3 I+ 0:00.01 /bin/sh /usr/X11R6/bin/startx 951 v3 I+ 0:00.04 xinit /home/genisis/.xinitrc -- 955 v3 S 0:03.00 xfce
В этом примере я вошел в систему с терминалов 0, 1, 2, 3. Я запустил команду ps с консоли (терминал 0 по совместительству выполняет роль системной консоли - прим. переводчика), на первом терминале запущена оболочка командного процессора, на втором - запущен браузер lynx и на третьем у меня запущен сеанс X Window. И так я являюсь владельцем 10 процессов. Если в команде kill я воспользуюсь идентификатором процесса (PID) равным -1, я отправлю указанный в команде сигнал всем принадлежащим мне процессам. так попробуем послать сигнал TERM таким образом:
kill -1
А теперь проверим результаты, воспользовавшись командой ps:
ps PID TT STAT TIME COMMAND 316 v0 Ss 0:00.41 -csh (csh) 969 v0 R+ 0:00.00 ps 317 v1 Ss+ 0:00.21 -csh (csh) 915 v2 Is+ 0:00.12 -csh (csh) 913 v3 Is+ 0:00.12 -csh (csh)
Обратите внимание - мы остановили шесть процессов, однако четыре оставшиеся проигнорировали сигнал TERM. Давайте будем более агрессивными:
kill -KILL -1 ps PID TT STAT TIME COMMAND 317 v1 Ss 0:00.22 -csh (csh) 995 v1 R+ 0:00.00 ps
Если вы "пройдетесь" по тем четырем терминалам, на которых выполнялись ваши программы, то на трех из них вы увидите приглашение войти в систему. Последняя команда kill уничтожила все процессы, за исключением своего родительского процесса, т.е. командного интерпретатора C shell, в котором вы набрали команду kill (так произошло потому, что структура процессов в UNIX древовидна и каждый процесс должен иметь своего родителя - прим. переводчика).
Обратите внимание, что если вы допустите ошибку при наборе команды и напишете:
kill 1
вместо
kill -1
то вы получите сообщение об ошибке:
1: Operation not permitted 1: Действие запрещено
Дело в том, что -1 это специальный идентификатор процесса, который обозначает "все процессы", а 1 это идентификатор процесса с именем init. Только суперпользователь может останавливать процесс init. К тому же суперпользователь должен останавливать процесс init только при условии того, что он знает что делает.
Теперь давайте поглядим что произойдет, если мы повторим то же упражнение, но только от имени суперпользователя. Для начала на моем тестовом компьютере (где выполняются следующие программы: apache, mysql, squid, nfs и т.п.) я выполню команду ps:
ps -acux USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND genisis 1050 0.0 0.2 428 244 v0 R+ 4:08PM 0:00.00 ps root 1 0.0 0.2 532 304 ?? ILs 5:10AM 0:00.04 init root 2 0.0 0.0 0 0 ?? DL 5:10AM 0:00.03 pagedaemon root 3 0.0 0.0 0 0 ?? DL 5:10AM 0:00.00 vmdaemon root 4 0.0 0.0 0 0 ?? DL 5:10AM 0:00.04 bufdaemon root 5 0.0 0.0 0 0 ?? DL 5:10AM 0:02.62 syncer root 27 0.0 2.0 70780 2540 ?? ILs 5:10AM 0:00.08 mount_mfs root 30 0.0 0.1 208 92 ?? Is 5:10AM 0:00.00 adjkerntz root 110 0.0 0.3 536 368 ?? Ss 10:10AM 0:00.22 dhclient root 163 0.0 0.5 904 608 ?? Ss 10:10AM 0:00.19 syslogd daemon 166 0.0 0.4 916 556 ?? Is 10:10AM 0:00.01 portmap root 171 0.0 0.3 504 320 ?? Is 10:10AM 0:00.00 mountd root 173 0.0 0.1 360 172 ?? Is 10:10AM 0:00.01 nfsd root 175 0.0 0.1 352 164 ?? I 10:10AM 0:00.00 nfsd root 176 0.0 0.1 352 164 ?? I 10:10AM 0:00.00 nfsd root 177 0.0 0.1 352 164 ?? I 10:10AM 0:00.00 nfsd root 178 0.0 0.1 352 164 ?? I 10:10AM 0:00.00 nfsd root 181 0.0 0.5 263052 576 ?? Is 10:10AM 0:00.00 rpc.statd root 197 0.0 0.6 1028 764 ?? Is 10:10AM 0:00.02 inetd root 199 0.0 0.6 956 700 ?? Ss 10:10AM 0:00.19 cron root 202 0.0 1.0 1424 1216 ?? Is 10:10AM 0:00.20 sendmail root 227 0.0 0.4 876 488 ?? Is 10:10AM 0:00.00 moused root 261 0.0 1.4 2068 1704 ?? Ss 10:10AM 0:00.98 httpd root 275 0.0 0.4 620 448 con- I+ 10:10AM 0:00.02 sh root 293 0.0 0.4 624 452 con- I+ 10:10AM 0:00.01 sh mysql 303 0.0 1.4 10896 1796 con- S+ 10:10AM 0:00.43 mysqld nobody 305 0.0 4.7 6580 5928 con- S+ 10:10AM 0:05.42 squid nobody 308 0.0 1.4 2092 1704 ?? I 10:10AM 0:00.00 httpd nobody 309 0.0 1.4 2092 1704 ?? I 10:10AM 0:00.00 httpd nobody 310 0.0 1.4 2092 1704 ?? I 10:10AM 0:00.00 httpd nobody 311 0.0 1.4 2092 1704 ?? I 10:10AM 0:00.00 httpd nobody 312 0.0 1.4 2092 1704 ?? I 10:10AM 0:00.00 httpd genisis 317 0.0 0.8 1336 960 v1 Is+ 10:10AM 0:00.24 csh root 320 0.0 0.5 920 628 v4 Is+ 10:10AM 0:00.02 getty root 321 0.0 0.5 920 628 v5 Is+ 10:10AM 0:00.01 getty root 322 0.0 0.5 920 628 v6 Is+ 10:10AM 0:00.01 getty root 323 0.0 0.5 920 628 v7 Is+ 10:10AM 0:00.01 getty nobody 324 0.0 0.3 832 348 ?? Is 10:10AM 0:00.01 unlinkd root 992 0.0 0.5 920 628 v2 Is+ 3:46PM 0:00.01 getty root 993 0.0 0.5 920 628 v3 Is+ 3:46PM 0:00.01 getty genisis 994 0.0 0.8 1336 956 v0 Ss 3:46PM 0:00.14 csh root 0 0.0 0.0 0 0 ?? DLs 5:10AM 0:00.02 swapper
Теперь я пошлю сигнал KILL специальному идентификатору -1 от имени суперпользователя:
$ su Password: # kill -9 -1
Эта команда произвела на меня большее впечатление чем предыдущая, поскольку я был выкинут из командного интерпретатора в котором только что набрал команду kill. После того, как я опять вошел в систему, я определил масштаб разрушений следующим образом:
ps -acux USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND genisis 1070 0.0 0.2 396 244 v0 R+ 4:11PM 0:00.00 ps root 1 0.0 0.2 532 304 ?? ILs 5:10AM 0:00.05 init root 2 0.0 0.0 0 0 ?? DL 5:10AM 0:00.03 pagedaemon root 3 0.0 0.0 0 0 ?? DL 5:10AM 0:00.00 vmdaemon root 4 0.0 0.0 0 0 ?? DL 5:10AM 0:00.05 bufdaemon root 5 0.0 0.0 0 0 ?? DL 5:10AM 0:02.65 syncer root 1059 0.0 0.5 920 628 v3 Is+ 4:10PM 0:00.01 getty root 1060 0.0 0.5 920 628 v2 Is+ 4:10PM 0:00.01 getty root 1061 0.0 0.5 920 628 v7 Is+ 4:10PM 0:00.01 getty root 1062 0.0 0.5 920 628 v6 Is+ 4:10PM 0:00.01 getty root 1063 0.0 0.5 920 628 v5 Is+ 4:10PM 0:00.01 getty genisis 1064 0.0 0.8 1336 956 v0 Ss 4:10PM 0:00.12 csh root 1065 0.0 0.5 920 628 v4 Is+ 4:10PM 0:00.01 getty root 1066 0.0 0.5 920 628 v1 Is+ 4:10PM 0:00.01 getty root 0 0.0 0.0 0 0 ?? DLs 5:10AM 0:00.02 swapper
Когда суперпользователь посылает сигнал идентификатору -1, он рассылается всем процессам за исключением системных. Если этим сигналом будет KILL, то вы наслушаетесь жалоб от простых пользователей, у которых будут потеряны все открытые ими, но не сохраненные файлы данных.
Это является одной из причин, по которой только суперпользователь может выполнять команды reboot и halt. Когда одна из этих команд запускается на выполнение, то всем процессам рассылается сигнал TERM, для того что бы дать им шанс для сохранения данных, поскольку за сигналом TERM, через некоторое время, следует сигнал KILL, который посылается для того что бы гарантированно уничтожить все процессы.
В следующей статье я продолжу эту тему, заострив внимание на процессах init и getty.