简单来说:应该是在调用的start_link返回一个{ok,Pid}就可以把这个进程放入监控树Supervisor里面:

   -module(worker).
-author("zhongwencool@gmail.com"). -export([start_link/,stop_worker/]). start_link() –>
{ok,spawn(fun() -> loop() end)}. loop() –>
case whereis(?MODULE) of
undefined –>
io:format("worker restart~n"),
erlang:register(?MODULE,self());
_ -> ok
end,
receive
stop –>
io:format("Worker Stop~n");
Msg –>
io:format("RECV:~w~n",[Msg]),
loop()
end. stop_worker() –>
erlang:send(?MODULE,stop).

这个和gen_server的区别在于,得不到很多如gen_server的行为保证(guarantees),比如gen_sever与supervisor的特性:只有init/1返回后才会返回{ok,Pid},如果你用spawn/1就是异步,无此特性了。

那么如果,你还是觉得gen_server放在你的需求中有点大材小用,要自己造一个更加轻量的进程,又想拥有类似于gen_server的特性,怎么办呢?

你可以参照OTP Design Principles: http://www.erlang.org/doc/design_principles/spec_proc.html#id72814

简单地说就是:使用sys,proce_lib使进程符合:Supervision tree.


6.2  Special Processes

这部分描述了怎么写一个不使用标准行为(gen_server,gen_fsm,gen_event)模板,写一个完全符合OTP 设计原则的进程要做到那些点:

1. 这个进程必须能被加入监控树;
2. 支持 Debug Facilities
3. 支持System Messages.

系统信息(System message )是在监控树里面使用的一种特殊信息,典型特性:支持trace output; 支持suspend or resume process execution(在版本更新时使用);标准的行为包(如gen_server)是自动支持这些特性的。

例子:

请看从 Overview 章节拿来来的简单例子:使用sys,proce_lib使进程符合:Supervision tree.
-module(ch4).
-export([start_link/0]).
-export([alloc/0, free/1]).
-export([init/1]).
-export([system_continue/3, system_terminate/4,
write_debug/3,
system_get_state/1, system_replace_state/2]). start_link() ->
proc_lib:start_link(ch4, init, [self()]). alloc() ->
ch4 ! {self(), alloc},
receive
{ch4, Res} ->
Res
end. free(Ch) ->
ch4 ! {free, Ch},
ok. init(Parent) ->
register(ch4, self()),
Chs = channels(),
Deb = sys:debug_options([]),
proc_lib:init_ack(Parent, {ok, self()}),
loop(Chs, Parent, Deb). loop(Chs, Parent, Deb) ->
receive
{From, alloc} ->
Deb2 = sys:handle_debug(Deb, fun ch4:write_debug/3,
ch4, {in, alloc, From}),
{Ch, Chs2} = alloc(Chs),
From ! {ch4, Ch},
Deb3 = sys:handle_debug(Deb2, fun ch4:write_debug/3,
ch4, {out, {ch4, Ch}, From}),
loop(Chs2, Parent, Deb3);
{free, Ch} ->
Deb2 = sys:handle_debug(Deb, fun ch4:write_debug/3,
ch4, {in, {free, Ch}}),
Chs2 = free(Ch, Chs),
loop(Chs2, Parent, Deb2); {system, From, Request} ->
sys:handle_system_msg(Request, From, Parent,
ch4, Deb, Chs)
end. system_continue(Parent, Deb, Chs) ->
loop(Chs, Parent, Deb). system_terminate(Reason, _Parent, _Deb, _Chs) ->
exit(Reason). system_get_state(Chs) ->
{ok, Chs}. system_replace_state(StateFun, Chs) ->
NChs = StateFun(Chs),
{ok, NChs, NChs}. write_debug(Dev, Event, Name) ->
io:format(Dev, "~p event = ~p~n", [Name, Event]).
如何使用以上的sys和ch4模块呢?

% erl
Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0] Eshell V5.2.3.6 (abort with ^G)
1> ch4:start_link().
{ok,<0.30.0>}
2> sys:statistics(ch4, true).
ok
3> sys:trace(ch4, true).
ok
4> ch4:alloc().
ch4 event = {in,alloc,<0.25.0>}
ch4 event = {out,{ch4,ch1},<0.25.0>}
ch1
5> ch4:free(ch1).
ch4 event = {in,{free,ch1}}
ok
6> sys:statistics(ch4, get).
{ok,[{start_time,{{2003,6,13},{9,47,5}}},
{current_time,{{2003,6,13},{9,47,56}}},
{reductions,109},
{messages_in,2},
{messages_out,1}]}
7> sys:statistics(ch4, false).
ok
8> sys:trace(ch4, false).
ok
9> sys:get_status(ch4).
{status,<0.30.0>,
{module,ch4},
[[{'$ancestors',[<0.25.0>]},{'$initial_call',{ch4,init,[<0.25.0>]}}],
running,<0.25.0>,[],
[ch1,ch2,ch3]]}

