Windows服务(1) (翻译自MSDN)
◆总述
如果一个服务应用程序遵循SCM(Service Control Manager)接口规范,那么系统登录用户就可以通过操作服务程序控制面板或者执行内部使用了服务函数(service function)的应用程序来使得该服务应用程序能够享受一种"特权"----改服务程序能够随系统启动而自动执行,甚至在用户没用登录的情况下也可以.而一个驱动服务必须遵循设备驱动协议的,这点与服务应用程序相类似,但它不会与SCM接触.
下面我将从下面几个方面来介绍windows服务:
● 服务控制管理器
● 服务程序
● 服务配置程序
● 服务控制程序
● 服务用户帐号
● 交互式服务
● 服务安全和服务权限
● 服务程序的调试
◆服务控制管理器(SCM)
SCM随着操作系统的启动而被启动,本质上算得上是一种RPC(远程程序调用)服务器,它这样的设计便于服务配置程序和服务控制程序具有了操控远程机器上的服务的能力.SCM的主要职责大体可以概括为以下几个方面:
● 维护已安装服务数据库
● 不论是在系统启动时还是在系统提出需求时都可以启动服务和驱动服务
● 遍历系统已安装的服务和驱动服务
● 维护正在运行服务和驱动服务的状态信息
● 传递控制请求给正在运行着的特定服务
● 加锁和解锁服务数据库
下面我们来详细介绍一下:
(1)已安装的服务数据库
SCM通过操作注册表来维护系统已安装服务的数据库的,该数据库在注册表中的注册键为:
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services,该键包含的每一个子键都代表了本系统安装的一个服务或者驱动服务,键的名字就是该服务的名字,如果服务安装了一个服务配置程序的话,CreateService函数的参数中就标明了这个名字,服务数据库包含了每个已安装服务和驱动服务的一些基本信息,这些信息包括:
(1.1)服务的类型
标明了此服务是单独运行在自己进程中,还是与别的服务共享一个运行进程,而此项对于驱动服务来说则意味着该服务是一个内核驱动还是一个文件系统驱动.
(1.2)启动类型
此项标明了该服务或者驱动服务是随系统自动启动程序还是一个请求启动服务,它还标明了当特定的服务不能够启动时对应的服务或者驱动服务使用应该被禁用.
(1.3)错误控制级别
此项标明当系统启动期间特定服务或者驱动服务启动失败时的错误严重程度,也指定启动程序应该采取的相应的措施.
(1.4)可执行文件的路径
对于服务文件名的扩展名是.exe而对于驱动服务来说为.SYS.
(1.5)可选的一些依赖信息,这些信息决定了启动服务或驱动服务的正确的顺序.
(1.6)对于服务,一个可选的账号和密码.
该服务必须此账户下运行,如果没有给定账户,该服务就用本地系统账户运行.
(1.7)对于驱动服务,一个可选的驱动对象名,该名字被I/O系统用来装载设备驱动的.
(2)自动启动服务
在系统启动期间,SCM开启所有自启动服务和这些服务所依赖的服务,例如,如果一个自启动服务依赖一个请求式服务,那么该请求式服务也会被自动启动.服务的装载启动顺序由下面几点决定:
(2.1)加载顺序群组表中的群组加载顺序.
这个信息被存储在KEY_LOCAL_MACHINE/System/CurrentControlSet/Control的子键ServiceGroupOrder中.CreateService或ChangeServiceConfig函数参数lpLoadOrderGroup标明了该值.
(2.2)同一服务群组按标签顺序标识的服务顺序.
这个信息被存储在KEY_LOCAL_MACHINE/System/CurrentControlSet/Control的子键GroupOrderList的值.
(2.3)每个服务所列出的依赖服务顺序.
当系统启动完成以后,系统会执行启动确认程序,该程序被HEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control的
BootVerificationProgram的键值所标识.默认情况下,该值不会被设置,当第一个用户登录系统以后,系统只是简单的报告一下"启动完成"的消息,当然你也可以提供一个启动确认程序来检测系统问题,然后通过NotifyBootConfigStatus函数将启动状态报告给SCM.
当系统成功启动以后,系统会对last-known-good(LKG)配置的服务数据库进行一次备份,以便于在改变当前服务数据库引起系统重新启动失败的情况下进行还原,下面的键保存的就是该备份数据库配置:HKEY_LOCAL_MACHINE/SYSTEM/ControlSetXXX/Services而这里的XXX的值被这个键值所保存:KEY_LOCAL_MACHINE/System/Select/LastKnownGood.
如果一个自启动服务发生的错误等级为SERVICE_ERROR_CRITICAL,这个时候SCM就会使用LKG重新启动电脑,但如果此时的LKG配置正在被使用,启动就会失败.
一个自启动服务也可以被配置为一个延迟自启动服务,你只要调用函数ChangeServiceConfig2并以参数
SERVICE_CONFIG_DELAYED_AUTO_START_INFO就可以达到此目的.等重新启动系统后,该变动才会生效.
(3)按照请求启动服务
用户可以根据服务控制面板工具来启动服务,用户可以为Start parameters域标明服务参数,一个服务控制程序可以通过调用StartService来启动一个服务.服务被启动以后,SCM开始按顺序执行以下操作:
→获的存储在数据库中的账户信息
→登录服务账户
→加载用户配置
→创建服务并将其置于挂起状态
→分配登录令牌给该进程
→让该进程执行
(4)服务记录表
当每一个服务条目从已安装服务数据库中读取出来,SCM为每一个服务创建一个服务记录.通常一个服务记录包括:服务名称、启动类型(自启动或者请求式启动)、服务状态(详见SERVICE_STATUS结构)、指向依赖链表指针.
当某个服务被安装的同时,你必须提供账户的用户名和密码,SCM将把用户名存储在注册表中而将密码以一种LSA(Local Security Authority)的更为安全的部分进行存储.系统管理员可以用永不过期的密码来创建账户,同时也可以用带有有效期的密码来创建账户,通过周期性的修改密码来管理账户.
SCM保存了用户账户密码的两份拷贝,当前密码和备份密码.服务第一次被安装的时候标明的密码被存储为当前密码,此时的备份密码没有被初始化.当SCM尝试在安全的用户账户下启动该服务时,此时它会使用当前密码.如果当前密码被成功的使用,它也被保存作为备份密码.如果密码被ChangeServiceConfig函数或者服务控制面板工具所修改,新密码就会被存储作为当前密码,而老密码就会被存储为备份密码.如果SCM尝试去用当前密码去启动服务却以失败告终,那它就会使用备份密码.如果备份密码被正确的使用,该备份密码就被设置为当前密码.
当某服务使用SetServiceStatus函数发送状态信息时,SCM更新服务状态.而对于驱动服务,SCM则是通过查询I/O系统来维持驱动服务状态,而不是通过接受状态通知信息.
通过调用SetServiceBits函数,一个服务可以注册其它的类型信息.NetServerGetInfo和NetServerEnum函数可以获得支持的服务类型.
(5)SCM句柄
通过使用SCM句柄你可以访问的对象有:已安装服务数据库、服务、数据库锁.一个SCManager对象标识了已安装服务数据库,它是一个盛放服务对象的容器对象.OpenSCManager函数返回了一个指定电脑上的SCManager对象句柄,该句柄在下面情况下被使用:安装、删除、打开、遍历所有服务和对服务数据库进行加锁.
一个服务对象代表了一个安装的服务.CreateService和OpenService函数可以返回已安装服务的句柄.
OpenSCManager,CreateService和OpenService可以请求多种对SCManager和Service对象的多种访问方式.请求访问权限应该被
授权还是被否决取决于调用进程的访问令牌和与SCManager或者服务对象的安全描述符.
CloseServiceHandle函数关闭SCManager和Service对象句柄.当你不再需要这些句柄时,确保一定要关闭他们.