基于ACE的后台服务程序的实现
来源:互联网 发布:java小程序抽签源代码 编辑:程序博客网 时间:2024/05/17 23:50
后台服务进程不属于任何一个终端会话,当然也就不用和任何用户交互,许多系统服务由后台服务进程实施;如网络服务,打印等。Windows和LINUX在实现后台服务进程上并不统一,Windows定义的名称为SERVICE而Linux上的名称为Daemon。对应到ACE上,对于Linux平台ACE提供了一个名称为ACE::daemonize()的静态方法,进程通过调用该方法即可成为后台服务进程;而对于Windows平台,ACE提供的是一个名称为 ACE_NT_Service的类,通过调用该类的一系列方法也可成为一个后台服务进程。下面分别讨论ACE::daemonize()及 ACE_NT_Service在ACE中的实现。
一、Linux下的后台服务程序
在Linux操作系统下实现后台服务程序用到ACE::daemonize()函数,该函数的实现在ACE.cpp文件中,对于Linux平台下生成daemon进程的完整步骤共7个,ACE在实现时有一步没有做。
/**
* Become a daemon process using thealgorithm in Richard Stevens
* "Advanced Programming in theUNIX Environment." If
* @a close_all_handles is non-zerothen all open file handles are
* closed.
*/
externACE_Export int daemonize (
const ACE_TCHAR pathname[] =ACE_TEXT ("/"),
bool close_all_handles =ACE_DEFAULT_CLOSE_ALL_HANDLES,
const ACE_TCHARprogram_name[] = ACE_TEXT ("<unknown>"));
该函数的具体实现如下:
int ACE::daemonize (const ACE_TCHAR pathname[],bool close_all_handles,const ACE_TCHAR program_name[])
{
ACE_TRACE ("ACE::daemonize");
#if !defined (ACE_LACKS_FORK)
pid_t pid = ACE_OS::fork ();
if (pid == -1)
return -1;
else if (pid != 0)
ACE_OS::exit (0); // Parent exits.
// 1st child continues.
ACE_OS::setsid (); // Become session leader.
ACE_OS::signal (SIGHUP, SIG_IGN);
pid = ACE_OS::fork (program_name);
if (pid != 0)
ACE_OS::exit (0); // First child terminates.
// Second child continues.
if (pathname != 0)
// change working directory.
ACE_OS::chdir (pathname);
ACE_OS::umask (0); // clear our file mode creation mask.
// Close down the I/O handles.
if (close_all_handles)
{
for (int i = ACE::max_handles () - 1; i >= 0; i--)
ACE_OS::close (i);
int fd = ACE_OS::open ("/dev/null", O_RDWR, 0);
if (fd != -1)
{
ACE_OS::dup2 (fd, ACE_STDIN);
ACE_OS::dup2 (fd, ACE_STDOUT);
ACE_OS::dup2 (fd, ACE_STDERR);
if (fd > ACE_STDERR)
ACE_OS::close (fd);
}
}
return 0;
#else
ACE_UNUSED_ARG (pathname);
ACE_UNUSED_ARG (close_all_handles);
ACE_UNUSED_ARG (program_name);
ACE_NOTSUP_RETURN (-1);
#endif /* ACE_LACKS_FORK */
}
下面结合这些步骤以及ACE的实现源码分别说明:
1、fork出新的服务进程以便父进程退出,这样可以确保子进程不是进程组的组长
pid_t pid = ACE_OS::fork ();
if (pid == -1)
return -1;
else if (pid != 0)
ACE_OS::exit (0);
2、使用SETSID创建会话并设置子进程为进程组组长,该子进程不存在关联的控制终端
ACE_OS::setsid ();
ACE_OS::signal (SIGHUP, SIG_IGN);
3、再次Fork新的服务进程,该进程不是进程组的组长并且永远不能再次获取控制终端
pid = ACE_OS::fork (program_name);
if (pid != 0)
ACE_OS::exit (0);
4、改变当前文件系统目录,因为不做这个会导致系统管理员不能卸装(umount)一个文件系统
if (pathname != 0)
ACE_OS::chdir (pathname);
5、重新设置文件的访问属性,以便我们拥有对于我们写的任何东西的完全控制
ACE_OS::umask (0);
6、关闭所有的文件句柄
if (close_all_handles)
for (int i = ACE::max_handles () - 1; i >= 0; i--)
ACE_OS::close (i);
return 0;
另外,出于安全以及健壮性考虑,即使当前进程不使用stdin、stdout、stderr,也应重新打开0、1、2三个句柄,使之对应/dev/null。但是不知道什么原因ACE的实现代码中并没有该步骤。
二、Windows下的后台服务程序
1、Windows NT 服务程序的四个组成部分
1)、控制台应用程序的main函数
包括服务信息表的定义、调用系统函数 StartServiceCtrlDispatcher 连接程序主线程到服务控制管理程序。
2)、服务入口函数ServiceMain
执行服务初始化任务、服务的循环等。
3)、控制服务处理程序函数ServiceCtrlHandler
SCM(服务管理器)利用该函数和服务通信并控制程序的起停等。
4)、在系统运行此服务之前,需要先安装InstallService;服务不再需要时还涉及到服务的卸载UnstallService。
/***说明
服务程序的模板:一个服务程序必须有两个函数,一个是服务程序的主
函数ServiceMain(),另一个是服务程序的派遣函数ServiceHandler(),
它负责处理外部控制消息。
*/
#include "stdafx.h"
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;
int main(int argc, _TCHAR* argv[])
{
//服务信息表
SERVICE_TABLE_ENTRY ServiceTable[2];
ServiceTable[0].lpServiceName=SRV_NAME;
ServiceTable[0].lpServiceProc=(LPSERVICE_MAIN_FUNCTION)ServiceMain;
ServiceTable[1].lpServiceName=NULL;
ServiceTable[1].lpServiceProc=NULL;
StartServiceCtrlDispatcher(ServiceTable);
return 0;
}
void WINAPI ServiceMain( DWORD dwArgc, LPTSTR *lpszArgv)
{
DWORD errorCode = 0;
DWORD specificError = 0xfffffff;
ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
hStatus=RegisterServiceCtrlHandler(SRV_NAME,(LPHANDLER_FUNCTION)ServiceCtrlHandler);
if (hStatus==0)
return;
// Handle error condition
errorCode = GetLastError();
if (errorCode!=NO_ERROR)
{
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
ServiceStatus.dwWin32ExitCode = errorCode;
ServiceStatus.dwServiceSpecificExitCode = specificError;
SetServiceStatus(hStatus, &ServiceStatus);
return;
}
// 初始化结束,报告运行状态
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
SetServiceStatus(hStatus, &ServiceStatus);
//在这里做其他的工作
DWORD ret=MainWork(dwArgc,lpszArgv); //MainWork()函数为服务执行的函数,为一个循环。
if(0!=ret)
{//MainWork异常退出
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
ServiceStatus.dwServiceSpecificExitCode=S_FALSE;
SetServiceStatus(hStatus,&ServiceStatus);
return;
}
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode=S_OK;
SetServiceStatus(hStatus, &ServiceStatus);
}
void WINAPI ServiceCtrlHandler( DWORD fdwControl)
{
switch(fdwControl)
{
case SERVICE_CONTROL_PAUSE:
ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
SetServiceStatus(hStatus,&ServiceStatus);
return ;
case SERVICE_CONTROL_INTERROGATE:
break;
default:
break;
}
SetServiceStatus(hStatus,&ServiceStatus);
return ;
}
服务的安装和卸载如下:
//安装服务
BOOL InstallService()
{
SC_HANDLE hScMgr=NULL;
SC_HANDLE hService=NULL;
hScMgr=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);
if(hScMgr==NULL)
{
printf("OpenSCManager() Error\n");
return FALSE;
}
hService=CreateService(hScMgr,SRV_NAME,
SRV_INFO,SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL,
SRV_PATH,0,0,0,0,0);
if(hService!=NULL)
printf("Create a new service successful!\n");
else
{
printf("Failed to create a new service! Error:OpenService() \n");
CloseServiceHandle(hScMgr);
return FALSE;
}
CloseServiceHandle(hScMgr);
CloseServiceHandle(hService);
return TRUE;
}
//卸载服务
BOOL UnstallService()
{
SC_HANDLE hScMgr,hService;
SERVICE_STATUS status;
hScMgr=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);
if(NULL==hScMgr)
{
printf("OpenSCManager() Error\n");
return FALSE;
}
hService=OpenService(hScMgr,SRV_NAME,SERVICE_ALL_ACCESS|DELETE);
if(NULL==hService)
{
printf("OpenService() Error\n");
return FALSE;
}
//查询服务状态
QueryServiceStatus(hService,&status);
//如果服务未停止,先停止它
if(status.dwCurrentState!=SERVICE_STOPPED)
{
ControlService(hService,SERVICE_CONTROL_STOP,&status);
Sleep(500);
}
BOOL bSuccess=DeleteService(hService);
if(bSuccess)
printf("Delete service successful!\n");
else
{
printf("Failed to delete service!\n");
return FALSE;
}
return TRUE;
}
2、基于ACE的Windows NT 服务程序
ACE_NT_Service类封装了Windows下的服务程序的实现框架,让开发者完全可以采用面向对象的思想来实现Windows服务程序。在ACE的源码包目录下(ACE_wrappers\examples\NT_Service)有一个示例程序,很好的讲述了一个服务程序的示例。基于ACE的Windows服务程序的实现包括如下几步:
1)、编写一个服务运行的类,该类继承自ACE_NT_Service,例如这里为MainNTService;
#ifndef _MAIN_NT_SVC_H_
#define _MAIN_NT_SVC_H_
#include "ace/NT_Service.h"
#include "ace/Singleton.h"
#include "ace/Mutex.h"
class MainNTService : public ACE_NT_Service
{
public:
MainNTService (void):m_running(false){}
~MainNTService (void){}
virtual void handle_control (DWORD control_code);
virtual int svc (void);
private:
bool m_running;
};
typedef ACE_Singleton<MainNTService, ACE_Mutex> SERVICE;
#endif /* #ifndef _MAIN_NT_SVC_H_ */
2)、实现父类的如下两个虚函数;
virtual voidhandle_control (DWORD control_code);
virtual int svc(void); //服务运行的主体函数
#include "main_service.h"
#include "ace/OS_NS_unistd.h"
void MainNTService::handle_control (DWORD control_code)
{
if (control_code == SERVICE_CONTROL_SHUTDOWN
|| control_code == SERVICE_CONTROL_STOP)
{
report_status (SERVICE_STOP_PENDING);
m_running=false; //告之svc()中的while循环结束
}
ACE_NT_Service::handle_control (control_code);
}
int MainNTService::svc (void)
{
m_running=true;
ACE_DEBUG ((LM_DEBUG,ACE_TEXT ("Service::svc\n")));
report_status (SERVICE_RUNNING);
while(m_running) //服务的核心循环体
{
ACE_OS::sleep(1);
}
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Shutting down\n")));
return 0;
}
3)、为了便于服务的安装、卸载、启动、停止、设置运行模式等,编写参数选项类,例如为WinNTProcess;
#include "ace/Get_Opt.h"
#include "ace/Init_ACE.h"
#include "MainNTService.h"
// Default for the -i (install) option
#define DEFAULT_SERVICE_INIT_STARTUP SERVICE_AUTO_START
class WinNTProcess
{
public:
WinNTProcess (void);
~WinNTProcess (void);
int run(int argc, ACE_TCHAR* argv[]);
private:
void parse_args (int argc,ACE_TCHAR* argv[]);
void print_usage_and_die (void);
private:
char progname[128];
int opt_install;
int opt_remove;
int opt_start;
int opt_kill;
int opt_type;
int opt_debug;
int opt_startup;
};
typedef ACE_Singleton<WinNTProcess, ACE_Mutex> PROCESS;
WinNTProcess::WinNTProcess (void)
: opt_install (0), opt_remove (0),
opt_start (0), opt_kill (0),
opt_type (0), opt_debug (0),
opt_startup (0)
{
ACE_OS::strcpy (progname, "service");
ACE::init ();
}
WinNTProcess::~WinNTProcess (void)
{
ACE::fini ();
}
void
WinNTProcess::print_usage_and_die (void)
{
ACE_DEBUG ((LM_INFO,
"Usage: %s"
" -in -r -s -k -tn -d\n"
" -i: Install this program as an NT service, with specified startup\n"
" -r: Remove this program from the Service Manager\n"
" -s: Start the service\n"
" -k: Kill the service\n"
" -t: Set startup for an existing service\n"
" -d: Debug; run as a regular application\n",
progname,
0));
ACE_OS::exit(1);
}
void WinNTProcess::parse_args (int argc, ACE_TCHAR* argv[])
{
ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("i:rskt:d"));
int c;
while ((c = get_opt ()) != -1)
switch (c)
{
case 'i': //安装
opt_install = 1;
opt_startup = ACE_OS::atoi (get_opt.opt_arg ());
if (opt_startup <= 0)
print_usage_and_die ();
break;
case 'r': //卸载
opt_remove = 1;
break;
case 's'://启动
opt_start = 1;
break;
case 'k'://停止
opt_kill = 1;
break;
case 't'://设置启动类型:自动、手动等
opt_type = 1;
opt_startup = ACE_OS::atoi (get_opt.opt_arg ());
if (opt_startup <= 0)
print_usage_and_die ();
break;
case 'd'://调试运行
opt_debug = 1;
break;
default:
// -i can also be given without a value - if so, it defaults
// to defined value.
if (ACE_OS::strcmp (get_opt.argv ()[get_opt.opt_ind () - 1], ACE_TEXT ("-i")) == 0)
{
opt_install = 1;
opt_startup = DEFAULT_SERVICE_INIT_STARTUP;
}
else
{
print_usage_and_die ();
}
break;
}
}
// Define a function to handle Ctrl+C to cleanly shut this down.
static BOOL WINAPI ConsoleHandler (DWORD ctrlType)
{ //服务管理器控制入口函数
SERVICE::instance ()->handle_control (ctrlType);
return TRUE;
}
ACE_NT_SERVICE_DEFINE (Beeper,MainNTService,ACE_TEXT ("Annoying Beeper Service"));
int WinNTProcess::run (int argc, ACE_TCHAR* argv[])
{
SERVICE::instance ()->name (ACE_TEXT ("Beeper"),ACE_TEXT ("Annoying Beeper Service"));
parse_args (argc, argv);
if (opt_install && !opt_remove)
{
if (-1 == SERVICE::instance ()->insert (opt_startup)) //安装
{
ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("insert")));
return -1;
}
return 0;
}
if (opt_remove && !opt_install)
{
if (-1 == SERVICE::instance ()->remove ()) //卸载
{
ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("remove")));
return -1;
}
return 0;
}
if (opt_start && opt_kill)
print_usage_and_die ();
if (opt_start)
{
if (-1 == SERVICE::instance ()->start_svc ()) //启动
{
ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("start")));
return -1;
}
return 0;
}
if (opt_kill)
{
if (-1 == SERVICE::instance ()->stop_svc ()) //停止
{
ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("stop")));
return -1;
}
return 0;
}
if (opt_type)
{
if (-1 == SERVICE::instance ()->startup (opt_startup)) //设置启动模式
{
ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("set startup")));
return -1;
}
return 0;
}
if (opt_debug)
{
SetConsoleCtrlHandler (&ConsoleHandler, 1); //添加(或删除)应用程序处理函数列表
SERVICE::instance ()->svc (); //调试运行
}
else
{
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%T (%t): Starting service.\n")));
ACE_NT_SERVICE_RUN(Beeper, SERVICE::instance (), ret);
if (ret == 0)
ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("Couldn't start service")));
else
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%T (%t): Service stopped.\n")));
}
return 0;
}
4)、实现main主函数入口
int ACE_TMAIN (int argc, ACE_TCHAR* argv[])
{
return PROCESS::instance ()->run (argc, argv);
}
到此,基于ACE实现的服务程序就编写完了,倘若编译生成的二进制文件为TestService.exe,那么就可以通过如下命令:
安装服务:TestService.exe –i
卸载服务:TestService.exe –r
启动服务:TestService.exe –s
停止服务:TestService.exe –k
分析:
对于ACE_NT_Service类是如何封装的Windows服务的编程框架,请参见NT_Service.cpp、NT_Service.h、NT_Service.inl等几个文件:
(1)宏ACE_NT_SERVICE_DEFINE中实现了ServiceMain()和ServiceCtrlHandler()两个函数的函数体部分,同时在ServiceCtrlHandler()中还调用了RegisterServiceCtrlHandler函数来注册服务管理器的入口函数。
(2)宏ACE_NT_SERVICE_RUN中实现了服务信息表SERVICE_TABLE_ENTRYServiceTable[2]的定义,并调用StartServiceCtrlDispatcher函数来连接程序主线程到服务控制管理程序。
- 基于ACE的后台服务程序的实现
- ACE框架简介以及一个基于ACE的C/S服务程序实例
- ACE框架简介以及一个基于ACE的C/S服务程序实例
- ACE框架简介以及一个基于ACE的C/S服务程序实例
- ACE框架简介以及一个基于ACE的C/S服务程序实例
- ACE框架简介以及一个基于ACE的C/S服务程序实例
- 基于ACE模板适度精简优化的后台管理模板
- 基于ACE的定时器队列实现
- 基于hibernate的分页程序的实现(后台)
- 基于ACE的服务定位器(图失效)
- 采用 ACE Reactor 实现服务程序例子
- 基于Mailslot(邮槽)技术的日志服务程序的实现
- 开写论文,基于ACE的网络程序
- 一种基于tomcat平台将用户后台服务程序加入servlet的方法
- 一个基于ACE的线程池的实现(转)
- android 创建后台运行的服务程序
- 基于ACE实现的一个内存池-续篇
- 基于ACE中ACE_Event_Handler的定时器管理器实现
- Hadoop2.0(HDFS2)以及YARN设计的亮点
- 第一款采用人工神经网络的智能输入法
- cheng@ARM结构与编程之day3-CPSR+SPSR
- JAVA通过JNI调用本地C语言方法
- linux下调试core的命令,察看堆栈状态命令
- 基于ACE的后台服务程序的实现
- 十六进制颜色码
- js小妙用
- DOM对象
- java环境配置和tomcat环境配置
- ACE的线程管理机制
- Android_06_Activity的跳转
- eclipse或myeclipse模板设置
- 决策树1---特征选择(信息增益)