如何用erlang创建一个守护程序?

时间:2011-05-12 02:49:14

标签: erlang

我准备开发一个心跳节目,需要每5秒发送一次udp数据包。

  1. 如何在erlang中睡5s或者是否有睡眠(5)功能?

  2. 如何让它在后台运行?

4 个答案:

答案 0 :(得分:6)

如果您希望您的应用程序发送udp数据包,我建议您从gen_server开始(因为您显然需要在应用程序中添加其他功能)。

1.定期发送数据包。

timer:send_interval(5000,interval),

这将调用gen_server的“handle_call(interval,State)”回调,每隔5秒发送一次数据包

2.让它在后台运行。

已经发布使用“run_erl”。我自己使用它作为守护进程成功运行我的应用程序。

run_erl -daemon /tmp "erl"

这将在unix的“/ tmp”目录下创建两个管道“erlang.pipe.1.r”和“erlang.pipe.1.w”,您可以编写命令来编写管道,以便使用perl或任何脚本来启动应用程序甚至是c / c ++ :)

答案 1 :(得分:4)

最近我一直在学习erlang编程语言。我给自己的一个任务就是编写一个linux守护进程。

您可能已经知道,守护进程用于运行unix服务。通常由守护进程控制的服务包括数据库服务器,Web服务器,Web代理等。在此示例中,服务器非常简单,客户端调用函数“say_hi”,服务器以“hello”响应。

在linux环境中,守护进程由存储在/etc/init.d等位置的脚本控制。这些脚本根据约定响应命令start,stop和restart。

让我们从shell脚本开始:

#!/bin/sh

EBIN=$HOME/Documents/Erlang/Daemon

ERL=/usr/bin/erl

case $1 in

  start|stop|restart)
    $ERL -detached -sname mynode \
           -run daemon shell_do $1  >> daemon2.log
    ;;


  *)
    echo "Usage: $0 {start|stop|restart}"
    exit 1
esac

exit 0

这必须是您见过的最简单的shell脚本之一。守护进程响应三个不同的命令,停止,启动和重启。在此脚本中,命令只是传递给守护程序。一个改进是退出守护程序执行的返回代码。

守护进程怎么样?这是......

%% PURPOSE
%% Author:  Tony Wallace
%%
%% Manage an erlang daemon process as controlled by a shell scripts
%%   Allow standard daemon control verbs
%%      Start  - Starts a daemon in detached mode and exits
%%      Stop   - Attaches to the daemon, monitors it, sends an EXIT message and waits for it to die
%%      Restart - Calls stop and then start
%%   Log events
%%   Return UNIX compatible codes for functions called from shell scripts
%%   Exit shell script calls so as to not stop the scripts from completing
%%   Shell scripts expected to use shell_do to execute functions
%%
%% Allow interaction with daemon from other erlang nodes. 
%%   Erlang processes are expected to call functions directly rather than through shell_do
%%
%% MOTIVATION
%%   Erlang is great, but as an application it needs to be managed by system scripts.
%%   This is particularly for process that are expected to be running without user initiation.
%%
%% INVOCATION
%%   See daemon.sh for details of calling this module from a shell script.
%%
%% TO DO
%%   Define and use error handler for spawn call.

-module(daemon).
%-compile([{debug_info}]).
-export [start/0,start/1,stop_daemon/0,say_hi/0,kill/0,shell_do/1].
%%-define (DAEMON_NAME,daemon@blessing).
-define (DAEMON_NAME,list_to_atom("daemon@"++net_adm:localhost())).
-define (UNIX_OKAY_RESULT,0).
-define (TIMEOUT_STARTING_VM,1).
-define (VM_STARTED_WITHOUT_NAME,2).
-define (INVALID_VERB,3).
-define (COULD_NOT_CONNECT,4).
-define (TIMEOUT_WAITING_QUIT,5).
-define (TIMEOUT_STOPPING_VM,6).

wait_vm_start(_,0) -> ?TIMEOUT_STARTING_VM;
wait_vm_start(D,N) ->
   net_kernel:connect(D),
   Dl = lists:filter(fun(X) -> X==D end,nodes()),
   if Dl =:= [] ->
       receive after 1000 -> true end,
       wait_vm_start(D,N-1);
     Dl /= [] -> ?UNIX_OKAY_RESULT
   end.

wait_vm_stop(_,0) -> ?TIMEOUT_STOPPING_VM;
wait_vm_stop(D,N) ->
   net_kernel:connect(D),
   Dl = lists:filter(fun(X) -> X==D end,nodes()),
   if Dl /= [] ->
       receive after 1000 -> true end,
       wait_vm_start(D,N-1);
      Dl == [] -> ?UNIX_OKAY_RESULT
   end.

