用lazarus开发Linux daemon程序及编写启动脚步
来源:互联网 发布:加内特生涯场均数据 编辑:程序博客网 时间:2024/05/16 13:41
用lazarus开发Linuxdaemon程序及编写启动脚步
Linux Daemon程序简介
Linux Daemon程序(守护进程或精灵进程)是生存期较长的一种进程。它们常常在系统自举时启动,仅当系统关闭时才终止。所以它们没有控制终端,在后台运行。
在Linux C环境下编写daemon程序,需要遵循一些基本规则,以便防止产生并不需要的交互作用。比如其中有两条重要的规则。
第一,要调用fork,然后父进程退出。这样做有两个目的:
(1)如果该daemon进程是作为一条shell命令启动的,那么父进程终止使得shell认为这条命令已经执行完毕;
(2)子进程继承了父进程的进程组ID,但具有一个新的进程ID,这就保证了子进程不是一个进程组的组长进程。这对于之后要调用的setsid是必要的前提条件。
第二,调用setsid会创建一个新会话,调用进程成为新会话的首进程,并成为一个新进程组的组长,最重要的该调用导致进程没有了控制终端。
但使用lazarus开发daemon服务程序,不用这么麻烦,只需要建立daemon工程,在其上编写代码即可。
Lazarus下开发daemon程序
安装lazarus
根据自己的系统类型,从 https://sourceforge.net/projects/lazarus/files/下载合适的rpm安装包到本地目录,包括如下三个文件:
fpc-2.6.2-0.laz.i686.rpm
fpc-src-2.6.2-0.laz.i686.rpm
lazarus-1.0.8-0.i686.rpm
命令行,进入下载目录,安装:
Rpm -ivh *.rpm
Lazarus在Linux系统默认安装在/usr/lib/lazarus目录中。
安装lazdaemon开发包
在lazarus ide的package->openpackage file(.lpk)菜单打开/usr/lib/lazarus/components/daemon/lazdaemon.lpk工程,如图:
图1 安装lazdaemon
依次compile,use->install。安装lazdaemon包,会重新编译lazaruside,并自动重启ide。
在lazarus ide的file菜单中,点击new…,弹出界面如下图,如果出现Daemon(service)applications项目,说明安装成功。
图2 新建daemonservice应用程序
Daemon例子
该例子以定期向一个注册服务器发送注册信息为例,注册服务器的代码这里没有给出,该例子主要是为了说明daemon程序如何编写。
在图2的截图中,新建Daemon(service) application项目。新建后,界面如下图。
新建工程后,默认有两个单元文件,分别是daemonMapperunit1.pas和daemonunit1.pas文件,当然还有其相应的窗体文件。
编写daemonunit1.pas单元文件内容如下:
unitDaemonUnit1;
{$mode objfpc}{$H+}
interface
uses
Classes,SysUtils,FileUtil,DaemonApp,sockets,eventlog;
type
{ TRegThread }
TRegThread=class(TThread)
private
FLog:TEventLog;
procedureDoLog(Msg:String);
public
ConstructorCreate(ALog:TEventLog);
ProcedureExecute;override;
end;
{ TDaemon1 }
TDaemon1=class(TDaemon)
procedureDataModuleStart(Sender:TCustomDaemon;varOK:Boolean);
private
{ private declarations }
FLog:TEventLog;
FThread:TRegThread;
procedureStartLog;
public
{ public declarations }
end;
var
Daemon1:TDaemon1;
implementation
procedureRegisterDaemon;
begin
RegisterDaemonClass(TDaemon1)
end;
{$R *.lfm}
{ TRegThread }
procedureTRegThread.DoLog(Msg:String);
begin
IfAssigned(FLog)then
FLog.Info(Msg);
end;
constructorTRegThread.Create(ALog:TEventLog);
begin
FLog:=Alog;
InheritedCreate(False);
end;
procedureTRegThread.Execute;
var
buf:string;
SAddr:TInetSockAddr;
s:Longint;
sin,sout:text;
inaddr:in_addr;
i:integer;
const
CRLF= #13#10;
begin
whiletruedo
begin
dolog('heart frame start...');
S:=fpSocket(AF_INET,SOCK_STREAM,0);
ifs=-1then
begin
dolog('fpsocket error:'+inttostr(socketerror));
exit;
end;
SAddr.sin_family:=AF_INET;
SAddr.sin_port:=htons(10001);
SAddr.sin_addr.s_addr:=hosttonet((10shl24)or(11shl16)or(32shl8)or(59));
ifnotconnect(s,SAddr,Sin,Sout)then
begin
dolog('connect error:'+inttostr(socketerror));
continue;
end;
reset(sin);
rewrite(sout);
begin
buf:=Char($FF);
buf:=buf+ 'cxqTest'+CRLF+ '10000'+CRLF;
buf:=buf+ 'svn';
buf:=buf+CRLF+ '1.0';
writeln(sout,buf);
end;
dolog(buf);
flush(sout);
close(sout);
closesocket(s);
sleep(10000);
end;
end;
{ TDaemon1 }
procedureTDaemon1.DataModuleStart(Sender:TCustomDaemon;varOK:Boolean);
begin
sleep(10000);
startlog;
FThread:=TRegThread.Create(flog);
end;
procedureTDaemon1.StartLog;
begin
FLog:=Self.Logger;
//FLog.FileName:= 'cxqcxq.txt';
//flog.LogType:=ltfile;
end;
initialization
RegisterDaemon;
end.
在这个例子中,DaemonMapperunit1.pas单元不用写任何代码,但需要设置其窗体属性。点击daemonMapper1窗体,在左边的objectinspector窗口中显示该窗体的属性列表。
点击daemonDefs属性后面的省略号,弹出一个编辑窗口,新建一个条目,并设置属性如下,其中DaemonClassName属性要和daemonunit1.pas单元中的继承于TDaemon类的类名相同。
设置好,就可以编译该程序了。但是,在linux下编译多线程程序时,需要在工程单元文件中,注释掉一个条件宏,否则该程序不能正常运行。这应该是lazarus ide的一个bug。代码如下。
Programproject1;
Uses
{$IFDEF UNIX}//{$IFDEF UseCThreads}
CThreads,
{$ENDIF}//{$ENDIF}
DaemonApp,lazdaemonapp,DaemonMapperUnit1,DaemonUnit1
{ add your units here };
begin
Application.Initialize;
Application.Run;
end.
这样,就可以编译并测试了。
手工运行daemon测试
在命令行下进入源程序目录,假设编译的程序名称是project1,执行project1 –r。如果想在后台执行,执行 project1 –r &。
要想在Linux开机时自动启动该程序,还需要配置启动脚本。由于不同的系统有不同的配置方法,我们先看看系统版本再配置启动脚本,下面以两个真实系统为例。
查看Linux版本命令
可用如下命令:
lsb_release cat /proc/version cat/etc/issue cat /etc/redhat-release
在fedora系统中执行各命令如下。
在redhat6.3系统中执行各命令如下。
启动脚本
在redhat Linux系统中,根据启动级别,执行不同集合的启动脚本。各级别的启动脚本分别放在rcN.d目录中,init进程会根据用户配置的启动等级,启动相应rcN.d目录下的脚本。在rcN.d目录中的脚本都是符号链接,真正的脚本放在/etc/rc.d/init.d/目录中。符号连接名称体现出是启动还是停止,以及优先级。所以,如果要想在开机启动自己的程序,需要在/etc/rc.d/init.d/目录中放启动脚本,并在各rcN.d目录中放符号连接。比如Linux代理程序SVNServerService程序,该程序放在/home/ftp/incoming/linux目录中,编写对应的控制脚本如下:
#!/bin/bash
#
# SVNServerAgent Startup script for the SVNServerAgent Server
#
# chkconfig: 2345 85 15
# description: The SVNServerAgent Server is an efficient and extensible \
# server implementing the SVNServer cmd Agent.
# processname: SVNServerService
#
### BEGIN INIT INFO
# Provides: SVNServerAgent
# Required-Start: $local_fs $remote_fs $network $named
# Required-Stop: $local_fs $remote_fs $network
# Should-Start: distcache
# Short-Description: start and stop SVNServerAgent Server
# Description: The Apache HTTP Server is an extensible server
# implementing the SVNServer cmd Agent.
### END INIT INFO
SVC_START_OPTIONS="-r"
SVC_STOP_OPTIONS="s"
# Edit SVC_ALIAS to the long description of your service application
SVC_ALIAS="SVNServerAgent server"
# Edit SVC_FILENAME to the actual name of your compiled service application
SVC_FILENAME="SVNServerService"
# Edit SVC_DIR to where you place your compiled service application
SVC_DIR="/home/ftp/incoming/linux/"
# Edit SVC_SERVICE_SCRIPT to the name of this file without the extension
SVC_SERVICE_SCRIPT="SVNServerAgent"
# this will become your service name. Ie.) service YourService start
SVC_FILE=$SVC_DIR$SVC_FILENAME
start(){
if [ -f $SVC_FILE ]; then
#reset
echo -n"Starting "$SVC_ALIAS": "
RETVALS=$(start-stop-daemon -S -b -x $SVC_FILE -- $SVC_START_OPTIONS)
Count=${#RETVALS[@]}
RETVAL="[FAIL]"
if [ $Count -eq 0 ];then
RETVAL="[OK]"
elif [ $Count -eq 1 ];then
if [ ${#RETVALS[0]}-eq0]; then
RETVAL="[OK]"
else
iStart=${#SVC_FILE}
iLength=${#RETVALS[0]}
Response=${RETVALS[0]:(iStart+1):7}
RETVAL=$Response
if [ "$Response" =="already"];then
RETVAL="[OK]"
fi
fi
fi
echo $RETVAL
return0
else
echo $SVC_ALIAS" not installed" $SVC_DIR
exit 2;
fi
}
stop(){
echo -n "Shutting down "$SVC_ALIAS":"
RETVALS=$(start-stop-daemon -K -x $SVC_FILE -- $SVC_STOP_OPTIONS)
#additional PROCKILLS=$(killall -w -q -e $SVC_PROCESS_NAME $SVC_FILENAME)
Count=${#RETVALS[@]}
Index=0
RETVAL="[FAIL]"
if [ $Count -eq 1 ];then
if [ ${#RETVALS[0]}-eq0]; then
RETVAL="[OK]"
else
Response=${RETVALS[0]:0:2}
RETVAL=$Response
if["$Response"=="No"];then
RETVAL="[OK]"
fi
fi
else
RETVAL="[OK]"
fi
echo $RETVAL
return 0
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status $SVC_SERVICE_SCRIPT
;;
restart)
stop
start
;;
*)
echo $SVC_ALIAS" [Invalid Startup Parameters]"
echo "Usage: {start|stop|status|restart}"
exit 1
;;
esac
exit $?
把该脚本放在/etc/rc.d/init.d/目录中,用chkconfig命令工具在各rcN.d目录中生成对应的启动和停止脚步符号连接。注意,用chkconfig工具管理该脚本,需要在脚本的头部按照指定格式编写注释。其中代码的第6行,分表表示启动的级别,启动优先级,停止优先级。
Chkconfig –add命令可以增加管理的脚本。Chkconfig根据脚本第6行注释在各rcN.d目录中生成对应的符号连接文件,详细见下图命令结果。
从上图中可以看到,chkconfig命令在rc2,rc3,rc4,rc5目录中生成了启动脚本,以S(start)开头,之后是启动等级,最后是脚本名称;除此,还在其他的rcN.d目录中生成了以K开头的符号连接,这表示kill。手工也可以完成该任务,但用chkconfig工具更方便。
在fedora中,用systemctl控制脚本的执行启动,该脚本是个ini文件。
[Unit]
Description=SVNServer
After=network.target
[Service]
Type=simple
ExecStart=/home/ftp/incoming/linux/SVNServerService -r
RemainAfterExit=yes
TimeoutSec=25
[Install]
WantedBy=multi-user.target
把该脚本保存为SVNServerAgent.service文件,并放到/lib/systemd/system目录里。用systemctl enable SVNServerAgent.service命令激活该脚步在启动时执行。
服务启停和状态
使用service脚本命令或systemctl控制服务启停和查看状态。Service最终调用的是服务程序本身+参数方式。
Service serviceName start
Service serviceName stop
Service serviceName status
以及
Systemctl status serverName
Systemctl enable serverName(使脚本在系统启动时执行)
Systemctl add serverName
查看状态:
Ps –axj(查看所有无终端的进程)
lsof -Pln +M -i4
- 用lazarus开发Linux daemon程序及编写启动脚步
- linux下编写daemon程序步骤
- 在Linux下编写Daemon(Linux启动流程2)
- Linux Daemon的编写
- 编写Linux系统下Daemon程序的方法步骤
- 编写Linux系统下Daemon程序的方法步骤
- 编写Linux系统下Daemon程序的方法步骤
- 编写Linux系统下Daemon程序的方法步骤
- ubuntu14下nginx自启动脚步编写
- linux下电话开发程序编写及运行思路
- linux实现daemon程序
- 用Lazarus编写第一个程序Pascal版的hello world
- 在Linux下编写Daemon
- 在Linux下编写Daemon
- 在Linux下编写Daemon
- 在Linux下编写Daemon
- 在Linux下编写Daemon
- 在Linux下编写Daemon
- BPM Process Instances – Faults, Rollback & Recovery – Part 2
- 面向对象软件工程概念模型
- 行了!!!!
- ubuntu 下命令行播放器mplayer 使用详解
- 批量修改文件名
- 用lazarus开发Linux daemon程序及编写启动脚步
- BPM Process Instances – Faults, Rollback & Recovery – Part 3
- linux C语言 select函数的用法
- Creating Apps With Material Design —— Working with Drawables
- iOS 7.1 Requires Ad-Hoc Installs To Be Over HTTPS
- Gabor金字塔在视觉注意模型研究中的应用(三)--冲刺篇
- BPM Process Instances – Faults, Rollback & Recovery – Part 4
- --cocos2dx场景切换--
- Sublime Text 2 快捷键用法大全