Как стать автором
Обновить

Слежение за процессами и обработка ошибок, часть 2

Время на прочтение4 мин
Количество просмотров3.6K

Преамбула


В первой части данной статьи мы рассмотрели механизм связей между процессами и процесс распространения ошибок. Сегодя давайте рассмотрим один случай, который не был освещен в предыдущей части – постреляем по процессам сигналом kill.

Сигнал kill


Сигнал kill не перехватываемый сигнал, т.е. он всегда убивает процесс, даже, если он системный. Это используется в OTP супервизором для принудительного завершения сбойных процессов. Когда процесс получает сигнал kill, он умирает и сигналы killed рассылаются по всем его связям.

Во всех примерах будет использовать такое дерево процессов.


Рисунок 1

Давайте напишем небольшой модуль.
-module(test).
-export([start/1, proc/2]).

start(Sysproc) ->
	process_flag(trap_exit, Sysproc),
	io:format("Shell Pid: ~p~n", [self()]),
	PidA = spawn_link(test, proc, [self(), a]),
	PidB = spawn_link(test, proc, [self(), b]),
	io:format("ProcA: ~p~nProcB: ~p~n", [PidA, PidB]),	
	exit(PidB, kill).

proc(Shell, Tag) ->
	receive
		after 5000 ->			   
				Shell ! {hello_from, Tag, self()}
	end.


В функции start/1 мы создаем два процесса: A и B, телом процессов служит функция proc/2, первым аргументом которой является Pid оболочки, второй Tag:atom(), который служит для удобства восприятия при анализе от какого процесса пришло сообщение. После создания вызывается функция exit/2, которая процессу B посылает сигнал kill.

Компилируем и запускаем.

(emacs@aleksio-mobile)2> test:start(false).
Shell Pid: <0.36.0>
ProcA: <0.43.0>
ProcB: <0.44.0>
** exception exit: killed
(emacs@aleksio-mobile)3> flush().
ok
(emacs@aleksio-mobile)4> self().
<0.45.0>
(emacs@aleksio-mobile)5>


Вызываем функцию start/1 (shell в данном примере не системный процесс и сигналы выхода ловить не может). После порождения процессов видим сообщение "** exception exit: killed", которое означает, что shell был убит с причиной killed. То, что shell действительно пал смертью храбрых говорит его новый Pid <0.45.0>. Схематично весь процесс можно изобразить следующим образом.


Рисунок 2

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

-module(test).
-export([start/1, proc/3]).

start(Sysproc) ->
	process_flag(trap_exit, Sysproc),
	io:format("Shell Pid: ~p~n", [self()]),
	PidA = spawn_link(test, proc, [self(), a, false]),
	PidB = spawn_link(test, proc, [self(), b, true]),
	io:format("ProcA: ~p~nProcB: ~p~n", [PidA, PidB]),	
	exit(PidB, kill).

proc(Shell, Tag, Sysproc) ->
	process_flag(trap_exit, Sysproc),
	receive
		after 5000 ->			   
				Shell ! {hello_from, Tag, self()}
	end.


(emacs@aleksio-mobile)2> test:start(false).
Shell Pid: <0.36.0>
ProcA: <0.43.0>
ProcB: <0.44.0>
** exception exit: killed
(emacs@aleksio-mobile)3> flush().
ok
(emacs@aleksio-mobile)4> self().
<0.45.0>
(emacs@aleksio-mobile)5> 


Несмотря на то, что процесс B был сделан системным (двойная окружность на рисунке 3), он все равно умирает получив сигнал kill, после чего сигнал killed распространяется по связям и все процессы умирают.


Рисунок 3

Теперь давайте сделаем shell тоже системным.

(emacs@aleksio-mobile)2> test:start(true).
Shell Pid: <0.36.0>
ProcA: <0.43.0>
ProcB: <0.44.0>
true
(emacs@aleksio-mobile)3> flush().
Shell got {'EXIT',<0.44.0>,killed}
Shell got {hello_from,a,<0.43.0>}
Shell got {'EXIT',<0.43.0>,normal}
ok
(emacs@aleksio-mobile)4> self().
<0.36.0>
(emacs@aleksio-mobile)5> 


Видим, что процесс B был убит, shell смог перехватить сигнал выхода от него (Shell got {'EXIT',<0.44.0>,killed}), процесс A послал сообщение и благополучно завершил работу (рисунок 4).


Рисунок 4

Осталось протестировать ещё один случай.
-module(test).
-export([start/1, proc/4]).

start(Sysproc) ->
 process_flag(trap_exit, Sysproc),
 io:format("Shell Pid: ~p~n", [self()]),
 PidA = spawn_link(test, proc, [self(), a, true, 
                                fun() -> ok end]),
 PidB = spawn_link(test, proc, [self(), b, false, 
                                fun() -> exit(kill) end]),
 io:format("ProcA: ~p~nProcB: ~p~n", [PidA, PidB]).

proc(Shell, Tag, Sysproc, F) ->
 process_flag(trap_exit, Sysproc),
 F(),
 receive
 	Msg ->
		io:format("Proc ~p get msg: ~p.~n", [Tag, Msg])
	after 5000 ->			   
		Shell ! {hello_from, Tag, self()}
 end.


В функцию proc был добавлен дополнительный аргумент — функция, которая будет вызываться перед блоком receive. Для процесса A функцию будет возвращать лишь значение ок, для процесса B в ней будет происходить вызов функции exit/1 с параметром kill, т.е. kill мы будем создавать внутри процесса, а не посылать сигнал снаружи. Процесс A в данном эксперименте всегда будет системным. Запускаем проверяем.

(emacs@aleksio-mobile)2> test:start(false).
Shell Pid: <0.36.0>
ProcA: <0.43.0>
ProcB: <0.44.0>
Proc a get msg: {'EXIT',<0.36.0>,killed}.
** exception exit: killed
(emacs@aleksio-mobile)3> flush().
ok
(emacs@aleksio-mobile)4> self().
<0.45.0>
(emacs@aleksio-mobile)5> 


Вызываем нашу функцию start/1 (shell делаем не системным), как и ожидалось shell упал. Процесс B был убит сигналом kill, после чего сигнал был передан shell, оболочка упала, затем сигнал был пойман процессом A (рисунок 5).


Рисунок 5

Внимание вопрос! Какой сигнал был передан процессом B оболочке (на рисунке три знака вопроса). По идее должен быть killed. Давайте это проверим — сделаем shell системным.

(emacs@aleksio-mobile)2> test:start(true).
Shell Pid: <0.36.0>
ProcA: <0.43.0>
ProcB: <0.44.0>
ok
(emacs@aleksio-mobile)3> flush().
Shell got {'EXIT',<0.44.0>,kill}
Shell got {hello_from,a,<0.43.0>}
Shell got {'EXIT',<0.43.0>,normal}
ok
(emacs@aleksio-mobile)4> self().
<0.36.0>
(emacs@aleksio-mobile)5> 


Оболочка получила сигнал {'EXIT',<0.44.0>,kill}, но не завершила свою работу! Готового ответа почему так у меня пока нет. Может быть кто-нибудь из читателей знает?

В следующей части мы рассмотрим механизм мониторов.

Список литературы


1. Отличная интерактивная документация.
2. ERLANG Programming by Francesco Cesarini and Simon Thompson.
3. Programming Erlang: Software for a Concurrent World by Joe Armstrong.
Теги:
Хабы:
+23
Комментарии11

Публикации

Изменить настройки темы

Истории

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн