查看源代码 erlsrv
在 Windows 上将 Erlang 模拟器作为服务运行
描述
此实用程序特定于 Windows NT/2000/XP(以及更高版本的 Windows)。它允许 Erlang 模拟器在 Windows 系统上作为服务运行,从而允许嵌入式系统在无需任何用户登录的情况下启动。以这种方式启动的模拟器可以通过 Windows 服务小程序进行操作,方式与其他服务类似。
请注意,erlsrv
不是 Windows 的通用服务实用程序,而是为嵌入式 Erlang 系统设计的。
erlsrv
还提供一个命令行界面,用于注册、更改、启动和停止服务。
要操作服务,登录用户必须具有计算机上的管理员权限。Erlang 机器本身(默认情况下)以本地管理员身份运行。可以使用 Windows 中的服务小程序更改此设置。
与普通服务相反,服务创建的进程可以使用任务管理器“杀死”。杀死由服务启动的模拟器会触发为该服务指定的“OnFail”操作,该操作可以是重新启动。
可以为每个 Erlang 服务指定以下参数
StopAction
- 告诉erlsrv
如何停止 Erlang 模拟器。默认是杀死它(Win32 TerminateProcess),但此操作可以指定任何将在模拟器中执行的 Erlang shell 命令,以使其停止。模拟器应在 shell 中发出命令后的 30 秒内停止。如果模拟器未停止,则它会向服务管理器报告运行状态。OnFail
- 可以是以下之一reboot
- 只要模拟器停止,Windows 系统就会重新启动(一种更简单的看门狗形式)。这对于不太关键的系统很有用,否则请使用 heart 功能来实现此目的。restart
- 使 Erlang 模拟器在停止时重新启动(使用为该服务注册的任何参数)。如果模拟器在 10 秒内再次停止,则不会重新启动,以避免无限循环,这可能会导致 Windows 系统挂起。restart_always
- 与restart
类似,但不尝试检测循环重启;预计会存在其他一些机制来避免此问题。ignore
(默认) - 只要服务失败,就向服务管理器报告该服务已停止;必须手动重新启动。
在使用发布处理的系统上,这始终设置为
ignore
。请改用heart
在失败时重新启动服务。Machine
- Erlang 模拟器的位置。默认值是位于与erlsrv.exe
相同目录中的erl.exe
。如果系统使用发布处理,则应将其设置为类似于
start_erl.exe
的程序。Env
- 为模拟器指定一个额外的环境。此处指定的环境变量将添加到服务启动时通常存在的系统范围的环境块中。系统范围的环境和 服务环境规范中都存在的变量将设置为服务中指定的值。WorkDir
- Erlang 模拟器的工作目录。必须在本地驱动器上(服务启动时不会挂载网络驱动器)。服务的默认工作目录是%SystemDrive%%SystemPath%
。调试日志文件将放置在此目录中。Priority
- 模拟器的进程优先级。可以是以下之一realtime
- 不建议使用,因为机器可能无法让交互用户访问。high
- 如果两个 Erlang 节点要驻留在同一个专用系统上,并且一个节点要优先于另一个节点,则可以使用此选项。low
- 如果不希望交互性能受到模拟器进程的影响,则可以使用此选项。default
- 默认优先级。
SName or Name
- 指定 Erlang 模拟器的短节点名称或长节点名称。Erlang 服务始终是分布式的。默认是使用服务名称作为(短)节点名称。DebugType
- 指定 Erlang shell 的输出将发送到“调试日志”。日志文件命名为 <服务名称>.debug
或 <服务名称>.debug.
<N>,其中 <N> 是从 1 到 99 的整数。日志文件放置在服务的工作目录中(如WorkDir
中指定)。可以是以下之一
new
- 对服务的每次调用使用单独的日志文件(<服务名称>.debug.
<N>)。reuse
- 重用同一个日志文件(<服务名称>.debug
)。console
- 为服务的 Erlang shell 打开一个交互式 Windows 控制台窗口。自动禁用StopAction
。使用交互式控制台窗口启动的服务在注销后无法存活。OnFail
操作也不适用于调试控制台。none
(默认) - Erlang shell 的输出将被丢弃。
注意
console
选项不适用于生产环境。它仅是在开发期间调试 Erlang 服务的一种方便方法。new
和reuse
选项在生产系统中可能看起来很方便,但请考虑日志在系统生命周期内会无限增长,并且除非重新启动服务,否则无法截断。简而言之,
DebugType
仅用于调试。在生产期间,最好使用标准的 Erlang 日志记录工具来生成日志。Args
- 将额外的参数传递给模拟器启动程序erl.exe
(或start_erl.exe
)。此处无法指定的参数是-noinput
(StopActions
将不起作用)、-name
和-sname
(它们以任何方式指定)。最常见的用途是指定要传递给init
的 cookie 和标志(-s
)。InternalServiceName
- 指定 Windows 内部服务名称(不是显示名称,erlsrv
使用该名称来标识服务)。此内部名称无法更改,即使重命名服务,它也是固定的。
erlsrv
在创建服务时会生成唯一的内部名称。如果应用程序要使用发布处理,建议保持默认设置。如果查看 Erlang 服务的
属性
,则可以在 Windows 服务管理器中看到内部服务名称。Comment
- 描述服务的文本注释。不是强制性的,但会在 Windows 服务管理器中显示为服务说明。
在使用发布处理的系统中,服务的命名必须遵循 节点名称_发布 的约定,其中节点名称是 Erlang 节点名称的第一部分(直到但不包括“@”),发布是应用程序的当前发布版本。
erlsrv {set | add} <服务名称> [<服务选项>]
set
和 add
命令分别修改或添加 Erlang 服务。最简单的 add
命令形式是没有选择,在这种情况下,所有默认值(如上所述)都适用。服务名称是强制性的。
每个选项都可以指定不带参数,然后应用默认值。仅当不使用默认值时,才提供选项的值。例如,erlsrv set myservice -prio -arg
设置默认优先级并删除所有参数。
服务选项
-st[opaction] [<Erlang shell 命令>]
- 定义StopAction
,即在停止服务时给 Erlang shell 的命令。默认值为 none。-on[fail] [{reboot | restart | restart_always}]
- 当 Erlang 模拟器意外停止时要采取的操作。默认是忽略。-m[achine] [<erl-command>]
- Erlang 模拟器的完整路径。永远不要为此使用werl
程序。默认为与erlsrv.exe
相同目录中的erl.exe
。当使用发布处理时,这应设置为类似于start_erl.exe
的程序。-e[nv] [<variable>[=<value>]] ...
- 编辑服务的环境变量块。每个指定的环境变量都会添加到系统环境变量块中。如果此处指定的变量与系统级环境变量同名,则指定的值会覆盖系统级的值。通过指定 <variable>=<value> 将环境变量添加到此列表,并通过单独指定 <variable> 从列表中删除环境变量。环境变量块会自动排序。一个命令中可以指定任意数量的-env
选项。默认情况下,使用未修改的系统环境变量块(除了两个添加项,请参阅下面的 环境 部分)。-w[orkdir] [<directory>]
- Erlang 模拟器的初始工作目录。默认为系统目录。-p[riority] [{low|high|realtime}]
- Erlang 模拟器的优先级。默认为 Windows 默认优先级。{-sn[ame] | -n[ame]} [<node-name>]
- Erlang 机器的节点名称。分布是强制性的。默认为-sname <服务名称>
。-d[ebugtype] [{new|reuse|console}]
- 指定将 shell 输出发送到何处。默认情况下,shell 输出会被丢弃。仅用于调试。-ar[gs] [<limited erl arguments>]
- Erlang 模拟器的额外参数。避免使用-noinput
、-noshell
和-sname
/-name
。默认情况下没有额外的参数。请记住,服务的 cookie 文件不一定与交互式用户的相同。该服务以本地管理员身份运行。将所有参数放在一个字符串中,使用双引号 (") 来指定包含空格的参数字符串,如果需要在参数字符串中指定引号,则使用转义引号 (\")。-i[nternalservicename] [<internal name>]
- *仅* 允许用于add
。指定服务的 Windows 内部服务名称,默认情况下,当添加新服务时,erlsrv
会将其设置为唯一的名称(以原始服务名称为前缀)。指定此项纯粹是表面上的操作,如果需要执行发布处理,则*不*建议这样做。内部服务名称一旦创建就无法更改。内部名称*不*要与普通的服务名称混淆,普通的服务名称是用于向erlsrv
标识服务的名称。-c[omment] [<short description>]
- 指定描述服务的文本注释。此注释在 Windows 服务管理器中显示为服务描述。
erlsrv {start | start_disabled | stop | disable | enable} <服务名称>
这些命令仅为方便起见而添加,操作服务状态的正常方法是通过控制面板的服务小程序。
start
和 stop
命令与服务管理器通信以启动和停止服务。这些命令会一直等待到服务启动或停止。禁用服务时,它不会停止,禁用状态在服务停止后才会生效。启用服务会将其设置为自动模式,该模式会在启动时启动。此命令无法将服务设置为手动模式。
start_disabled
命令对服务进行操作,而不管它是启用/禁用还是启动/停止。它首先启用它(无论是否已启用),然后启动它(如果尚未启动),然后禁用它。结果是一个已禁用但已启动的服务,而不管其之前的状态如何。这对于在发布升级期间临时启动服务很有用。使用 start_disabled
与序列 enable
、start
和 disable
之间的区别在于,在 start_disable
的操作序列期间,所有其他 erlsrv
命令都被锁定,使得从 erlsrv
用户的角度来看,该操作是原子的。
erlsrv remove <服务名称>
完全删除服务及其所有注册的选项。它会在删除之前停止。
erlsrv list [<服务名称>]
如果未指定服务名称,则会显示所有 Erlang 服务的简短列表。如果提供了服务名称,则会显示该服务的所有选项。
erlsrv help
显示简短的帮助文本。
环境
ERLSRV_SERVICE_NAME
- 启动该机器的服务名称。ERLSRV_EXECUTABLE
-erlsrv.exe
的完整路径,可用于操作该服务。在为您的服务定义心跳命令时,这非常方便。
重新启动服务的命令文件如下所示
@echo off
%ERLSRV_EXECUTABLE% stop %ERLSRV_SERVICE_NAME%
%ERLSRV_EXECUTABLE% start %ERLSRV_SERVICE_NAME%
然后将此命令文件设置为心跳命令。
环境变量还可用于检测我们是否作为服务运行,并使端口程序对注销时生成的控制事件做出正确反应(请参阅下一节)。
端口程序
当程序在服务上下文中运行时,它必须处理在交互式用户注销时发送给系统中每个程序的控制事件。对于在控制台子系统中运行的程序和作为窗口应用程序运行的程序,处理方式不同。在控制台子系统中运行的应用程序(端口程序的常见情况)使用 win32 函数 SetConsoleCtrlHandler
来注册一个控制处理程序,该处理程序在响应 CTRL_LOGOFF_EVENT
和 CTRL_SHUTDOWN_EVENT
事件时返回 true
。其他应用程序仅将 WM_ENDSESSION
和 WM_QUERYENDSESSION
转发到默认窗口过程。
以下是在 C 语言中设置控制台控制处理程序的简短示例
#include <windows.h>
/*
** A Console control handler that ignores the log off events,
** and lets the default handler take care of other events.
*/
BOOL WINAPI service_aware_handler(DWORD ctrl){
if(ctrl == CTRL_LOGOFF_EVENT)
return TRUE;
if(ctrl == CTRL_SHUTDOWN_EVENT)
return TRUE;
return FALSE;
}
void initialize_handler(void){
char buffer[2];
/*
* We assume we are running as a service if this
* environment variable is defined.
*/
if(GetEnvironmentVariable("ERLSRV_SERVICE_NAME",buffer,
(DWORD) 2)){
/*
** Actually set the control handler
*/
SetConsoleCtrlHandler(&service_aware_handler, TRUE);
}
}
说明
尽管选项以类似 Unix 的格式描述,但选项或命令的大小写并不相关,并且字符“/”和“-”都可以用于选项。
请注意,该程序位于模拟器的 bin
目录中,而不是直接位于 Erlang 根目录下的 bin
目录中。这样做的原因是,在运行的系统上升级模拟器时存在微妙的问题,在这种情况下,新版本的运行时系统不应覆盖现有的(可能正在使用的)可执行文件。
为了方便地操作 Erlang 服务,请将 <erlang_root>\erts-<version>\bin
目录添加到路径中,而不是 <erlang_root>\bin
。可以通过使用 os:find_executable/1
Erlang 函数从 Erlang 内部找到 erlsrv
程序。
为了使发布处理正常工作,请使用 start_erl
作为 Erlang 机器。如上文所述,服务名称非常重要。