启动进程要做的事:

必须使用proc_lib模块的函数来启动进程,这里有几个可能用到的函数,例如:spawn_link/3,4用于异步启动,start_link/3,4,5用于同步启动(和刚才说的要等init/1返回才能再继续一个原理),

使用proc_lib函数启动的进程会把监控树所要的进程信息(继承关系ancestors,初始华调用initial call等)都保存下来;

并且,当进程被异常(不是normal or shutdown)终结时,会生成相应的崩溃报告(crash report),你可以查看SASL User’s Guide来得到。

在上面这个ch4的例子中,使用的是同步启动(和gen_server一样),这个进程启动时调用ch4:start_link():

start_link() ->
proc_lib:start_link(ch4, init, [self()]).

ch4:start_link 调用proc_lib:start_link. 这个函数使用模块明,函数名和一个参数列表来创建(spawns)一个新的进程并links它.新的进程会调用ch4:init(Pid), Pid就是父进程传进来的self().

在初始华时,所有的初始化(包括注册名字)都会完成,初始化完成时必须要通知父进程已完成:

init(Parent) ->
...
proc_lib:init_ack(Parent, {ok, self()}),
loop(...).

注意:proc_lib:start_link 是同步创建,它会一直等待子进程用 proc_lib:init_ack 返回; 异步创建请用proce_lib:spawn_link.

Debugging

我们需要用一个debug 结构(通过sys:debug_options/1初始化得到的term结构)使用sys模块支持Debug

init(Parent) ->
...
Deb = sys:debug_options([]),
...
loop(Chs, Parent, Deb).

sys:debug_options/1 返回一个选择列表(list of options),在这里面是一个空列表,代表没有debugging在初始化时建立,你可以通过sys(3)查看其它可能的选项。

使用以下函数来记录(logged)或跟踪(traced)每一个我们想要的system event

sys:handle_debug(Deb, Func, Info, Event) => Deb1
  • Deb 就是在上面被sys:debug_options/1 初始华的debug结构

  • Func 是用户自己定义用于跟踪输出的函数,对于每一个system event,都会调用 Func(Dev, Event, Info),
    • Dev是标准IO设备用于输入,可以查看io(3).

    • Event 和 Info由 handle_debug得到.
  • Info 可以是任意term 来代表其它附加的信息传给Func.

  • Event 是system event,这取决于用户怎么去定义system event,但是典型至少包括那些进出的消息(incoming and outgoing message),就像这样的结构{in Msg,[,From]} ,{out,Msg,To}.

