zoukankan      html  css  js  c++  java
  • Erlang generic standard behaviours -- gen

    在分析 gen_server (或者是gen_fsm )之前,首先应该弄明白,gen 这个module .

    1 -module(gen).
    2 -compile({inline,[get_node/1]}).
    3 
    4 %%%-----------------------------------------------------------------
    5 %%% This module implements the really generic stuff of the generic
    6 %%% standard behaviours (e.g. gen_server, gen_fsm).
    7 %%%
    8 %%% The standard behaviour should export init_it/6.
    9 %%%-----------------------------------------------------------------

    也就是说,gen 这个模块 gen_server ,gen_fsm 的基础.

    PS. 无特殊说明,以下提到的gen_server 都指代gen_server 和 gen_fsm .

    export func

    1 -export([start/5, start/6, debug_options/1,
    2           call/3, call/4, reply/2]).

    gen 模块导出了以上这些函数,其中:

    start/N 是用于spawn gen_server 进程. 基本的流程是使用proc_lib:spawn(_link) 创建新的process, 并以init_it 作为初始化函数, 继而回调 gen_server 模块中的init_it函数, 完成gen_server 模块的init .

    call/N 是用于gen_server call func 的实现,也就是 gen_server:call 的实际调用.

    reply/2 的实现相对“简单”,即调用bif erlang:'!'/2 发送消息. 但是这个函数的作用很大, 之后会一一提到.

    start/N

    start/5 用于创建无名gen_server , start/6 用于创建指定name 的gen_server .

     1     start(GenMod, LinkP, Name, Mod, Args, Options)
     2         GenMod  :: gen_server | gen_fsm
     3         LinkP   :: nolink     | link
     4         Name    :: {local, atom()} | {global, term()} | {via, atom(), term()} %% 就是指定的name
     5         Mod     :: atom() %% 就是使用了gen_server behaviour 的模块名
     6         Args    :: term() %% Mod 模块 init func 的初始参数
     7         Options :: [{timeout, Timeout} | {debug, [Flag]} | {spawn_opt, OptionList}] %% 这个参数是用来指定创建进程的参数
     8         
     9     
    10         Flag    :: trace | log | {logfile, File} | statistics | debug
    1     start(GenMod, LinkP, Mod, Args, Options)
    2         %% 相比start/6, 没有了执行name 的参数

    start/6 会先确认给定的name 是否已经被使用, 如果被使用, 会返回

    1 {error, {already_started, Pid}} %% Pid 是名字为name 的进程

    反之, 继续执行do_spawn , do_spawn 会调用proc_lib:spawn(_link) 函数创建进程, 并以 init_it 为初始化函数. (先挖个坑, ** proc_lib:spawn VS erlang:spawn ** 后面填)

    注意, 此时已经有新的进程被创建了, 而 init_it 函数,已经是在由新的进程执行.

    1     init_it(GenMod, Starter, Parent, Mod, Args, Options)
    2         Starter :: pid() %% 即调用proc_lib:spawn 的进程
    3         Parent  :: pid() | atom() %% Parent 只有在以link方式 start的时候才有意义 (此坑后填)
    4     init_it(GenMod, Starter, Parent, Name, Mod, Args, Options)
    5         %% Name 即指定的name, 首先会以Name 为参数调用 name_register/1
    6         
    7     init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options) ->
    8         GenMod:init_it(Starter, Parent, Name, Mod, Args, Options).
    9     %% 回调 gen_server/gen_fsm 模块的init_it 函数

     call/N

    call/3 使用default_timeout 作为第四个参数调用 call/4 .

    call/4 首先会进程一系列的参数校验, 然后调用do_call/4

     1     do_call(Process, Label, Request, Timeout) ->
     2         %% 首先调用 bif monitor Process
     3         try erlang:monitor(process, Process) of
     4         Mref ->
     5             %% 然后给 Process 发送消息
     6             catch erlang:send(Process, {Label, {self(), Mref}, Request},
     7               [noconnect]),
     8             %% 并等待 Process 的返回结果, 就是在 handle_call 中的Reply 或者是 gen:reply 的 结果
     9             receive
    10             {Mref, Reply} ->
    11                 %% 关于 erlang:monitor/erlang:demonitor 的文档很详细
    12                 erlang:demonitor(Mref, [flush]),
    13                 {ok, Reply};
    14             {'DOWN', Mref, _, _, noconnection} ->
    15                 Node = get_node(Process),
    16                 exit({nodedown, Node});
    17             {'DOWN', Mref, _, _, Reason} ->
    18                 exit(Reason)
    19             after Timeout ->
    20                 erlang:demonitor(Mref, [flush]),
    21                 exit(timeout)
    22             end
    23         catch
    24         %% bif monitor Process 失败, 就 monitor Process 所在的 Node
    25         error:_ ->
    26             Node = get_node(Process),
    27             monitor_node(Node, true),
    28             receive
    29             {nodedown, Node} -> 
    30                 monitor_node(Node, false),
    31                 exit({nodedown, Node})
    32             after 0 -> 
    33                 Tag = make_ref(),
    34                 %% bif erlang:'!'/2 发送消息
    35                 Process ! {Label, {self(), Tag}, Request},
    36                 %% 等待响应
    37                 wait_resp(Node, Tag, Timeout)
    38             end
    39         end.

     call/3 以及 call/4 主要就做了这些事情.

    reply/2

    此函数实现在erl 代码层面很简单, 但对gen_server 进程来说,有独特的作用,后续会进行分析.

    填坑

    proc_lib:spawn 和 erlang:spawn 的区别:

    翻下代码,就能看到, so easy

     相比较 erlang:spawn, proc_lib:spawn 在被创建的进程中,添加了一些信息:

    '$ancestors',  '$initial_call'.

    在某些场景下,这些信息对于定位进程的归属有很大的作用.比如说:定位僵死进程的归属,定位崩溃进程的归属等.

  • 相关阅读:
    hbase
    pig
    flume
    sqoop
    eclipse 提交作业到JobTracker Hadoop的数据类型要求必须实现Writable接口
    hadoop 8步走
    ssh原理
    MapReduce基础
    Arduino数字贴片磁感应传感器(收藏篇)
    去掉input回车自动提交
  • 原文地址:https://www.cnblogs.com/--00/p/4271386.html
Copyright © 2011-2022 走看看