我有大约十几个基于 cygwin 的 Windows 服务,它们依赖于 Windows 启动期间启动的应用程序的状态(不是作为服务,因为它需要与用户交互)。在应用程序达到特定的所需状态之前,这些服务不应启动。
我编写了一个小程序(我们称之为
app-ready
),它定期检查应用程序的状态。现在我希望这个程序作为上述服务所依赖的服务运行,以便Windows在这个app-ready
服务表明应用程序已达到所需状态之前不会启动依赖的服务。
执行此操作的一般方法是,只要我们等待应用程序达到所需状态,就将
app-ready
服务保持在 SERVICE_START_PENDING
状态 - 当这种情况发生时,app-ready
服务应该从 转换SERVICE_START_PENDING
至 SERVICE_RUNNING
。这向 SCM 表明相关服务现在可以启动。
但是,
cygrunsrv
在单独的线程中执行所需的应用程序,并自行处理所有SCM回调和状态报告——我们的应用程序app-ready
没有机会挂钩这些,因此无法显式控制服务状态.
有没有办法使用
cygrunsrv
来管理基于cygwin的Windows服务,以便我们可以控制服务何时报告为SERVICE_RUNNING
?
事实证明,有一种巧妙的方法可以滥用
cygrunsrv
的功能来完成我们想要的事情。
cygrunsrv
支持此选项:
-x, --pidfile <file> Optional path for .pid file written by application
after fork().
Default is that application must not fork().
cygrunsrv
来源的帮助下可以看出,使用此选项,cygrunsrv
切换到特殊模式,等待实际启动的程序终止,并等待pidfile
更新其内容(或被创建)。然后,这个新进程将作为基于 cygwin 的 Windows 服务背后的实际进程进行监视。
在最初启动的程序(使用
--pidfile
选项)仍在运行的时间段内,cygrunsrv
通过 syslog
记录此信息:
...app-ready: PID 5000: service `app-ready': waiting for fork of 5001 (#1)
...app-ready: PID 5000: service `app-ready': waiting for fork of 5001 (#2)
...
并在启动的程序终止后切换到以下内容:
...app-ready: PID 5000: service `app-ready': waiting for file /tmp/app-ready.pid (#6)
...app-ready: PID 5000: service `app-ready': waiting for file /tmp/app-ready.pid (#7)
...
直到pid文件写入,然后报告:
...app-ready: PID 5000: service `app-ready' started, pid 5002 read from /tmp/app-ready.pid
此行为可以(误)用于在守护子进程(然后必须无限期运行)之前简单地等待满足任意条件,这将实现我们想要的行为。
可用作
app-ready
服务的示例脚本:
#!/bin/bash -eu
sleep 5 # wait for some condition to become fulfilled
(
echo $BASHPID > /tmp/app-ready.pid # do this when finally ready (NB: $$ is the top-level PID)
tail -f /dev/null # run forever -- or periodically check the condition and terminate
) & # background the long-running child process
# top-level exits normally immediately
像这样安装:
cygrunsrv --install app-ready \
--user $USER \
--passwd $PASSWD \
--path /bin/bash \
--args '-c ~/bin/app-ready' \
--type auto \
--pidfile /tmp/app-ready.pid \
--timeout 600 \
--neverexits
现在,服务将在 5 秒睡眠期间显示为“正在启动”,并在 PID 写入 pidfile 后显示为已启动。这允许我们在启动服务之后但在它显示为已启动之前执行任意工作/等待。
--timeout 600
选项允许我们延长默认的最长30秒,cygrunsrv
等待fork和写入pidfile。
如果没有
--neverexits
选项,如果我们的服务终止,cygrunsrv
将向 SCM 报告 预期 服务停止。使用此选项,退出被视为失败,cygrunsrv
终止而不将SERVICE_STOPPED
发送到SCM。反过来,SCM 也将此视为故障并触发为服务配置的任何恢复操作
-- 这可能很有用,具体取决于用例。例如,简单的服务重启可能是有意义的,这将使服务再次处于“正在启动”状态,直到第二次满足条件。