我正在使用Erlang端口读取Linux进程的输出。我希望当我连接的Erlang进程死亡时,Linux进程会自动被杀死。从文档中,我认为这应该自动发生,但事实并非如此。
最小的例子。把这个放在文件test.erl中:
-module(test).
-export([start/0, spawn/0]).
start() ->
Pid = spawn_link(?MODULE, spawn, []),
register(test, Pid).
spawn() ->
Port = open_port({spawn, "watch date"},[stream, exit_status]),
loop([{port, Port}]).
loop(State) ->
receive
die ->
error("died");
Any ->
io:fwrite("Received: ~p~n", [Any]),
loop(State)
end.
然后,在erl shell中:
1> c(test).
{ok,test}
2> test:start().
true
进程启动并每2秒打印从Linux“watch”命令接收的一些数据。
然后,我使Erlang进程崩溃:
3> test ! die.
=ERROR REPORT==== 26-May-2021::13:24:01.057065 ===
Error in process <0.95.0> with exit value:
{"died",[{test,loop,1,[{file,"test.erl"},{line,15}]}]}
** exception exit: "died"
in function test:loop/1 (test.erl, line 15)
Erlang进程按预期停止,来自“watch”的数据停止显示,但watch进程仍在后台继续运行,如Linux(非erl)终端所示:
fuxoft@frantisek:~$ pidof watch
1880127
在我的现实生活场景中,我没有使用“watch”命令,而是使用其他输出数据但不接受输入的进程。我如何才能使它在我连接的Erlang进程崩溃时自动死亡呢?我可以使用Erlang管理程序并在Erlang进程崩溃时手动发出“kill”命令来做到这一点,但我认为这可以更简单、更干净地完成。
1条答案
按热度按时间u0njafvf1#
The
open_port
function creates aport()
and links it to the calling process. If the owning process dies, theport()
closes.In order to communicate with the externally spawned command, Erlang creates several pipes, which are by default tied to the
stdin
andstdout
(file descriptors) of the external process. Anything that the external process writes through the stdout will arrive as a message to the owning process.When the
Port
is closed, the pipes attaching it to the external process are broken, and so trying to read or write to them will give you a SIGPIPE/EPIPE.You can detect that from your external process when writing or reading from the FDs and exiting the process then.
E.g.: With your current code, you can retrieve the external process OS pid with
proplists:get_value(os_pid, erlang:port_info(Port))
. If youstrace
it, you will see:SIGPIPE in ports and Erlang
It seems that although the default action for SIGPIPE is to terminate the process, Erlang sets it to ignore the signal (and thus the children processes inherit this configuration).
If you're unable to modify the external process code to detect the EPIPE, you can use this c wrapper to reset the action:
just compile it and run it as
wrapper path-to-executable [arg1 [arg2 [...]]]
withopen_port