基于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函数来连接程序主线程到服务控制管理程序。

0 0
原创粉丝点击