Service Control Manager,服务控制管理器,人称SCM就是它!在Windows内核中,都可以看到她忙碌的身影,可以说是系统服务和驱动的管家婆了!

    SCM管家婆起早贪黑,每次系统启动,她也随着而起。她凭借着自己的努力,终于在Windows的内核占据了一席之地,调配着手下许多服务和驱动。SCM她到底具有什么能力呢?她是一种远程过程调用服务,为普通程序操控计算机的服务提供了一扇方便的门,这里还包括远程计算机喔。服务配置和控制程序通过调用一系列服务函数接口使用SCM的功能!
 
    说到底,SCM在内核中默默地发挥她的作用,才能受到众多服务的尊敬,那么她到底为Windows服务做了哪些好事呢?细细想来,SCM在内核中主要担任了五大任务:
  • 维护已安装服务的数据库
  • 在需要或系统启动的时候,打开服务或驱动服务
  • 枚举已安装的服务或驱动服务
  • 为已运行的服务或驱动服务维护状态信息
  • 将控制码传递给运行中的服务
  • 锁定/解锁服务数据库
    我们现在来说说SCM的看家本领,即上面的五大任务!
 
1. 维护已安装服务的数据库
 
1.1 什么是数据库?
 
    什么是服务的数据库?数据库?听起来那么耳熟,但其实又不是那么回事,初学者一定会和MySQL等主流的数据库混为一谈,其实不太一样。这个数据库只不过是系统注册表的一些和服务相关的数据集合而已。它的具体位置是HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services。
 
 
    它很重要,因为它存储着已安装服务的一些信息。SCM可以在需要或系统启动的时候,负责启动已安装的服务。在启动的时候,SCM需要取得驱动的基本信息数据库为设备驱动提供了需要的入口。。同时数据库可以用于SCM或者程序来添加,删除,修改或配置服务。
    那数据库到底包含了哪些重要的内容呢?
    
1.2 数据库内容
 
    我们从最外窥探到里面吧。
 
1.2.1 子键名字——服务的名字
 
    先说services下的众多子键吧,什么.NET CLR Data等等不知所云的名字其实就是服务的名字,每个服务安装后都会在这个数据库里挂个名,以防SCM在启动的时候找不到它。那这个名字是谁指定,总得说清楚吧!那还要追溯到服务出生的那个夜晚...还记得CreateService这个函数吧,里面有个lpServiceName形参便是服务的名字!
 
1.2.2 子键的内容
 
    上面是一个服务的最基本的内容,当然还有其他的。

  • Type(服务类型)。对普通服务来说,它表明服务运行环境:自己进程内或与其他服务共享的进程;对于驱动服务来说,它是一个内核服务还是文件系统服务。下面是它的数字含义:0x1:驱动服务,0x2:文件系统服务;0x10:运行在自己进程的服务;0x20:与其他服务共享进程的服务
  • Start(启动类型)。它表明这是一个自启动服务呢还是一个需求启动服务,自启动又有系统引导启动或SCM启动;需求启动服务有服务配置程序手动启动。同时它也能够指示此服务是否有用,在这种情况下,服务是无法启动的。0x00:系统引导程序自动运行服务;0x01:一个由 IoInitSystem函数启动的服务;0x02:在系统启动的时候,由SCM自动运行服务;0x03:需求启动,当一个程序调用StartService函数时,SCM启动服务;0x04:一个不能启动的服务
  • ErrorControl(错误处理方式)。当该启动服务失败时产生错误的严重程度以及采取的保护措施。0x00:忽略错误;0x01-0x03:其他错误处理,参考CreateService函数。
  • ImagePath(可执行文件的完整路径)。对服务来说,后缀名是.exe;对驱动来说,后缀名是.sys。可以是相对路径,可以带有启动参数。
  • 可选的账户名字和密码(对服务),服务程序运行在此账户的上下文环境,当没指定任何账户时,默认是LocalSystem账户
  • 可选的驱动对象名字(对驱动),名字在IO系统中会被用来加载驱动设备。如果没有指定名字,IO系统会根据驱动服务名字生成一个默认的名字。
  • DependOnService(可选的依赖信息)。对于服务,信息可以是一个服务列表,在SCM启动此服务前,必须先启动列表中指定的服务。对于驱动,信息可以是一个驱动列表,在SCM启动此服务前,必须先启动列表中指定的驱动。
  • Group(服务组)。如果服务没有这一项,那么它不属于任何一个服务组,系统则会默认的将其在所有的服务启动后加载。
  • Tag(标签)。它用来描述服务的标识。每一个在服务组中的服务都会被分配一个唯一的标识。注册表通过对服务组的服务标识的排列来安排,同一服务组中的个服务的加载先后顺序
 
2. 打开服务或驱动服务
 
    SCM管理着服务的启动,当然启动对于我们使用者来说分为自动启动和手动启动,这两种启动都是由SCM亲自启动的。还记得上面所讲的启动类型吗?在系统启动的时候,SCM随着而起,她要干活了。她首先要读取数据库中服务的启动信息,根据启动类型,打开服务!
 
2.1 自启动服务
 
    在系统启动的时候,SCM会启动所有自启动类型的服务和驱动,包括这些服务和驱动所依赖的其他服务和驱动。
 
2.2 延迟的自启动服务
 
    一个自启动服务可以通过ChangeServiceConfig2 函数的SERVICE_CONFIG_DELAYED_AUTO_START_INFO形参被配置为延迟启动,当系统重启后,改变才会生效。
 
2.3 修改服务启动顺序
 
    对于自启动的服务,如果没有人告诉或提醒该先启动哪一项,SCM她会稀里糊涂的,最终导致服务启动顺序紊乱,例如A需要先在B前面启动,但是偶然的机会,SCM先启动了B那么会导致系统错误!
 
2.3.1 Group服务组
    操作系统提供一种服务组的机制,这个机制由两部分组成;一个是服务组列表,另一个是Tag标志。这个机制是怎么运作的呢?
    注册表:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder的List值存放的是服务组的启动顺序。最前面的服务组最先启动。
    一个服务可以通过上面的第一点的Group值指定它是属于哪个服务组的,这样不同服务组的不同服务便有不同的启动顺序。如果服务没有这一项,那么它不属于任何一个服务组,系统则会默认的将其在所有的服务启动后加载。
    但是属于同一个服务组的启动顺序该由谁决定呢?
    注册表:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GroupOrderList存放的是所有服务组的信息。
 
    
 
    每个服务组信息都被保存为了一个REG_BINARY类型的值,为了方便查看,我们把它分为几部分来看:
 
    02 00 00 00 | 01 00 00 00 | 02 00 00 00
 
    第一个字段表示该服务组存在两个服务,一个是Tag值为01的服务,一个是Tag值为02的服务,从左到右顺序加载不同Tag值的服务。
    关于Tag标志,可以查看第一点的Tag标签,它是一个服务的基本内容之一。
    这样便解决服务的启动顺序问题!
 
2.4 确认程序  
 
    当启动完成的时候,系统执行启动确认程序(由注册表的HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control中的BootVerificationProgram键指定,默认情况下,这个值是没有的。)。当第一个用户登录后,系统会简单地报告启动成功。可以单独提供一个启动确认程序来检查系统问题和报告启动状态给SCM,使用 NotifyBootConfigStatus 函数。
 
2.5 传说中的LKG——last-known-good

    当系统成功启动后,系统就克隆保存一份数据库备份,作为last-known-good(LKG)配置。如果当前使用的数据库导致系统启动失败,那么可以用备份来恢复。备份的数据库就保存在:HKEY_LOCAL_MACHINE\SYSTEM\ControlSetXXX\Services 中。
 
    其中XXX值也被保存在:HKEY_LOCAL_MACHINE\System\Select\LastKnownGood 中。
 
    如果自动启动的服务自动的时候得出SERVICE_ERROR_CRITICAL错误,SCM就会重新启动机器,并使用LKG的配置,如果LKG的配置已经被使用了,启动就会失败。
 
    注册表中服务的ErrorControl值表示SCM如何处理服务错误。如果值为SERVICE_ERROR_IGNORE(0)或者没有指定,SCM只忽略错误并继续服务的启动,如果为SERIVCE_ERROR_NORMAL(1),就在事件日志中记录下错误原因。如果错误控制为SERIVCE_ERROR_SEVERE(2)或者SERIVCE_ERROR_CRITICAL(3),服务就报告启动错误。SCM记录事件日志,并调用函数ScreverToLastKnownGood,将系统注册配置切换到LKG的版本,然后调用NtShutDownSystem重新启动系统。如果系统已经使用LKG版本,就直接重新启动。
 
2.5.1  LKG版本的产生
 
    LKG版本的产生:SCM在系统启动阶段启动了所有自起服务之后,需要来决定这个LKG配置。缺省情况下,一次成功的启动包括所有服务的成功启动和一个用户的登录。如果在启动服务阶段存在服务的SERIVCE_ERROR_SEVERE(2)或者SERIVCE_ERROR_CRITICAL(3)错误,那么这就是失败的启动。如果SCM成功完成服务的启动,当有用户登录的时候,Winlogon调用NotifyBootConfigStatus函数发送消息给SCM。在成功启动所有服务,并且收到NotifyBootConfigStatus的登录信息,SCM就调用NtInitializeRegistry保存当前的启动配置信息。
 
2.6 手动启动服务
 
 
 
    通过服务控制面板启动服务,或者通过调用StartService函数启动服务;两者都可以为服务指定参数。
    当服务启动时,SCM将会执行下列步骤:
  • 检索取回存储在数据库的账户信息
  • 登录服务的账户
  • 加载用于预置文件
  • 创建处于挂起状态的服务
  • 将登录令牌分配给程序
  • 允许程序运行
3. SCM句柄
 
    SCM支持句柄类型来操作以下对象:
  • 已安装服务的数据库
  • 一个服务
  • 数据库锁
    一个SCManager 对象代表已安装服务的数据库。它是一个存储服务对象的对象容器。 OpenSCManager 能够取得一个SCManager 对象的句柄。这个句柄用于安装,删除,打开,枚举服务,以及锁定服务数据库。
    一个服务对象代表一个已安装的服务。 CreateService 和OpenService 函数返回一个已安装服务的句柄。
    OpenSCManager ,CreateService 和OpenService函数能够获取不同的权限,允许或拒绝请求权限取决于运行程序的权限令牌和与SCManager或服务对象相关的安全描述符。
    当你不需要这些句柄时,确保使用CloseServiceHandle 函数关闭此句柄
 
4. 将控制码传递给运行中的服务——ControlService
 
    SCM这个人做事比较专一,在还未完成一件事前,她绝不会去接收另一件事。她是以串行的方式处理服务控制通知。
    当有客户端通过ControlService函数向指定的服务发送一个控制码,如果SCM她正好有空,她会先接收这个控制码,然后她会判断指定的服务是否已经处于控制码可以发送给它的状态或者指定服务已经指定要接收控制码了,好这时服务已经接收了控制码。但是SCM这个人做事比较专一,有始有终。在得到服务的反馈之前,任务还未完成。她会等待服务完成一个服务控制通知的处理,才发送下一个通知,这个时候SCM很忙,她不会接收处理了。
    所以如果任何服务正忙于处理一个控制码,ControlService的一个调用将会暂停30s的时间(这是一个超时时间),如果超时时间一过但处理还没完成,那么函数将返回一个ERROR_SERVICE_REQUEST_TIMEOUT。
    和ControlService一样,如果其他服务正忙于处理控制码,StartService也有一个超时时间30s。如果忙于处理控制码的服务没有在30s内从它的处理函数返回,那么StartService将调用失败,返回ERROR_SERVICE_REQUEST_TIMEOUT,这是因为SCM一次只能处理一个服务控制通知。
 
5. 枚举已安装或已运行的服务或驱动服务
 
    SCM管理着系统上上下下许多的服务,包括驱动。对于来自应用层的访问,她提供有固定的接口。对于枚举或获取服务和驱动的相关信息,SCM提供了EnumServicesStatusEx函数,甚至还可以通过EnumDependentServices来获取服务或驱动的依赖关系。当然,如果只是想获得服务的名字,只要简单地调用GetServiceKeyNameGetServiceDisplayName即可。
 

【原创】Windows服务管家婆之Service Control Manager的更多相关文章

  1. 【Windows】Windows服务管家婆之Service Control Manager

    Service Control Manager,服务控制管理器,人称SCM就是它!在Windows内核中,都可以看到她忙碌的身影,可以说是系统服务和驱动的管家婆了!     SCM管家婆起早贪黑,每次 ...

  2. DIM-00014: Cannot open the Windows NT Service Control Manager.

    创建Oracle数据库时出错: OPW-00001: Unable to open password-file DIM-00014: Cannot open the Windows NT Servic ...

  3. Error connecting to the Service Control Manager: 拒绝访问 Mongodb问题-解决

    原文地址:https://blog.csdn.net/carrot5032/article/details/74742888 发现在mongodb.log里出现  2017-07-07T17:01:5 ...

  4. Several Service Control Manager Issues (Event ID's 7000, 7009, 7011)

    https://answers.microsoft.com/en-us/windows/forum/windows_7-performance/several-service-control-mana ...

  5. 事件类型: 错误 事件来源: Service Control Manager 事件种类: 无 事件 ID: 7000

    在控制面板\管理工具\服务里找dns Client 服务,把他启动了

  6. 玩转Windows服务系列——服务运行、停止流程浅析

    通过研究Windows服务注册卸载的原理,感觉它并没有什么特别复杂的东西,Windows服务正在一步步退去它那神秘的面纱,至于是不是美女,大家可要睁大眼睛看清楚了. 接下来研究一下Windows服务的 ...

  7. 玩转Windows服务系列——Windows服务小技巧

    伴随着研究Windows服务,逐渐掌握了一些小技巧,现在与大家分享一下. 将Windows服务转变为控制台程序 由于默认的Windows服务程序,编译后为Win32的窗口程序.我们在程序启动或运行过程 ...

  8. Asp.net(C#) windows 服务{用于实现计划任务,事件监控等}

    什么是windows服务?      一个Windows服务程序是在Windows操作系统下能完成特定功能的可执行的应用程序.Windows服务程序虽然是可执行的,但是它不像一般的可执行文件通过双击就 ...

  9. windows服务部署与卸载

    同事问到windows service的东东,现在整理一下,用c#如何创建一个windows service,以及如何调试.部署.卸载. 一.创建windows service 1. 打开VS2008 ...

随机推荐

  1. C++二分图匹配基础:zoj1002 FireNet 火力网

    直接给出题目吧... 问题 D(1988): [高级算法]火力网 时间限制: 1 Sec 内存限制: 128 MB 题目描述 给出一个N*N的网格,用'.'表示空地,用'X'表示墙.在网格上放碉堡,可 ...

  2. c# 将object尝试转为指定对象

    主方法: /// <summary> /// 将object尝试转为指定对象 /// </summary> /// <param name="data" ...

  3. idea使用自动生成变量的时候总是默认final,每次都会跳出来declare final的选项,并且默认是勾选的,很难受

    看下截图: 我这边首先要保证:settings----->Editor------>Code Style-------->java下的这两个选项不被勾选; 然后在生成变量的时候,再次 ...

  4. SpringCloud服务注册与发现

    1.介绍对于微服务的治理而言,其核心就是服务的注册和发现.在SpringCloud 中提供了多种服务注册与发现组件:Eureka,Consul,Zookeeper.官方推荐使用Eureka. 说明:E ...

  5. 京东Alpha平台开发笔记系列(一)

    2018京东Alpha开发者大赛是由京东智能面向广大开发者举办的大型语音技能开发比赛,参赛者将通过Skill开放平台开发技能,在实现开发者自身价值的同时,为京东智能活跃用户提供更优质.更便捷.更智能的 ...

  6. Android从入门到进阶——布局

    一.组件 1.UI组件 (Android.view.View的子类或者间接子类) 2.容器组件(Android.view.ViewGroup子类或者间接子类) 二.UI组件:TextView,Spin ...

  7. python计时器类

    import time as t class MyTimer(): def __init__(self): self.unit = ['年', '月', '日', '时', '分', '秒'] sel ...

  8. Requests+正则表达式抓取猫眼电影TOP100

    spider.py # -*- coding:utf-8 -*- import requests import re import json import codecs from requests.e ...

  9. C语言 字符二维数组(多个字符串)探讨 求解

    什么是二维字符数组? 二维字符数组中为什么定义字符串是一行一个? “hello world”在C语言中代表什么? 为什么只能在定义时才能写成char   a[10]="jvssj" ...

  10. 【webpack】-- 入门与解析

    每次学新东西总感觉自己是不是变笨了,看了几个博客,试着试着就跑不下去,无奈只有去看官方文档. webpack是基于node的.先安装最新的node. 1.初始化 安装node后,新建一个目录,比如ht ...