用lazarus开发Linux daemon程序及编写启动脚步
2014-10-20 10:31
465 查看
用lazarus开发Linuxdaemon程序及编写启动脚步
在Linux C环境下编写daemon程序,需要遵循一些基本规则,以便防止产生并不需要的交互作用。比如其中有两条重要的规则。
第一,要调用fork,然后父进程退出。这样做有两个目的:
(1)如果该daemon进程是作为一条shell命令启动的,那么父进程终止使得shell认为这条命令已经执行完毕;
(2)子进程继承了父进程的进程组ID,但具有一个新的进程ID,这就保证了子进程不是一个进程组的组长进程。这对于之后要调用的setsid是必要的前提条件。
第二,调用setsid会创建一个新会话,调用进程成为新会话的首进程,并成为一个新进程组的组长,最重要的该调用导致进程没有了控制终端。
但使用lazarus开发daemon服务程序,不用这么麻烦,只需要建立daemon工程,在其上编写代码即可。
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目录中。
图1 安装lazdaemon
依次compile,use->install。安装lazdaemon包,会重新编译lazaruside,并自动重启ide。
在lazarus ide的file菜单中,点击new…,弹出界面如下图,如果出现Daemon(service)applications项目,说明安装成功。
图2 新建daemonservice应用程序
在图2的截图中,新建Daemon(service) application项目。新建后,界面如下图。
新建工程后,默认有两个单元文件,分别是daemonMapperunit1.pas和daemonunit1.pas文件,当然还有其相应的窗体文件。
编写daemonunit1.pas单元文件内容如下:
在这个例子中,DaemonMapperunit1.pas单元不用写任何代码,但需要设置其窗体属性。点击daemonMapper1窗体,在左边的objectinspector窗口中显示该窗体的属性列表。
点击daemonDefs属性后面的省略号,弹出一个编辑窗口,新建一个条目,并设置属性如下,其中DaemonClassName属性要和daemonunit1.pas单元中的继承于TDaemon类的类名相同。
设置好,就可以编译该程序了。但是,在linux下编译多线程程序时,需要在工程单元文件中,注释掉一个条件宏,否则该程序不能正常运行。这应该是lazarus ide的一个bug。代码如下。
这样,就可以编译并测试了。
要想在Linux开机时自动启动该程序,还需要配置启动脚本。由于不同的系统有不同的配置方法,我们先看看系统版本再配置启动脚本,下面以两个真实系统为例。
lsb_release cat /proc/version cat/etc/issue cat /etc/redhat-release
在fedora系统中执行各命令如下。
在redhat6.3系统中执行各命令如下。
把该脚本放在/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文件。
把该脚本保存为SVNServerAgent.service文件,并放到/lib/systemd/system目录里。用systemctl enable SVNServerAgent.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
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. |
点击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]}-eq 0]; 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]}-eq 0]; 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 $? |
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 |
服务启停和状态
使用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
相关文章推荐
- linux下编写daemon程序步骤
- 编写Linux系统下Daemon程序的方法步骤
- 编写Linux系统下Daemon程序的方法步骤
- 编写Linux系统下Daemon程序的方法步骤
- Linux下启动Java程序的启动脚本编写
- 在Redhat9 Linux下安装java开发环境,并编写“Hello world”程序
- linux 开机启动自动执行某用户的脚步、程序
- 让Java程序作为linux的Daemon后台运行 和 使用Java Service Wrapper将java程序作为linux服务并且开机自动启动
- Linux平台下启动java程序的脚本编写
- Linux下apache服务器安装,sqlite安装,apache启动,关闭,重启,编写cig程序进行测试,浏览器访问cig程序
- linux下电话开发程序编写及运行思路
- Android For JNI(一)——JNI的概念以及C语言开发工具dev-c++,编写你的第一个C语言程序,使用C启动JAVA程序
- 编写Linux系统下Daemon程序的方法步骤
- linux环境下编写shell脚本启动和关闭jar包服务程序
- 在Linux下编写Daemon(Linux启动流程2)
- 编写Linux系统下Daemon程序的方法步骤
- 将java工程 程序 添加到 linux 服务 daemon 开机启动
- 编写Linux系统下Daemon程序的方法步骤
- Android For JNI(一)——JNI的概念以及C语言开发工具dev-c++,编写你的第一个C语言程序,使用C启动JAVA程序
- C/C++编译器和开发库(linux下编写程序)