Abstract

  本文主要介绍 Windows service 的编程模式和 SCM 的相关功能。

Content

  • 基础知识

    • What is Windows Service
    • SCM
    • Service Programs
    • Event Trace for Windows
  • 关键函数
    • Service Entry Point
    • ServiceMain
    • Service Control Handler
  • Simple Windows Service in C++
  • Service程序的设计逻辑

基础知识

What_is_Windows_Service

关于 Windows Service 的定义,可以参考 Wiki: Windows Service ,总结如下:

  1. Windows Services 类似 Unix Daemon (守护进程),它是一种常驻内存的后台进程
  2. Windows Services 运行在专用账户上下文中,可以先于用户登录前运行。
  3. Windows Services 分别运行在三类用户账户:System, Network and Local 。它们的权限各不相同。
  4. Windows Services 需要符合 SCM 的接口规范和协议。
  5. SCM 负责管理 Windows Services,包括:查看状态、启动、停止等。

参考 MSDN - About Services 可知,Windows Services 包括以下几个主要知识点:

  • Service Control Manager
  • Service program
  • Service configuration program
  • Service control program

SCM

SCM - Service Control Manager,是一个特殊的系统进程,负责管理 Windows Services 。

The SCM executable, Services.exe, runs as a Windows console program and is launched by the Wininit process early during the system startup. Its main function, SvcCtrlMain(), launches all the services configured for automatic startup.

SCM 通过读下面的注册表项来初始化它的“internal database”

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services, which contains the actual database of services and device drivers and is read into SCM’s internal database.

换句话说,HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services 中包含了 Windows Services 的各种信息。

《Windows驱动开发技术详解》 CH3 中的 LoadNTDriver.exe 示例程序演示了如何通过 SCM 加载和卸载一个 Windows Service 。它的核心函数是 CreateService() 。

services.msc

通过 services.msc 命令可以打开 SCM 的 console 。

关于 SCM 更详细的资料,可以参考 MSDN - SCM,其中也提供了一个完整的示例程序 - The Complete Service Sample。

Database

参考 MSDN - About Services 可知“SCM 主要是通过数据库来管理 services”:

The service control manager (SCM) maintains a database of installed services and driver services, and provides a unified and secure means of controlling them. The database includes information on how each service or driver service should be started. It also enables system administrators to customize security requirements for each service and thereby control access to the service.

Service_Programs

参考 MSDN - Service Programs。

为了符合 SCM 的接口要求,Windows Services程序必须包含这三个部分:

  • Service Entry Point
  • Service ServiceMain Function
  • Service Control Handler Function

ETW

通过 CMD 命令 eventvwr.msc 可以查看 Windows Services 程序的加载、卸载、启动、停止和崩溃等等记录。

关键函数

从进程创建的角度来看,Services 的启动需要经过以下几个步骤:

  1. CreateService/OpenService 向 SCM 注册 service 和拿到句柄
  2. SCM 将 Service Program 调起,其中需要创建进程内核对象
  3. 操作系统检测到新的进程内核对象后,会为它分配“虚拟地址空间”,并将Image文件(module文件)加载到该地址空间
  4. 操作系统创建主线程,执行 Service Program 的 main 函数
  5. main 函数执行完 StartServiceCtrlDispatcher (向 SCM 注册 ServiceMain 函数指针)后退出。
  6. StartService 向 service controller 发起请求,service controller 在前面创建的进程地址空间中创建新线程,调用 ServiceMain
  7. ServiceMain 再创建线程,执行 service tasks 。
  8. ServiceMain 不直接执行 service tasks ,而是复杂与 controller 交互,控制 service 的 start/stop 。
  9. ServiceMain 会向 SCM 注册 Service Control Handler 回调函数,当需要停止时,SCM 会调用这个回调函数。
  10. Service Control Handler 会设置 STOP 事件,ServiceMain 收到事件后即可继续执行。

Service_Entry_Point

Services are generally written as console applications. The entry point of a console application is its main function. The main function receives arguments from the ImagePath value from the registry key for the service.

需要注意的是,services 程序都是 console 程序,它运行在后台,不带窗口。显然,它的入口函数只能是 main() 函数。

main

Services 程序的执行流在 SCM 与 main() 函数之间有一个切换的动作,具体流程是:

  1. SCM 启动 service 程序,进入 main() 函数
  2. main() 执行一些初始化操作
  3. main() 调用 StartServiceCtrlDispatcher() 函数,将控制权又转交给 SCM 。

When the SCM starts a service program, it waits for it to call the StartServiceCtrlDispatcher function.

