我不会尝试通过 Pid 访问子进程,而是 register/2 使用名称的子进程,因此无论实际 Pid 是什么都可以访问它。
使用代码from the answer you reference,一个简单的方法是将register(ch1, self()), 添加到子进程的init 过程中。对于ch1.erl,这将给出:
init(_Args) ->
io:format("ch1 has started (~w)~n", [self()]),
% register a name to this process
register(child, self()),
{ok, ch1State}.
这会将孩子self() 的pid 注册到名称child
我们可以看到它有效:
1> root_sup:start_link().
{ok,<0.34.0>}
2> supervisor:start_child(root_sup, []).
ch1 has started (<0.36.0>)
{ok,<0.36.0>}
3> lists:filter(fun(X) -> X == child end, registered()).
[child]
我们确实有一个以child 名义注册的进程。
4> gen_server:cast(child, calc).
result 2+2=4
这是一个正确的进程,运行来自ch1.erl的代码。
让我们通过调用错误代码来崩溃这个过程:
5> gen_server:cast(child, calcbad).
result 1/0
ok
ch1 has started (<0.41.0>)
6>
=ERROR REPORT==== 28-Oct-2012::01:31:30 ===
** Generic server <0.36.0> terminating
** Last message in was {'$gen_cast',calcbad}
** When Server state == ch1State
** Reason for termination ==
** {'function not exported',
[{ch1,terminate,
[{badarith,
[{ch1,handle_cast,2,[{file,"ch1.erl"},{line,27}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,607}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
ch1State],
[]},
{gen_server,terminate,6,[{file,"gen_server.erl"},{line,722}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,227}]}]}
于是孩子,进程<0.36.0>崩溃了,一个新的孩子<0.41.0>开始承担死者<0.36.0>的职责。由于这个新进程以相同的名称注册,calc 将再次工作:
6> gen_server:cast(child, calc).
result 2+2=4
ok
请注意,这并不能保证gen_server:cast/2s 总是导致相应代码的执行,因为可能刚刚杀死了子进程,而新进程仍未启动(实际上已注册)。
您可能想参考优秀的Programming Erlang: Software for a Concurrent World by Joe Armstrong,了解有关流程注册、主管、OTP 等的更多详细信息。很多细节也可以在the online documentation of OTP找到。