handle_debug 返回一个更新的debug structure((Deb1).

在这个例子中,handle_debug会被所有的进出消息调用,每个消息的处理会调用ch4:write_debug/3 :

loop(Chs, Parent, Deb) ->
receive
{From, alloc} ->
Deb2 = sys:handle_debug(Deb, fun ch4:write_debug/3,
ch4, {in, alloc, From}),
{Ch, Chs2} = alloc(Chs),
From ! {ch4, Ch},
Deb3 = sys:handle_debug(Deb2, fun ch4:write_debug/3,
ch4, {out, {ch4, Ch}, From}),
loop(Chs2, Parent, Deb3);
{free, Ch} ->
Deb2 = sys:handle_debug(Deb, fun ch4:write_debug/3,
ch4, {in, {free, Ch}}),
Chs2 = free(Ch, Chs),
loop(Chs2, Parent, Deb2);
...
end. write_debug(Dev, Event, Name) ->
io:format(Dev, "~p event = ~p~n", [Name, Event]).

Handling System Messages

收到的系统消息格式如下:

{system, From, Request}

这些消息不会打扰到进程,会被自动调用以下的函数处理:

The content and meaning of these messages do not need to be interpreted by the process. Instead the following function should be called:

sys:handle_system_msg(Request, From, Parent, Module, Deb, State)

这个函数不会返回,它会处理完system message 后再调用以下函数来继续这个进程:

Module:system_continue(Parent, Deb, State)

也可以调用以下函数来终结这个进程:

Module:system_terminate(Reason, Parent, Deb, State)

如果进程应该被终结,那么监控树里的一个监控进程也会以相同的原因终结掉。

  • Request 和From 要从system message 中得到,再用于handle_system_msg;
  • Parent 是父进程的PID;
  • Module 代表的是模块名;
  • Deb 是一个debug结构;
  • State 是用于描述内部state糊状的term,通过system_continue/system_terminate/ system_get_state/system_replace_state来得到。

如果进程想返回它的类型(类于gen_server:call返回),就使用:

Module:system_get_state(State)

或者进程只需要更新StateFunc :

Module:system_replace_state(StateFun, State)
 
在上面的例子中:
loop(Chs, Parent, Deb) ->
receive
... {system, From, Request} ->
sys:handle_system_msg(Request, From, Parent,
ch4, Deb, Chs)
end. system_continue(Parent, Deb, Chs) ->
loop(Chs, Parent, Deb). system_terminate(Reason, Parent, Deb, Chs) ->
exit(Reason). system_get_state(Chs) ->
{ok, Chs, Chs}. system_replace_state(StateFun, Chs) ->
NChs = StateFun(Chs),
{ok, NChs, NChs}.

如果进程想要设置trap exits,来让进程在终结时调用terminates 那么调用以下会发 {'EXIT', Parent, Reason}处理:

init(...) ->
...,
process_flag(trap_exit, true),
...,
loop(...). loop(...) ->
receive
... {'EXIT', Parent, Reason} ->
..maybe some cleaning up here..
exit(Reason);
...
end.

 
  突然感觉膝盖有些微微的痛。。。

[Erlang13]怎么把一个普通的进程挂入Supervisor监控树?的更多相关文章

  1. 分析Linux内核创建一个新进程的过程【转】

    转自:http://www.cnblogs.com/MarkWoo/p/4420588.html 前言说明 本篇为网易云课堂Linux内核分析课程的第六周作业,本次作业我们将具体来分析fork系统调用 ...

  2. 分析Linux内核创建一个新进程的过程

    一.原理分析 1.进程的描述 进程控制块PCB——task_struct,为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. struct task_struct ...

  3. APC 篇—— APC 挂入

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  4. linux内核分析作业6:分析Linux内核创建一个新进程的过程

    task_struct结构: struct task_struct {   volatile long state;进程状态  void *stack; 堆栈  pid_t pid; 进程标识符  u ...

  5. 实验六:分析Linux内核创建一个新进程的过程

    原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 题目自拟,内容围绕对Linu ...

  6. 一个普通的 Zepto 源码分析(二) - ajax 模块

    一个普通的 Zepto 源码分析(二) - ajax 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块,以 ...

  7. 一个普通的 Zepto 源码分析(三) - event 模块

    一个普通的 Zepto 源码分析(三) - event 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块, ...

  8. 一个普通的 Zepto 源码分析(一) - ie 与 form 模块

    一个普通的 Zepto 源码分析(一) - ie 与 form 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核 ...

  9. 第六周分析Linux内核创建一个新进程的过程

    潘恒 原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 task_struct结构: ...

随机推荐

  1. WePY 在手机充值小程序中的应用与实践

    wepyjs 发布了两个月了,中间经历了很多版本更新,也慢慢开始有一些用户选择 wepyjs 作为开发框架来开发小程序,比如一些线上小程序. 以及一些来自网上的 wepyjs 的相关资源: demo源 ...

  2. 塔防游戏 Day2

    1. 创建炮塔选择的 UI 使用 UI -> Toggle .注意指定同一 group. 2. 创建炮台的数据类 [System.Serializable] // 序列化 public clas ...

  3. nodejs开发工具

    我选择的是Hbuilder作为node项目的开发工具.   先在Hbuilder 里面安装nodeEclipse插件,然后重启工具. 点击添加项目,选择其他选项,出现下图选项,然后选择圈住的选项点击下 ...

  4. linux下connect超时时间探究

    最近在linux做服务器开发的时候,发现了一个现象:服务器在启动的时候调用了 connect 函数,因为连接了一个不可用的端口,导致connect最后报出了 “Connection timed out ...

  5. C# Common Keyword II

    [C# Common Keyword II] 1.as 运算符用于在兼容的引用类型之间执行某些类型的转换. class csrefKeywordsOperators { class Base { pu ...

  6. go反射实例

    需求分析: 如在rocketmq的网络通信中,所有通信数据包以如下形式传输: (注:rocketmq的java结构体,这里使用了go形式表示) type RemotingCommand struct ...

  7. 我的MBTI性格测试

    写在前面: 很多人争论MBTI靠谱不靠谱.一个人的性格肯定不能只用这么几个维度就能描述的,一个人的性格也肯定不是通过这么几个问题就能测出来的,一个人的性格也肯定不是一成不变的,所以MBTI的准确度肯定 ...

  8. cdoj1091-秋实大哥の恋爱物语 【kmp】

    http://acm.uestc.edu.cn/#/problem/show/1091 秋实大哥の恋爱物语 Time Limit: 5000/2000MS (Java/Others)     Memo ...

  9. Codeforces 76D 位运算

    题意:给你两个数x 和 y, x = a + b, y = a XOR b,问有没有合法的a和b满足这个等式? 思路:有恒等式: a + b = ((a & b) << 1) + ...

  10. spring mabatis springmvc 看过

    .Spring中AOP的应用场景.Aop原理.好处? 答:AOP--Aspect Oriented Programming面向切面编程:用来封装横切关注点,具体可以在下面的场景中使用: Authent ...