StartServiceCtrlDispatcher

StartServiceCtrlDispatcher() 函数能够告诉 SCM 关于该 service 的一些信息:

The StartServiceCtrlDispatcher function takes a SERVICE_TABLE_ENTRY structure for each service contained in the process. Each structure specifies the service name and the entry point for the service.

SERVICE_TABLE_ENTRY

SERVICE_TABLE_ENTRY 是一个字符串与函数指针的映射表,它标明一个进程可以注册多个 ServiceMain 函数。

联系前面的内容,事实上,main() 与传统意义的 win32 console 程序的 main() 完全不同,它仅仅是进行一些“公共初始化”,然后告诉 SCM 真正的入口点函数(ServiceMain)在哪。

service type

main() 函数的初始化操作与 service type 相关,共有两种 type:

  • SERVICE_WIN32_OWN_PROCESS
  • SERVICE_WIN32_SHARE_PROCESS

前者立即调用 StartServiceCtrlDispatcher ,并在 service 启动后再执行初始化;后者需要在调 StartServiceCtrlDispatcher 进行一些“公共初始化”,也可以开新线程执行这个“公共初始化”。

示例

总结来说,service 的 main 函数很简单,主要调用 StartServiceCtrlDispatcher 函数,传入“Service Name”和“ServiceMain”。

void __cdecl _tmain(int argc, TCHAR *argv[])
{ // If command-line parameter is "install", install the service. // Otherwise, the service is probably being started by the SCM.if( lstrcmpi( argv[1], TEXT("install")) == 0 ){SvcInstall();return;}// TO_DO: Add any additional services for the process to this table.SERVICE_TABLE_ENTRY DispatchTable[] = { { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain }, { NULL, NULL } }; // This call returns when the service has stopped. // The process should simply terminate when the call returns.if (!StartServiceCtrlDispatcher( DispatchTable )) { SvcReportEvent(TEXT("StartServiceCtrlDispatcher")); }
}

ServiceMain

ServiceMain() 才是 service 程序真正的功能开始执行的地方。它的名字可以任意指定,仅需要在 SERVICE_TABLE_ENTRY 保存即可。

When a service control program requests that a new service run, the Service Control Manager (SCM) starts the service and sends a start request to the control dispatcher. The control dispatcher creates a new thread to execute the ServiceMain function for the service.

ServiceMain 与 main 不在同一个线程中。但是,它们应该在同一个进程上下文环境中(我猜)。

ServiceMain 的主要任务如下:

  1. 初始化所有的全局变量。参考《Windows核心编程》CH4“进程”,一般的Windows程序的全局变量初始化是在 mainCRTStartup 函数中。
  2. 注册回调函数。

Call the RegisterServiceCtrlHandler function immediately to register a Handler function to handle control requests for the service. The return value of RegisterServiceCtrlHandler is a service status handle that will be used in calls to notify the SCM of the service status.

  1. 执行其他初始化工作。
  2. 创建 STOP 事件。
  3. 将 service 状态设为 “SERVICE_RUNNING”,告诉 service controller 该 service 已准备就绪。
  4. 开线程,执行“service tasks”
  5. 等待 STOP 事件。
  6. 设置 STOP 状态,执行清理工作,退出。

Service_Control_Handler

参考 MSDN - Service Control Handler :

Each service has a control handler, the Handler function, that is invoked by the control dispatcher when the service process receives a control request from a service control program. Therefore, this function executes in the context of the control dispatcher.

Service Control Handler 是一个回调函数,其中会维护一个 switch - cases,执行 Controller dispatcher 的一些转发过来的一些关于 service 的 STOP 或 PENDING 相关的操作。

Simple_Service

Simple Windows Service in C++ 是一个非常小巧但是功能集备的 Windows Service 程序。

对照上面的知识点讲解,可以很轻松地掌握 Windows Service 程序的架构。

除此之外,MSDN - CPP Windows Service 示例程序演示了 service 程序的加载、启动、停止和卸载,也可以参考。

Service程序的设计逻辑

对于 Windows service 程序来说,它除了可以设置开机自动启动外,它还有一项非常有用的特点——自动重启(Auto restart)。当 Windows service 程序意外奔溃之后,系统会自动重启该 service,可以设置重启的次数。参考:Auto restart a windows service if it crashes

ServiceMain 函数用于与 SCM 交互,一般不在其所在的线程执行 tasks,以防阻塞 service 。因此,明智的设计是在 ServiceMain 中创建新的线程执行 service tasks 任务。

