上一篇文章中介绍了如何在 Delphi 中创建 Linux 守护进程,而这篇文章则试图将上文的操作简单化,不要再去考虑那么多事事非非。
【基础入门】
1、引入 qconsole_looper.pas 单元,这个单元本身原来是为了能够在控制台程序中运行主线程函数而引入的,换句话说,引用它后可以保证 QWorker 在控制台程序中正常运行。同时,它加入了等待循环,这样就像 Application.Run 一样,具体的实现请自行参考相关的实现代码。当然了,控制台程序在等待输入时,由于 ReadLn 等函数是阻塞的,所以对应的主线程调用会被挂起,在使用时要注意。当然,最后就是加入了对 POSIX 系的信号处理和守护进程的相关属性支持。
2、如果要创建守护进程,执行下面的代码:
TQConsoleLooper.Current.IsDaemon := true;
如果要为守护进程指定一个 PID 信息文件(一般这个在 Linux 等系统中用于获取当前服务的主进程ID,以便结束服务进程),则设置 PidFile 为对应的文件的完整路径,必需确保对应的目录有写入权限。
TQConsoleLooper.Current.PidFile :='/var/qdac/test.pid';
3、调用 TQConsoleLooper.Current.Run 启动控制台循环。
完成上述基本过程后,一个简单的控制台服务就可以了。当你在系统中打开程序时,就会发现程序创建了子服务进程后,直接退出了。
【POSIX 信号响应】
默认情况下,程序在收到 QUIT 、 ABRT、TERM 信号时,也会退出程序,如果您想自己控制信号的行为,响应 OnSignal 事件。在 OnSignal 事件中,如果设置 AHandled 参数为 true,就不会触发默认操作。
如果要响应自定义的事件,首先要调用 HandleSignal 传入自己要响应的信号ID,然后在 OnSignal 中就可以得到具体的信号响应。
【服务安装与卸载】
TQConsoleLooper 在 Linux 上提供了服务的安装和卸载功能。用户运行时,通过命令行参数 -install 安装服务,-uninstall 来卸载服务。如果要做为服务,你需要为其提供以下额外属性的值:
- Name :服务的名称,在 Linux 中 systemctl 或 service 对服务控制时需要。
- Title : 服务的标题描述,用来说明服务是干什么的,注册服务时会对应于服务描述
- BeforeInstall/AfterInstall:如果你需要为服务安装添加额外的属性控制,比如依赖关系等,需要在 BeforeInstall 里对参数进行控制。当服务安装完成启动后,会触发 AfterInstall 事件。
- BeforeUninstall/AfterUninstall:卸载服务时触发,默认卸载只是停止服务,并且删除服务注册单元自身,你需要额外处理以删除服务运行过程中产生的其它文件。
【额外的事件及属性】
1、 如果在外部要结束进程,可以向进程发送 KILL/QUIT/ABRT 信号(前提未重载)。 如果程序内结束自身,则调用 Terminate 方法,该方法能够让程序“优雅”的退出
2、在用户调用 Terminate 时,Terminated 属性会被设置为 true。
3、TerminateOnException 属性用于在发生未捕获的异常时,自动中止程序的执行。
4、BeforeRun 事件在调用 Run 执行相关代码之前触发。它是 Run 函数的第一句代码。
5、OnTerminate 事件在应用即将退出时触发。
6、DaemonProcessId 是守护子进程的进程ID,如果当前进程是初始化创建的父进程则返回子进程的ID,否则,返回当前进程ID。如果 IsDaemon 属性不为 true,则始终返回 0。
7、Current 类属性,该属性用来访问全局唯一的 TQConsoleLooper 实例。
【多线程守护进程】
由于守护进程是通过 fork 模式创建的。而 fork 的限制要求当前父进程处于单线程模式,所以必需保证fork执行时,没有额外的线程在执行。QLog/QWorker 已经针对这种需求做了针对性的调整,但仍要保证不要注册时提交作业,否则守护进程里这些线程都将失效。为了避免这种情况,用户自己的守护对象类型,应继承自 TQBaseConsoleWorker 并且调用 RegisterWorker 注册用户自己的类型。用户自己的类型在对应的 Startup 完成初始化,在 Cleanup 里完成清理。TQConsoleLooper 的 Run 方法将会触发它们。
【其它】
没想到,有啥补充的将来再说。