链接进程

进程可以互相监视。这里要引入两个概念,进程链接EXIT信号。在执行期间,进程可以与其他进程(和端口,参见??章节)建立链接。当一个进程终止(无论正常或非正常终止)时,一个特殊的EXIT信号将被发送到所有与即将终止的进程相链接的进程(及端口)。该信号的格式如下:

  1. {'EXIT', Exiting_Process_Id, Reason}

Exiting_Process_Id是即将终止的进程的进程标识,Reason可以是任意的Erlang项式。

收到Reason不是原子式normalEXIT信号时,信号接收进程的默认动作是立即终止并,同时向当前与之链接的进程发送EXIT信号。默认情况下,Reason为原子式normalEXIT信号将被忽略。

EXIT信号的默认处理方式行为可以被覆写,以允许进程在接收到EXIT信号时采取任意必要的动作。

创建和删除链接

进程可以链接到其它进程和端口。进程间的链接都是双向的,也就是说,如果进程A链接到进程B,那么进程B也会自动链接到进程A。

通过执行BIF link(Pid)便可创建链接。调用link(Pid)时,若调用进程和Pid之间已经存在链接,则不会产生任何影响。

进程终止时,它所持有的链接都将被删除。也可以通过执行BIF unlink(Pid)显式删除链接。由于所有链接都是双向的,删除这一端到另一端的链接的同时,另一端的到这一端的链接也会被删除。若调用进程和Pid之间原本就没有链接,unlink(Pid)不会产生任何影响。

BIF spawn_link/3在创建新进程的同时还会在调用进程和新进程间建立链接。其行为可以定义为:

  1. spawn_link(Module, Function, ArgumentList) ->
  2. link(Id = spawn(Module, Function, ArgumentList)),
  3. Id.

只不过spawnlink是原子方式执行的。这是为了避免调用进程在执行link之前就被EXIT信号杀死。尝试向一个不存在的进程发起链接将导致信号{'EXIT',Pid,noproc}被发送至link(Pid)的调用进程。

程序7.2中,函数start/1建立了若干以链式互联的进程,其中第一个进程的注册名为start(参见图7.1)。函数test/1向该注册进程发送消息。每个进程不断打印自己在链中的位置及收到的消息。消息stop令链中最后一个进程执行BIF exit(finished),该BIF将导致该进程异常终止。

程序7.2

  1. -module(normal).
  2. -export([start/1, p1/1, test/1]).
  3.  
  4. start(N) ->
  5. register(start, spawn_link(normal, p1, [N - 1])).
  6.  
  7. p1(0) ->
  8. top1();
  9. p1(N) ->
  10. top(spawn_link(normal, p1, [N - 1]),N).
  11.  
  12. top(Next, N) ->
  13. receive
  14. X ->
  15. Next ! X,
  16. io:format("Process ~w received ~w~n", [N,X]),
  17. top(Next,N)
  18. end.
  19.  
  20. top1() ->
  21. receive
  22. stop ->
  23. io:format("Last process now exiting ~n", []),
  24. exit(finished);
  25. X ->
  26. io:format("Last process received ~w~n", [X]),
  27. top1()
  28. end.
  29.  
  30. test(Mess) ->
  31. start ! Mess.

我们启动三个进程(参见图7.1(a))

  1. > normal:start(3).
  2. true

_images/7.1.png图7.1 进程退出信号的传递

然后向第一个进程发送消息123

  1. > normal:test(123).
  2. Process 2 received 123
  3. Process 1 received 123
  4. Last process received 123
  5. 123

再向第一个进程发送消息stop

  1. > normal:test(stop).
  2. Process 2 received stop
  3. Process 1 received stop
  4. Last process now exiting
  5. stop

这条消息顺着进程链传递下去,我们将看到它最终导致链中最后一个进程的终止。这会引发一个发送给倒数第二个进程的EXIT信号,致其异常终止(图7.1(b)),接着又向第一个进程发送EXIT信号(图7.1(c)),于是注册进程start也异常终止(图 7.1(d))。

若这时再向注册进程start发送一条新消息,将由于目标进程不存在而失败:

  1. > normal:test(456).
  2. !!! Error in process <0.42.1> in function
  3. !!! normal:test(456)
  4. !!! reason badarg
  5. ** exited: badarg **