Service 进程作为一种常驻内存的后台进程,一般都将它当作 watch dog 。在程序设计的时候,可以让 Service 进程在创建其他进程去执行任务,这种设计可以保持Service 进程地址空间的独立性,进而减少 service 程序的崩溃次数。

Windows Service编程相关推荐

  1. python 编程服务_Python编写Windows Service服务程序

    如果你想用Python开发Windows程序,并让其开机启动等,就必须写成windows的服务程序Windows Service,用Python来做这个事情必须要借助第三方模块pywin32,自己去下 ...

  2. 什么是Windows Service应用程序?(转)

    什么是Windows Service应用程序? Microsoft Windows 服务(即,以前的 NT 服务)使您能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序.这些服务 ...

  3. Windows下编程需要看哪些书

    本人是自学计算机的,所有计算机方面的知识都看,研究过一段时间网络,对计算机网络方面有一定的基础,对程序设计尤感兴趣,个人认为学习Windows编程需要有以下基础: 1.C语言 这方面不用说清华大学谭浩 ...

  4. [笔记]Windows核心编程《十六》线程栈

    系列文章目录 [笔记]Windows核心编程<一>错误处理.字符编码 [笔记]Windows核心编程<二>内核对象 [笔记]Windows核心编程<三>进程 [笔记 ...

  5. windows服务编程

    文章目录 前言 方案一:服务程序 方案二:后台程序 对比 windows服务编程 windows服务控制 附录 - 完整代码 前言 在linux中,如果需要一个程序在后台持续提供服务,我们一般会使用守 ...

  6. C#开发Windows Service程序

    Windows Service概念介绍 Windows Service,也称Windows服务,是32位Windows操作系统中一种长期运行的后台程序.它们长期后台运行,没有用户界面,默默无闻,但它们 ...

  7. Windows Service服务程序的原理及实现(0)服务主函数 控制处理函数

    摘要: 何为服务?Windows 会话中可长时间运行的可执行应用程序.这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面.这种服务非常适合在服务器上使用,或任何时候,为了不影 ...

  8. 《windows核心编程系列》二谈谈ANSI和Unicode字符集

    第二章:字符和字符串处理 使用vc编程时项目-->属性-->常规栏下我们可以设置项目字符集合,它可以是ANSI(多字节)字符集,也可以是unicode字符集.一般情况下说Unicode都是 ...

  9. Windows Socket编程笔记之最简单的小Demo

    Windows Socket编程的大致过程: 服务器端: ----过程-------------对应的API-------  0.初始化         |  WSAStartup()  1.创建So ...

最新文章

  1. OpenStack 关闭安全组
  2. 关于spring service层的mybatis缓存问题,待解决
  3. 折叠式菜单 html,JQuery实现折叠式菜单的详细代码
  4. mysql 触发器学习
  5. 【IDE】关于IDE生成文件大小的计算
  6. c++ 关于char *的类库函数
  7. ajax mysql项目 react_React视频教程来啦,每周末都有前端视频教程学
  8. linux多进程编程计算圆周率,中值积分定理计算PI值的多线程实现
  9. ubuntu server 14.10 安装 nodejs
  10. android触摸进度条,Android仿IOS ViewPager滑动进度条
  11. 多项logistic回归系数解释_逻辑回归logistic(含python代码)
  12. teechart mysql_TeeChart的X轴为时间,多个Y轴的显示
  13. 【企业了解】巨量引擎(飞鱼CRM)
  14. 带你深入理解矩阵乘法
  15. 什么是5G?它能为我们带来什么样的便利?思维导图《5G时代》给你新认识
  16. 软考-信息安全工程师(汇总1000题)
  17. 关于TL431和光耦PC817反馈控制部分电阻取值计算
  18. nvm安装node成功,npm安装失败问题
  19. 谷歌seo自建博客做外链有用吗?谷歌外链怎么做?
  20. 『 图片处理 』图片转换字符图

热门文章

  1. 浅谈:斐波那契搜索算法(Fibonacci search)
  2. mysql查找并删除记录_mysql 子查询删除记录
  3. 书论73 张绅《法书通释》
  4. 如何进入带有密码的QQ空间
  5. uniapp 微信公众号网页获取微信头像昵称
  6. 手把手教你使用Hugo搭建个人博客网站|保姆级教学
  7. 影响复购率的主要因素
  8. 常见的计算机系统可靠性数学模型,计算机系统可靠性(串并联)计算方法详解
  9. macos从10.12.6升级到10.15.7
  10. scratch编程如何学?