📜 ⬆️ ⬇️

Process tracking and error handling, part 2

Preamble


In the first part of this article, we examined the mechanism of relationships between processes and the process of propagating errors. Today, let us consider one case that was not covered in the previous part — shoot the kill signal at the processes.

Signal kill


The kill signal is not an intercepted signal, i.e. he always kills the process, even if it is systemic. This is used by the OTP supervisor to force the completion of failed processes. When a process receives a kill signal, it dies and the killed signals are sent to all its connections.

All examples will use such a process tree.


Picture 1
')
Let's write a small module.
-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. 


In the start / 1 function, we create two processes: A and B, the body of the processes is the proc / 2 function, the first argument of which is the Pid of the shell, the second Tag: atom (), which serves for convenience of perception when analyzing from which process the message came. After creation, the exit / 2 function is called, which sends a kill signal to process B.

Compile and run.

 (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> 


We call the start / 1 function (the shell in this example is not a system process and cannot catch output signals). After spawning processes, we see the message "** exception exit: killed", which means that the shell was killed with the cause of the killed. The fact that the shell has really died a brave death is told by its new Pid <0.45.0>. Schematically, the whole process can be represented as follows.


Figure 2

The error spreads across all connections and all processes die. Now we slightly modify the code so that the created processes can be made systemic.

 -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> 


Despite the fact that process B was made systemic (the double circle in Figure 3), it still dies after receiving the kill signal, after which the killed signal is distributed through the links and all processes die.


Figure 3

Now let's make the shell system too.

 (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> 


We see that process B was killed, the shell was able to intercept the exit signal from it (Shell got {'EXIT', <0.44.0>, killed}), process A sent a message and safely completed the work (Figure 4).


Figure 4

It remains to test another case.
 -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. 


An additional argument was added to the proc function - a function that will be called before the receive block. For process A, the function will return only the value ok, for process B, it will call the exit / 1 function with the kill parameter, i.e. kill we will create inside the process and not send a signal outside. Process A in this experiment will always be systemic. We start we check.

 (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> 


We call our start / 1 function (we make the shell non-systemic), as expected, the shell has dropped. Process B was killed by the kill signal, after which the signal was transmitted to the shell, the shell dropped, then the signal was caught by process A (Figure 5).


Figure 5

Attention question! What signal was transmitted by the B shell process (in the figure there are three question marks). The idea is to be killed. Let's check it - let's make the shell system.

 (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> 


The shell received a signal {'EXIT', <0.44.0>, kill}, but did not complete its work! There is no ready answer why so far. Maybe someone from readers knows?

In the next part we will look at the mechanism of monitors.

Bibliography


1. Excellent online documentation .
2. ERLANG Programming by Francesco Cesarini and Simon Thompson.
3. Programming Erlang: Software for a Concurrent World by Armstrong.

Source: https://habr.com/ru/post/114753/


All Articles