flush() ->
    receive
        _ ->
            flush()
    after
        0 ->
            true
    end.

sd(Hdl) ->
   MyNode=node(),
   if
      MyNode =:= nonode@nohost ->
         info(stdout,"~s","Error: Erlang not started with a name.  Use -sname <name>"),
         ?VM_STARTED_WITHOUT_NAME;
      MyNode /= nonode@nohost ->
         Atm_daemon = ?DAEMON_NAME,
         Connected = net_kernel:connect(Atm_daemon),
         case Connected of
            true ->
               info(Hdl,"~s",["daemon process already started"]),
               ?UNIX_OKAY_RESULT;
            false ->
               info(Hdl,"~s",["starting daemon process"]),
               StartString = "erl -detached -sname daemon",
               os:cmd(StartString),
               Vm_daemon = wait_vm_start(Atm_daemon,10),
               case Vm_daemon of
                  ?UNIX_OKAY_RESULT ->
                     info(Hdl,"~s",["spawning main daemon process"]),
                     spawn(Atm_daemon,?MODULE,start,[]), ?UNIX_OKAY_RESULT;
                  A -> A
               end
         end % case Connected %
   end.


say_hi() ->
   Daemon = ?DAEMON_NAME,
   Connected = net_kernel:connect(Daemon),
   if Connected ->
      {listener,Daemon} ! {hello,self()},
      receive
          Response -> Response
      after 10000 -> timeout end;
      not Connected -> could_not_connect
   end.


stop_daemon() ->
   Daemon = ?DAEMON_NAME,
   Connected = net_kernel:connect(Daemon),
   if Connected ->
         flush(),
         {listener,Daemon} ! {quit,self()},
         receive
         bye -> wait_vm_stop(Daemon,10)
     after 10000 -> ?TIMEOUT_WAITING_QUIT
         end;
      not Connected -> ?COULD_NOT_CONNECT
   end.

shell_do(Verb) ->
   {A,Hdl} = file:open('daemon_client.log',[append]),
   case A of
      ok ->
       info(Hdl,"~s",[Verb]);
      error  -> error
   end,
   Result = handle_verb(Hdl,Verb),
   info(Hdl,"Return status ~.10B",[Result]),
   init:stop(Result).

%%handle_verb(_,_) -> 0;

handle_verb(Hdl,["start"]) -> sd(Hdl);
handle_verb(_,["stop"]) ->  stop_daemon();
handle_verb(Hdl,["restart"]) ->
    stop_daemon(),
    sd(Hdl);
handle_verb(Hdl,X) ->   
    info(Hdl,"handle_verb failed to match ~p",[X]),
    ?INVALID_VERB.

kill() ->
    rpc:call(?DAEMON_NAME, init, stop, []).

start(Source) ->
    Source ! starting,
    start().

start() ->
   register(listener,self()),
   case {_,Hdl}=file:open("daemon_server.log",[append]) of
     {ok,Hdl}    -> server(Hdl);
     {error,Hdl} -> {error,Hdl}
   end.

info(Hdl,Fmt,D)->
  io:fwrite(Hdl,"~w"++Fmt++"~n",[erlang:localtime()] ++ D).

server(Hdl) ->
   info(Hdl,"~s",["waiting"]),
   receive
      {hello,Sender} ->
          info(Hdl,"~s~w",["hello received from",Sender]),
          Sender ! hello,
          server(Hdl);
      {getpid,Sender} ->
          info(Hdl,"~s~w",["pid request from ",Sender]),
          Sender ! self(),
          server(Hdl);
      {quit,Sender} ->
          info(Hdl,"~s~w",["quit recevied from ",Sender]),
          Sender ! bye,
          init:stop();
      _ ->
          info(Hdl,"~s",["Unknown message received"])
      after
          50000 ->
             server(Hdl)
   end.

对于不习惯读取erlang的读者,有些代码是由我们上面看到的shell脚本运行的。此文件中的其他代码是守护程序本身。回到shell脚本,我们看到脚本调用过程shell_do。 Shell_do写入日志条目,调用handle_verb并退出。 Handle_verb为每个动词实现不同的行为。启动守护进程由函数sd处理,函数sd通过操作系统调用os:cmd创建守护进程,等待erlang虚拟机初始化,然后生成名为start的服务器代码,该代码又调用服务器。

答案 2 :(得分:3)

通过计时器功能在erlang中可以使用睡眠。

http://www.erlang.org/doc/man/timer.html

对于后台进程,您可以使用-detached cli参数。 您可以使用-s

指定一个入口点

修改

您还可以spawn主程序中的新进程:

http://www.erlang.org/doc/reference_manual/processes.html

答案 3 :(得分:3)

关于守护进程,请考虑使用OTP附带的run_erl实用程序启动erlang程序。请特别注意-daemon命令行标志。