IgorKa — Информационный ресурс
Немного обо всем и все о немногом, или практический опыт системного администратора.
Пн | Вт | Ср | Чт | Пт | Сб | Вс |
---|---|---|---|---|---|---|
« Окт | Дек » | |||||
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
Лекция №13 Сигналы в Linux
Прошлая лекция была посвящена процессам в Linux. Сегодня мы поговорим о взаимодействии процессом между собой, а также о том как мы можем воздействовать на процессы. Сначала посмотрим как процессы могу взаимодействовать между собой. Мы уже писали в командной строке конструкции подобные этой: less /etc/group | grep user . В этом примере процесс less взаимодействует с процессом grep посредством механизма, который называется неименований канал или пайп (pipe — канал). Мы не будем вдаваться в подробности, просто запомните, что посредством символа |, который можно в данном случае называть “пайпом” информация (результат выполнения) процесса less идет на вход другого процесса — grep. Таким образом один процесс передал информацию другому процессу.
Еще один способ общения процессов — это именованные каналы. Изучение именованный каналов не входит в этот курс, но на практическом примере расскажу, что это. Именованный канал можно создать командой mkfifo:
igor@adm-ubuntu:
/linux$ mkfifo my_pipe
igor@adm-ubuntu:
/linux$ ls -l | grep my_pipe
prw-r–r– 1 igor igor 0 2009-11-09 17:59 my_pipe
Теперь в одной консоли выполните команду:
/linux$ echo Hello > my_pipe
Как видите команда не завершает свою работу, а ждет. Зарегистрируйтесь еще в одной консоли и выполните команду:
/linux$ cat my_pipe
Hello
Если вернуться на первую консоль, то вы увидите, что команда echo завершила свою работу. Таким образом через именованный канал my_pipe команда (процесс) echo передала информацию (слово Hello) процессу cat, который ее принял и вывел на экран.
Давайте теперь рассмотрим основной способ “общения” процессов — сигналы. Один процесс при помощи ядра может передать другому процессу специальное числовое значение сигнала. Процесс вызывает функцию передачи сигнала и передает необходимую информацию (код сигнала, PID процесса) ядру. Ядро передает сигнал процессу получателю и отслеживает как этот сигнал обрабатывается. Сигналы обозначаются цифрами или мнемоническими обозначениями. Перечень сигналов можно вывести командой kill -l.
Мнемонические имена которые вы видите (SIGTERM, SIGINT, SIGKILL) начинаются с приставки SIG. Имена в этом виде используются в языках программирования таких как С. В интерпретаторе bash используются или числа или мнемонические имена, но без приставки SIG — TERM, INT, KILL.
Часть сигналов (INT, TERM) являются перехватываемыми. Это означает, что процесс при получении такого сигнала должен перейти на специальную подпрограмму, которая занимается обработкой сигнала. Если подпрограммы обработки нет (а ее написанием занимаются разработчики программы, которая выполняется в контексте процесса), то управление передается ядру, которое выполняет действия по умолчанию, описанные для каждого сигнала.
Часть сигналов являются такими которые можно заблокировать. Например, один процесс посылает сигнал TERM другому процессу, а он в свою очередь не закончил операции ввода/вывода. В таком случае второй процесс может заблокировать полученный сигнал (опять таки в обработчике сигнала) до момента выполнения необходимой операции ввода/вывода. Сигнал TERM — это сигнал корректного завершения работы процесса. Обработчик этого сигнала, должен выполнить все необходимые действия для правильного завершения работы.
И есть третья группа сигналов, которые не блокируются и для которых не нужны обработчики. Примером такого сигнала является сигнал KILL. Этот сигнал уничтожает процесс, так как для него нет обработчиков в процессах, то он будет обрабатываться ядром по умолчанию и так как он не блокируемый, то действия будут выполнятся немедленно.
Пока мы говорили о том как процессы “общаются” между собой с помощью сигналов. Но мы (пользователи) также можем посылать сигналы процессам. Например, комбинация клавиш Ctrl+C посылает процессу сигнал INT, который прерывает выполнение процесса. Если вы наберете в терминале команду sleep 100, то команда не вернет управление терминалу пока не завершится. Прервать выполнение этой команды можно нажав комбинацию клавиш Ctrl+C.
В чем же отличия между похожими сигналами INT, TERM, KILL (а также QUIT и HUP)? Несмотря на похожесть отличия есть:
Сигнал KILL не блокируется и не перехватывается и ведет к немедленному завершению процесса.
Сигнал INT в отличии от KILL является блокируемым сигналом и перехватываемым.
Сигнал TERM также является перехватываемым и блокируемым и предназначен для корректного (предпочтительного) завершения работы процесса.
Сигнал QUIT — похож на TERM, но позволяет сохранить дамп памяти.
Сигнал HUP — сейчас этот сигнал чаще всего интерпретируется процессами как “прочесть конфигурационные файлы”.
Рассмотрим два сигнала: STOP и CONT. Сигнал STOP останавливает процесс, то есть процесс переходит в состояние “остановленный“. В таком состоянии процесс будет до тех пор пока снова не получит сигнал. Если будет получен сигнал CONT, то процесс возобновит свою работу с того момента как он был остановлен. Практический пример:
Наберите в терминале команду sleep 1000 &.
Затем проверьте, что процесс находится в состоянии ожидания, о чем нам говорит буква S в столбце STAT:
$ ps x | grep [s]leep
PID TTY STAT TIME COMMAND
6301 pts/1 S 0:00 sleep 1000
Теперь пошлем процессу сигнал STOP. Для этого используем команду kill (kill -название процесса PID процесса):
$ kill -STOP 6301
[1]+ Stopped sleep 1000
Проверяем статус процесса:
igor@ubuntu:
$ ps x | grep [s]leep
PID TTY STAT TIME COMMAND
6301 pts/1 T 0:00 sleep 1000
Видим, что процесс действительно находится в состоянии “остановленный” (символ T в столбце STAT).
Теперь отправим процессу сигнал продолжения работы (CONT) и проверим состояние:
igor@ubuntu:
$ kill -CONT 6301
igor@ubuntu:
$ ps x | grep [s]leep
PID TTY STAT TIME COMMAND
6301 pts/1 S 0:00 sleep 1000
Если необходимо корректно завершить процесс, то необходимо послать ему сигнал TERM:
igor@ubuntu:
$ kill -TERM 6301
igor@ubuntu:
$ ps x | grep [s]leep
[1]+ Terminated sleep 1000
igor@ubuntu:
$ ps x | grep [s]leep
Если сразу же после посылки сигнала TERM выполнить команду ps x | grep [s]leep, то можно успеть увидеть сообщение о том, что процесс завершает работу, в при следующей попытке вывести информацию о нашем процессе sleep мы уже ничего не увидим — процесс прекратил существование. Команда kill без указания сигнала, по умолчанию передает процессу именно сигнал TERM. Поэтому можно было написать просто kill 6301.
Если необходимо срочно завершить процесс, или процесс не завершается по сигналу TERM, то тогда необходимо послать процессу сигнал KILL:
igor@ubuntu:
$ sleep 1000 &
[1] 6348
igor@ubuntu:
$ kill -KILL 6348
igor@ubuntu:
$ ps x | grep [s]leep
[1]+ Killed sleep 1000
Если необходимо послать один и тот же сигнал нескольким процессам, то можно перечислить их через пробел: kill -TERM 2345 3456 4567.
Команда kill довольно ограничена в возможностях и не позволяет выполнять более сложные действия. Поэтому рассмотрим еще одну команду — killall. Основное преимущество этой команды, то что она умеет посылать сигналы всем процессам с одинаковым именем или всем процессам одного пользователя. Запустите несколько раз подряд команду sleep 1000 &:
$ ps x | grep [s]leep
6460 pts/1 S 0:00 sleep 1000
6461 pts/1 S 0:00 sleep 1000
6462 pts/1 S 0:00 sleep 1000
6463 pts/1 S 0:00 sleep 1000
6464 pts/1 S 0:00 sleep 1000
6465 pts/1 S 0:00 sleep 1000
6466 pts/1 S 0:00 sleep 1000
Теперь, чтобы завершить все процессы с именем sleep, достаточно набрать команду killall sleep:
igor@ubuntu:
$ killall sleep
[1] Terminated sleep 1000
[2] Terminated sleep 1000
[3] Terminated sleep 1000
[4] Terminated sleep 1000
[6]- Terminated sleep 1000
[7]+ Terminated sleep 1000
[5]+ Terminated sleep 1000
Выполните команду sleep 1000 & еще несколько раз, а затем зарегистрируйтесь в другой консоли от имени другого пользователя (например, test) и также от его имени выполните команду sleep 1000 &. Теперь вернитесь в свою консоль и просмотрите процессы sleep всех пользователей:
$ ps aux | grep [s]leep
igor 6540 0.0 0.0 2952 628 pts/1 S 22:30 0:00 sleep 1000
igor 6541 0.0 0.0 2952 632 pts/1 S 22:30 0:00 sleep 1000
igor 6542 0.0 0.0 2952 628 pts/1 S 22:30 0:00 sleep 1000
test 6543 0.0 0.0 2952 632 pts/3 S 22:30 0:00 sleep 1000
test 6544 0.0 0.0 2952 628 pts/3 S 22:30 0:00 sleep 1000
test 6545 0.0 0.0 2952 628 pts/3 S 22:30 0:00 sleep 1000
test 6546 0.0 0.0 2952 632 pts/3 S 22:30 0:00 sleep 1000
Теперь для того, чтобы удалить процессы только пользователя test необходимо выполнить (от имени пользователя root) команду killall -u test:
igor@ubuntu:
$ sudo killall -u test
igor@ubuntu:
$ ps aux | grep [s]leep
igor 6540 0.0 0.0 2952 628 pts/1 S 22:30 0:00 sleep 1000
igor 6541 0.0 0.0 2952 632 pts/1 S 22:30 0:00 sleep 1000
igor 6542 0.0 0.0 2952 628 pts/1 S 22:30 0:00 sleep 1000
Команда выше удалит не только процессы sleep, но вообще все процессы пользователя test. Если необходимо удалить конкретно процессы sleep, то тогда команду нужно было записать так: killall -u test sleep.
Если запустить команду killall c ключом -i, то перед посылкой сигнала будет запрашиваться подтверждение:
$ killall -i sleep
Прибить sleep(6540) ? (y/N) n
Прибить sleep(6541) ? (y/N) n
Прибить sleep(6542) ? (y/N) n
Следующая лекция будет завершающей по теме процессов и сигналов Linux. Мы поговорим о заданиях (jobs), командах jobs, fg, bg, strong>nohup, и top.
Источник
Управление процессами в Linux
Материал этой статьи ни в коем случае не претендует на свою избыточность. Более подробно о процессах вы можете прочитать в книгах, посвященных программированию под UNIX.
Процессы. Системные вызовы fork() и exec(). Нити.
Процесс в Linux (как и в UNIX) — это программа, которая выполняется в отдельном виртуальном адресном пространстве. Когда пользователь регистрируется в системе, автоматически создается процесс, в котором выполняется оболочка (shell), например, /bin/bash.
В Linux поддерживается классическая схема мультипрограммирования. Linux поддерживает параллельное (или квазипараллельного при наличии только одного процессора) выполнение процессов пользователя. Каждый процесс выполняется в собственном виртуальном адресном пространстве, т.е. процессы защищены друг от друга и крах одного процесса никак не повлияет на другие выполняющиеся процессы и на всю систему в целом. Один процесс не может прочитать что-либо из памяти (или записать в нее) другого процесса без «разрешения» на то другого процесса. Санкционированные взаимодействия между процессами допускаются системой.
Ядро предоставляет системные вызовы для создания новых процессов и для управления порожденными процессами. Любая программа может начать выполняться только если другой процесс ее запустит или произойдет какое-то прерывание (например, прерывание внешнего устройства).
В связи с развитием SMP (Symmetric Multiprocessor Architectures) в ядро Linux был внедрен механизм нитей или потоков управления (threads). Нить — это процесс, который выполняется в виртуальной памяти, используемой вместе с другими нитями процесса, который обладает отдельной виртуальной памятью.
Если интерпретатору (shell) встречается команда, соответствующая выполняемому файлу, интерпретатор выполняет ее, начиная с точки входа (entry point). Для С-программ entry point — это функция main. Запущенная программа тоже может создать процесс, т.е. запустить какую-то программу и ее выполнение тоже начнется с функции main.
Для создания процессов используются два системных вызова: fork() и exec. fork() создает новое адресное пространство, которое полностью идентично адресному пространству основного процесса. После выполнения этого системного вызова мы получаем два абсолютно одинаковых процесса — основной и порожденный. Функция fork() возвращает 0 в порожденном процессе и PID (Process ID — идентификатор порожденного процесса) — в основном. PID — это целое число.
Теперь, когда мы уже создали процесс, мы можем запустить программу с помощью вызова exec. Параметрами функции exec является имя выполняемого файла и, если нужно, параметры, которые будут переданы этой программе. В адресное пространство порожденного с помощью fork() процесса будет загружена новая программа и ее выполнение начнется с точки входа (адрес функции main).
В качестве примера рассмотрим этот фрагмент программы
if (fork()==0) wait(0);
else execl(«ls», «ls», 0); /* порожденный процесс */
Теперь рассмотрим более подробно, что же делается при выполнении вызова fork():
- Выделяется память для описателя нового процесса в таблице процессов
- Назначается идентификатор процесса PID
- Создается логическая копия процесса, который выполняет fork() — полное копирование содержимого виртуальной памяти родительского процесса, копирование составляющих ядерного статического и динамического контекстов процесса-предка
- Увеличиваются счетчики открытия файлов (порожденный процесс наследует все открытые файлы родительского процесса).
- Возвращается PID в точку возврата из системного вызова в родительском процессе и 0 — в процессе-потомке.
Общая схема управления процессами
Каждый процесс может порождать полностью идентичный процесс с помощью fork(). Родительский процесс может дожидаться окончания выполнения всех своих процессов-потомков с помощью системного вызова wait.
В любой момент времени процесс может изменить содержимое своего образа памяти, используя одну из разновидностей вызова exec. Каждый процесс реагирует на сигналы и, естественно, может установить собственную реакцию на сигналы, производимые операционной системой. Приоритет процесса может быть изменен с помощью системного вызова nice.
Сигнал — способ информирования процесса ядром о происшествии какого-то события. Если возникает несколько однотипных событий, процессу будет подан только один сигнал. Сигнал означает, что произошло событие, но ядро не сообщает сколько таких событий произошло.
Примеры сигналов:
- окончание порожденного процесса (например, из-за системного вызова exit (см. ниже))
- возникновение исключительной ситуации
- сигналы, поступающие от пользователя при нажатии определенных клавиш.
Установить реакцию на поступление сигнала можно с помощью системного вызова signal
func = signal(snum, function);
snum — номер сигнала, а function — адрес функции, которая должна быть выполнена при поступлении указанного сигнала. Возвращаемое значение — адрес функции, которая будет реагировать на поступление сигнала. Вместо function можно указать ноль или единицу. Если был указан ноль, то при поступлении сигнала snum выполнение процесса будет прервано аналогично вызову exit. Если указать единицу, данный сигнал будет проигнорирован, но это возможно не для всех процессов.
С помощью системного вызова kill можно сгенерировать сигналы и передать их другим процессам.
kill(pid, snum);
где pid — идентификатор процесса, а snum — номер сигнала, который будет передан процессу. Обычно kill используется для того, чтобы принудительно завершить («убить») процесс.
Pid состоит из идентификатора группы процессов и идентификатора процесса в группе. Если вместо pid указать нуль, то сигнал snum будет направлен всем процессам, относящимся к данной группе (понятие группы процессов аналогично группе пользователей). В одну группу включаются процессы, имеющие общего предка, идентификатор группы процесса можно изменить с помощью системного вызова setpgrp. Если вместо pid указать -1, ядро передаст сигнал всем процессам, идентификатор пользователя которых равен идентификатору текущего выполнения процесса, который посылает сигнал.
Таблица 1. Номера сигналов
Номер | Название | Описание |
01 | SIGHUP | Освобождение линии (hangup). |
02 | SIGINT | Прерывание (interrupt). |
03 | SIGQUIT | Выход (quit). |
04 | SIGILL | Некорректная команда (illegal instruction). Не переустанавливается при перехвате. |
05 | SIGTRAP | Трассировочное прерывание (trace trap). Не переустанавливается при перехвате. |
06 | SIGIOT или SIGABRT | Машинная команда IOT. |
07 | SIGEMT | Машинная команда EMT. |
08 | SIGFPE | Исключительная ситуация при выполнении операции с вещественными числами (floating-point exception) |
09 | SIGKILL | Уничтожение процесса (kill). Не перехватывается и не игнорируется. |
10 | SIGBUS | Ошибка шины (bus error). |
11 | SIGSEGV | Некорректное обращение к сегменту памяти (segmentation violation). |
12 | SIGSYS | Некорректный параметр системного вызова (bad argument to system call). |
13 | SIGPIPE | Запись в канал, из которого некому читать (write on a pipe with no one to read it). |
14 | SIGALRM | Будильник |
15 | SIGTERM | Программный сигнал завершения |
16 | SIGUSR1 | Определяемый пользователем сигнал 1 |
17 | SIGUSR2 | Определяемый пользователем сигнал 2 |
18 | SIGCLD | Завершение порожденного процесса (death of a child). |
19 | SIGPWR | Ошибка питания |
22 | Регистрация выборочного события |
Сигналы (точнее их номера) описаны в файле singnal.h
Для нормального завершение процесса используется вызов
exit(status);
где status — это целое число, возвращаемое процессу-предку для его информирования о причинах завершения процесса-потомка.
Вызов exit может задаваться в любой точке программы, но может быть и неявным, например при выходе из функции main (при программировании на C) оператор return 0 будет воспринят как системный вызов exit(0);
Перенаправление ввода/вывода
Практически все операционные системы обладают механизмом перенаправления ввода/вывода. Linux не является исключением из этого правила. Обычно программы вводят текстовые данные с консоли (терминала) и выводят данные на консоль. При вводе под консолью подразумевается клавиатура, а при выводе — дисплей терминала. Клавиатура и дисплей — это, соответственно, стандартный ввод и вывод (stdin и stdout). Любой ввод/вывод можно интерпретировать как ввод из некоторого файла и вывод в файл. Работа с файлами производится через их дескрипторы. Для организации ввода/вывода в UNIX используются три файла: stdin (дескриптор 1), stdout (2) и stderr(3).
Символ > используется для перенаправления стандартного вывода в файл.
Пример:
$ cat > newfile.txt Стандартный ввод команды cat будет перенаправлен в файл newfile.txt, который будет создан после выполнения этой команды. Если файл с этим именем уже существует, то он будет перезаписан. Нажатие Ctrl + D остановит перенаправление и прерывает выполнение команды cat.
Символ < используется для переназначения стандартного ввода команды. Например, при выполнении команды cat > используется для присоединения данных в конец файла (append) стандартного вывода команды. Например, в отличие от случая с символом >, выполнение команды cat >> newfile.txt не перезапишет файл в случае его существования, а добавит данные в его конец.
Символ | используется для перенаправления стандартного вывода одной программы на стандартный ввод другой. Напрмер, ps -ax | grep httpd.
Команды для управления процессами
Предназначена для вывода информации о выполняемых процессах. Данная команда имеет много параметров, о которых вы можете прочитать в руководстве (man ps). Здесь я опишу лишь наиболее часто используемые мной:
Параметр | Описание |
-a | отобразить все процессы, связанных с терминалом (отображаются процессы всех пользователей) |
-e | отобразить все процессы |
-t список терминалов | отобразить процессы, связанные с терминалами |
-u идентификаторы пользователей | отобразить процессы, связанные с данными идентификаторыми |
-g идентификаторы групп | отобразить процессы, связанные с данными идентификаторыми групп |
-x | отобразить все процессы, не связанные с терминалом |
Например, после ввода команды ps -a вы увидите примерно следующее:
Для вывода информации о конкретном процессе мы можем воспользоваться командой:
В приведенном выше примере используется перенаправление ввода вывода между программами ps и grep, и как результат получаем информацию обо всех процессах содержащих в строке запуска «httpd». Данную команду (ps -ax | grep httpd) я написал только лишь в демонстрационных целях — гораздо проще использовать параметр -С программы ps вместо перенаправления ввода вывода и параметр -e вместо -ax.
Предназначена для вывода информации о процессах в реальном времени. Процессы сортируются по максимальному занимаемому процессорному времени, но вы можете изменить порядок сортировки (см. man top). Программа также сообщает о свободных системных ресурсах.
Просмотреть информацию об оперативной памяти вы можете с помощью команды free, а о дисковой — df. Информация о зарегистрированных в системе пользователей доступна по команде w.
Изменение приоритета процесса — команда nice
nice [-коэффициент понижения] команда [аргумент]
Команда nice выполняет указанную команду с пониженным приоритетом, коэффициент понижения указывается в диапазоне 1..19 (по умолчанию он равен 10). Суперпользователь может повышать приоритет команды, для этого нужно указать отрицательный коэффициент, например —10. Если указать коэффициент больше 19, то он будет рассматриваться как 19.
nohup — игнорирование сигналов прерывания
nohup команда [аргумент]
nohup выполняет запуск команды в режиме игнорирования сигналов. Не игнорируются только сигналы SIGHUP и SIGQUIT.
kill — принудительное завершение процесса
kill [-номер сигнала] PID
где PID — идентификатор процесса, который можно узнать с помощью команды ps.
Команды выполнения процессов в фоновом режиме — jobs, fg, bg
Команда jobs выводит список процессов, которые выполняются в фоновом режиме, fg — переводит процесс в нормальные режим («на передний план» — foreground), а bg — в фоновый. Запустить программу в фоновом режиме можно с помощью конструкции &
Источник