You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The term "service" in this context generally refers to a callable routine, or set of callable routines. This is distinct from the concept of a "service process", which is a user mode component somewhat analogous to a daemon in Unix-like operating systems.
A service is an application type that runs in the system background and is similar to a UNIX daemon application. Services provide core operating system features, such as Web serving, event logging, file serving, help and support, printing, cryptography, and error reporting. With the Services snap-in, you can manage services on local or remote computers.
A service is a long-running executable that does not support a user interface, and which might not run under the logged-on user account. The service can run without any user being logged on to the computer.
Microsoft Windows services, formerly known as NT services, enable you to create long-running executable applications that run in their own Windows sessions. These services can be automatically started when the computer boots, can be paused and restarted, and do not show any user interface. These features make services ideal for use on a server or whenever you need long-running functionality that does not interfere with other users who are working on the same computer. You can also run services in the security context of a specific user account that is different from the logged-on user or the default computer account.
You can easily create services by creating an application that is installed as a service. For example, suppose you want to monitor performance counter data and react to threshold values. You could write a Windows Service application that listens to the performance counter data, deploy the application, and begin collecting and analyzing data.
A service application conforms to the interface rules of the Service Control Manager (SCM). It can be started automatically at system boot, by a user through the Services control panel applet, or by an application that uses the service functions. Services can execute even when no user is logged on to the system.
A driver service conforms to the device driver protocols. It is similar to a service application, but it does not interact with the SCM. For simplicity, the term service refers to a service application in this overview.
Service Control Manager (SCM) is a special system process under the Windows NT family of operating systems, which starts, stops and interacts with Windows service processes. It is located in the %SystemRoot%\System32\services.exe executable. Service processes interact with SCM through a well-defined API, and the same API is used internally by the interactive Windows service management tools such as the MMC snap-in Services.msc and the command-line Service Control utility sc.exe. Terminating this file is used as a method of causing the Blue Screen of Death.
Svchost.exe ( 服务主机 ,或 SvcHost )是一个系统进程,它可以承载 Windows NT 系列操作系统中的一个或多个 Windows 服务 。Svchost 在共享服务进程的实现中是必不可少的,其中多个服务可以共享一个进程以减少资源消耗。将多个服务组合到一个进程中可以节省计算资源,这一考虑是 NT 设计人员特别关心的,因为创建 Windows 进程比其他操作系统( 例如 Unix 系列 )需要更多时间和消耗更多内存。但是,如果其中一项服务导致未处理的异常,则整个进程可能会崩溃。此外,最终用户可能更难以识别组件服务。
svchost 进程是在 Windows 2000 中引入的,尽管从 Windows NT 3.1 开始就存在对共享服务进程的底层支持。
nssm is a service helper which doesn't suck. srvany and other service helper programs suck because they don't handle failure of the application running as a service.
usingSystem;usingSystem.Diagnostics;usingSystem.ServiceProcess;usingSystem.Timers;namespaceWindowsServiceTest{publicpartialclassMyNewService:ServiceBase{privatereadonlyEventLogeventLog;privateTimertimer;privateinteventId=1;publicMyNewService(string[]args){InitializeComponent();CanPauseAndContinue=true;eventLog=newEventLog();if(!EventLog.SourceExists("MySource")){EventLog.CreateEventSource("MySource","MyNewLog");}eventLog.Source="MySource";eventLog.Log="MyNewLog";eventLog.WriteEntry("Init: "+string.Join(",",args));}protectedoverridevoidOnStart(string[]args){eventLog.WriteEntry("In OnStart: "+string.Join(",",args));// Set up a timer that triggers every minute.timer=newTimer();timer.Interval=60000;// 60 secondstimer.Elapsed+=newElapsedEventHandler(this.OnTimer);timer.Start();}publicvoidOnTimer(objectsender,ElapsedEventArgsargs){eventLog.WriteEntry("Monitoring the System",EventLogEntryType.Information,eventId++);}protectedoverridevoidOnStop(){eventLog.WriteEntry("In OnStop.");}protectedoverridevoidOnPause(){timer.Stop();eventLog.WriteEntry("In OnPause.");}protectedoverridevoidOnContinue(){timer.Start();eventLog.WriteEntry("In OnContinue.");}}}
什么是服务
维基百科: Architecture of Windows NT 、Windows service
从 Microsoft 技术文档 找的一些描述:
按我的理解,服务就是一个抽象概念,是一套被设计用于管理那些 需要长时间运行、具有依赖关系、没有用户界面、不需要用户登录、需要特殊权限、可跟随系统启动而自动运行的程序 的体系结构,而相关的数据库、程序、进程都是其中不可或缺的一部分。用户向系统注册服务后便由系统来自动调度服务而无需用户参与。任务管理器的服务标签页中,每行数据可以表示一个服务,实际数据存储于注册表中:
服务这个术语更倾向于描述程序的运行方式,比如以服务的方式运行( run as a service )。
Windows 中的服务和 Linux 中的服务从目的性上( 后台运行、开机启动等 )来说比较类似,只不过实现方式和运行机制有所不同。 Linux 服务通常会在
/etc/init.d
目录下有一个对应的配置脚本( 参考 ),比起在 Windows 上更直观易懂,配置服务也更容易。服务变更历史
服务与程序和进程的关系
类似的问答
相关术语解释
操作系统
资源
程序
通常用源代码( Source code ,人类可读的符合程序设计语言规范书写的文本文件 )表示源程序,而程序泛指目的程序,即可执行的文件( Executable ),不需要关联 打开方式 就能双击打开的文件。在 Windows 中除了常见的 exe 文件和批处理文件外,还可以用注册表查询 HKEY_CLASSES_ROOT 中数据为
"%1" %*
的项。软件
软件是一系列文档、程序以及其正常运行所需的相关数据的集合。我们常说的软件( 手机上的 APP )一般指的是运行在操作系统之上的应用软件( Application ),比如 QQ ,打开安装目录可以看到除了 exe 文件外还有一堆其他的文件。
应用与任务
对应 win10 任务管理器中进程标签页下的应用分类,每个应用展开后会有任务。应用是存在前台窗口与用户进行交互的主进程,任务是其子进程或者抽象出来的窗口。像常用的 合并任务栏按钮 设置项,没合并前显示全部的窗口任务,合并后只显示应用。( 打开任务栏设置 )
它们和进程之间的对应关系不太好描述,自行感受吧:
regedit -m
命令打开两个注册表编辑器,可以看到 2 个应用、 每个应用 1 个任务、 2 个 regedit.exe 进程。进程
进程是可并发执行的程序在一个数据集合上的运行过程,是系统进行资源分配和调度的基本单位,是线程的容器。迄今为止对这一概念还没有一个确切的统一的描述。一个进程是特定程序的一个实例( instance ),在运行过程中可以创建自己的子进程。 后台进程 就是不与用户进行交互( 隐藏起来 )的进程。
程序和进程就是 名词 与 现在进行时 的关系,常见的比喻是菜谱和做菜过程。
服务程序
一种符合 服务控制管理器 (SCM) 接口规范的应用程序,即 Service Program ,是服务启动实际所执行的可执行文件。( 详见下文 )
它们之间的关系:
这些术语本就比较抽象,再加上翻译、口语化、场景( 开发还是使用 )等等因素就容易造成歧义令人困惑。就像
1+1=2
一样,不需要纠结为什么是这样,只需要知道这是约定成俗的,了解多了自然就懂了。这些术语的存在是为了帮助人们更容易理解复杂抽象的计算机软件体系结构。口语中的服务通常在各个语境中分别指代服务本身、服务程序和服务进程,能明白其中的含义就行了。什么是服务控制管理器 (SCM)
我们都知道服务设置为自动后就能开机启动,而这个操控者就是 SCM 。SCM 是一个特殊的系统进程,它执行各种与服务有关的任务。它跟随系统启动而运行,之后它便会启动所有设置了 自动启动 的服务以及它们所依赖的 手动启动 的服务。SCM 维护了一个数据库来存储已安装的服务,并提供了安全统一的方式来管理它们。
SCM 就是整个服务体系的核心,由它直接管理服务的一切配置和行为,它提供一系列 函数 供其他程序调用以间接控制服务:
调用这些函数的程序根据职能主要分为以下几类:
服务程序
为一个或多个服务提供可执行代码的应用程序。主要用到了连接 SCM 和修改服务状态的函数。一些 服务程序 还会通过命令行参数实现自 安装 的功能,比如
mysqld --install
。通常 服务程序 作为控制台程序直接运行而不是通过服务运行时会报错:
# 参考 StartServiceCtrlDispatcher 的返回值 1063 (ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
服务配置程序
主要用于增删改查 SCM 数据库中服务配置信息的程序。虽然可以通过注册表管理数据库,但还是应该只使用 SCM 提供的相关函数以确保配置的正确性。系统自带的命令行工具 Sc.exe 就包含此类功能。
服务控制程序
主要用于 启动服务程序 和 控制服务进程 的程序,后面简称 SCP 。它们只是向 SCM 发送请求,再由 SCM 转发给服务。系统自带的命令行工具 Sc.exe 、net.exe 就包含此类功能。
SCM 以串行的方式依次处理请求,一个服务处理完一个请求后 SCM 才会发送下一个请求。因此如果服务正忙于处理请求时 ,之后的 StartService 和 ControlService 都会阻塞,超时( 30 秒 )直接返回错误。
SCM 同时是一个 RPC 服务器,因此服务配置和服务控制程序可以管理远程主机上的服务。
进一步了解服务
服务属性
所有属性值详见下面的链接:
提一下 ServiceName 、 DisplayName 、 Description 这三个属性, ServiceName 才是每个服务的唯一标识,比较时不区分大小写,另外两个则是显示名称和描述,是为了帮助用户识别这个服务的作用。另外, DisplayName 还用于 NET START 命令。然而在 任务管理器 和 服务 以及其他第三方工具的界面中通常只显示两个字段( 名称和描述 ),内容则是这三个属性混用或者组合而成的,很混乱。
服务类型
OneSyncSvc_ff97b
,这个后缀是每次登录后随机生成的。服务启动类型
服务状态
服务运行后需要及时正确地向 SCM 更新自己最新的 状态 ,这样 SCP 才能通过 SCM 查询服务的状态。服务的状态决定了服务和 SCM 之间该如何交互。例如,如果服务正处于 SERVICE_STOP_PENDING 状态,则 SCM 不会向服务发送进一步的控制请求,因为此状态指示服务正在关闭。
服务的初始化状态是 SERVICE_STOPPED ,主要的合法转换过程见下图:
服务程序启动过程
看不到 StartService 的底层实现,我猜测 SCM 启动服务 的大概流程应该是这样的( 参考 ):
SCP 调用 StartService 。
SCM 准备启动服务。
获取 服务锁 。
确保同一时刻只有一个服务在启动,只有在启动服务时才会获取锁,并且不再允许除 SCM 外的其他进程控制锁。( 参考 )
从注册表中读取服务路径( ImagePath )用于启动进程( 包含启动参数 )。
从数据库查找 帐户 信息并登录,加载用户配置。
如果多个服务共享一个进程且该进程已存在则获取进程对应的连接。
否则创建新的服务进程( 将登录令牌分配给进程 ),等待建立连接。
在 入口点 中调用 StartServiceCtrlDispatcher :
默认超过 30 秒没有调用 StartServiceCtrlDispatcher 会直接终止进程。
修改超时时间 ( 重启才会生效 ):
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control ServicesPipeTimeout = 60000
连接建立时, SCM 发送 启动请求 并传递启动参数。
当收到通知用于执行 ServiceMain 的线程创建成功后, StartService 就直接返回以继续执行 SCP 中的代码( 可定期 查询 或者 监听 服务的最新状态 )。
SCM 继续等待进程退出( 表示启动失败 )或者报告 SERVICE_RUNNING 状态。
如果服务在 80 秒内没有更新其状态,则 SCM 会认为服务发生了错误,将记录一个事件并停止服务( 不确定是否会终止进程 )。
当 SCM 得知服务正在运行后便会 释放 服务锁 。
dispatcher 线程 的请求处理流程:
启动请求
新建线程调起此服务对应的 ServiceMain 并通知 SCM 线程创建成功。
调用 RegisterServiceCtrlHandler 注册 Service Control Handler 函数以监听来自 SCM 的 控制请求 。
由于调用 SetServiceStatus 依赖于 RegisterServiceCtrlHandler 返回的 服务状态句柄 ,所以只有将 RegisterServiceCtrlHandler 放置在 ServiceMain 中最开始的位置才能够在发生错误时总能将状态更新为 SERVICE_STOPPED 。
执行服务初始化逻辑并更新服务状态为 SERVICE_RUNNING 。
初始化过程应尽可能快( 不超过 1 秒 ),否则要考虑特殊方法去优化。
执行服务实际的业务逻辑。
避免直接调用 ExitThread 来结束线程,这样做会跳过清理任务而导致内存泄漏。
控制请求 ( ControlService )
在当前线程上下文中调起 Handler ,根据请求的 控制代码 执行对应的业务代码。
Handler 被调用的时候,如果业务代码会导致服务状态发生变化则一定要记得调用 SetServiceStatus ,如果不会导致状态变化则不需要调用。
控制代码 除了已规定的几种外还可以是用户自定义的代码( 128-255 )如果有 权限 的话。
什么是 Svchost
前面提到了 与其他服务共享一个进程的服务 ,可以在多个服务之间共享代码,一个常见的例子就是服务主机 Svchost.exe ,它用来托管系统内部的服务( DLL 形式 )。
微软的 文档 中明确指出了 Svchost.exe 保留供操作系统使用,不应由非 Windows 服务使用,开发者应实现自己的服务托管程序 ,所以使用 Svchost.exe 伪装自己的行为就是在 耍流氓 。
早期的 Windows 一般只有有数几个 svchost 进程,之后才慢慢增多并划分成不同的主机组,不同组的服务在不同实例中运行。服务组是根据安全需求划分的。
Windows 10 1703 起有了新的改动:内存大于 3.5 GB 的机器自动为每个共享服务( 有例外 )分配一个单独进程。所以在任务管理器中看到密密麻麻一堆 svchost 进程是很正常的现象。单独进程会造成内存开销增大( 大概 500 MB ),但好处是加强了安全性、稳定性、可扩展性,降低了故障排除的开销,以及更细致的资源管理和诊断数据。( 参考 )
Svchost 参考资料
创建 SvcHost.exe 调用的服务原理与实践
svchost.exe 启动服务原理(如何查看系统服务究竟启动了哪个文件)
svchost.exe 为什么会占用那么多 CPU?
DLL 与 UEFI 的故事之一:Windows 下运行 Dll 的三种方式
How to determine what services are running under a SVCHOST.EXE process
安全之路 —— 利用 SVCHost.exe 系统服务实现后门自启动
编写在 Svchost 中运行的 DLL
注册任何程序为服务
一个标准的 服务程序 必须包含以下几点 SCM 接口规范:
并不是说 SCM 会检测程序里是否暴露了这几个函数, 而是一个服务想要正常运行就需要进程和 SCM 之间建立连接并能够响应 SCM 发出的请求( 正确更新状态 ) ,要实现这一目的就不得不利用上面的几个函数。比如用 sc 命令可以成功创建一个记事本的服务,启动服务的时候记事本也能打开,但因为没法调用 StartServiceCtrlDispatcher , 30 秒之后就会被 SCM 强制关闭并提示启动失败。
我们日常使用的软件在设计的时候大多没有考虑以服务的形式运行,自然就不满足上面的接口规范。如果我们非要以服务的形式运行就需要在它们和 SCM 之间构建一座桥梁, SCM 通过这个 第三者 来间接控制进程,就像 Svchost 那样。
找了几个工具可以实现将任何程序注册为 合法的 Windows 服务 。工具可以放在任何位置,不必放在系统目录下,脚本在程序目录下执行。
RunAsSrv
缺失 MSVCP71.DLL 和 MSVCR71.DLL ,下载后放到 exe 同级目录下或者安装 微软常用运行库合集 可以解决。
可能会被杀软报病毒,介意者慎用。
runassrv add /cmdline:"C:\Users\test\Desktop\nginx\nginx.exe" /name:nginx_runassrv
SrvStart
很古老的工具,号称支持 Windows NT 4 之后的所有 Windows 版本。配置项很多,文档也不是很清楚,调通有点费劲。
RunAsService
需要安装 .NET Framework 3.5 。官网 下载的不是最新的代码,仅支持 exe 和 com 类型的文件作为启动命令。
需要在管理员命令提示符中运行,否则会看不到任何信息。
如果应用子进程异常关闭了会导致无法直接关闭服务,需要手动杀死 RunAsService.exe 进程。
srvany
官网已经找不到了,只能通过第三方下载( 参考 )。支持 暂停/恢复 。
由于它只是一个服务外壳并没有提供安装命令,需要自己手动创建服务:
有人专门为它写了一个管理工具 SrvanyUI ( 已内置 srvany ),还包括了对系统服务的管理,可以摆脱注册表操作。
NSSM
提供了其他工具没有的异常处理机制,注册表的结构类似于 srvany 。也可通过 Chocolatey 的方式安装, Chocolatey 中的 nginx 就是通过这种方式创建服务的。
nssm install nginx_nssm "C:\Users\test\Desktop\nginx\nginx.exe"
也可以使用命令打开服务的配置界面:
winsw
根据配置文件来加载安装和启动配置 ,类似于 SrvStart 的升级版。 V2 版本创建一个服务就要复制一份程序的做法略显笨重,在 V3 版本中增加了全局方式。
V2 版本的配置过程:
整体使用下来 NSSM 体验最好,功能多使用也方便。 winsw 也不错,毕竟有配置文件更适合复杂配置以及批量安装和迁移。而 SrvanyUI 是 UI 最强大的,上手难度最低适合普通用户。
此类工具毕竟需要高权限,出于安全方面考虑还是建议使用开源项目。
nginx 服务
nginx 说实话不太适合做成服务的形式,管理起来也比较麻烦,而且上面的工具中除了 NSSM 和 SrvStart 外都不能正常关闭服务,不是进程没杀全就是其他的异常。我更喜欢的做法:
把快捷方式丢到开始菜单的启动项(
shell:startup
)中用于开机启动。再建两个快捷方式改参数丢到任务栏工具栏中用于关闭和重启:
C# 编写服务程序
这里演示如何在 Visual Studio 中创建可向事件日志中写入消息的 Windows 服务应用程序。( 如何调试 )
具体步骤可以看 微软文档 ,只是机翻的文档很别扭,我重新整理了一下。
下载安装 Visual Studio
新版安装程序还是很清爽的,按需选择,不像以前那样一股脑安装各种不需要的东西,太占空间了。
只是为了测试,勾选 .NET 桌面开发 即可,安装占用空间( 不压缩 C 盘 )大概 7.36 G 。
新建项目
以 管理员权限 运行 VS 并新建项目 WindowsServiceTest 。
语言选择 C# ,平台选择 Windows ,选中 Windows 服务 模板。( 可直接搜索 )
修改代码
在 解决方案资源管理器 中,选择默认创建的 Service1.cs 右键重命名(
F2
)为 MyNewService.cs 并 切换到代码视图 。Program.cs
MyNewService.cs
设置属性 CanPauseAndContinue 是为了启用 暂停/恢复 功能,可以在 属性 窗口直接设置,这里直接写死在初始化代码里。
注意有两个参数:一个是构造方法中的初始化参数,另一个是 OnStart 方法中的启动参数。
生成服务程序
在 解决方案资源管理器 中,右键项目名选择 生成 。(
Ctrl + B
)生成的 exe 文件默认是不能直接运行的( 可以修改 ),需要注册为服务才行。
安装服务
可以使用通用的 sc 命令进行注册,就是详细配置会繁琐一点。
:: 注意等号与值中间有个空格 sc create MyNewServiceSC binpath= "C:\Users\test\source\repos\WindowsServiceTest\WindowsServiceTest\bin\Debug\WindowsServiceTest.exe"
也可以按教程中的方法:
添加安装程序
在 解决方案资源管理器 中,右键服务名( MyNewService.cs ) 查看设计器 ,在 设计器 窗口中右键 添加安装程序 。
修改执行用户
选中 serviceProcessInstaller1 ,在 属性 中修改 Account 为 LocalSystem 。
修改服务配置( 可选 )
选中 serviceInstaller1 ,在 属性 中修改服务名称、显示名称、描述等。
不修改就是默认的 Service1 。
重新生成服务程序(
Ctrl + B
)使安装配置生效。打开 开发者命令提示
执行安装命令
启动服务
验证结果
看不到 MyNewLog 需要先启动服务后再重新打开 事件查看器 。
因为通用逻辑都封装在 ServiceBase 里面了,只需要添加自己的业务代码,比起 C++ 实现的 demo 要简单很多。编写 多线程服务 或者复杂应用时可能会遇到一些问题( 参考 )。
参考文章
结语
以上内容是我根据日常使用经验再结合有限的资料整理而成,难免有错误的地方,欢迎指正。相关内容比较多,全部引用太占篇幅了,感兴趣的话就多点点文中给出的外链。
转载请注明出处:https://github.com/anyesu/blog/issues/41
The text was updated successfully, but these errors were encountered: