
说实话在 Windows 上做服务操作我很少开 services.msc 了。平时维护服务器、部署中间件、或者排查别人机器上莫名其妙起不来的业务服务我都是直接在命令行下敲sc和 PowerShell。一开始确实会嫌命苦因为没人愿意背命令但用顺手以后你会发现命令行服务操作能批量、能留痕、能远程还特别容易写进自动化脚本。这篇文章把我在 Windows 命令行下做服务操作的高频命令、踩坑经历和实操思路完整梳理一遍适合系统管理员、运维工程师、开发调试人员以及所有被 Windows 服务折腾过的朋友参考。1. 为什么我把服务管理从图形界面搬回命令行1.1 services.msc 的痛点看得见却摸不着重点services.msc 是 Windows 自带的服务管理图形界面打开后能看到一长串服务列表状态、启动类型、登录身份都列在那里。问题是当机器上装了数据库、消息队列、中间件、监控代理、备份客户端之后这个列表通常会超过一百项而你关心的往往只有那么几个。每次要做一次操作得先找到服务、右键、选择停止或启动然后再去确认状态有没有变成预期的样子。如果一批业务服务有启动顺序要求图形界面得在那儿盯着一项一项点效率非常低。更麻烦的是图形界面没有任何“回放”能力。你今天改了哪个服务的启动类型、把哪个服务停了几分钟一个月后要追溯完全无从查起。命令行就不一样命令本身留在脚本历史里、输出结果可以重定向到日志文件部署和排障都能形成完整证据链。这也是我在生产环境里越来越依赖命令行操作服务的原因。1.2 命令行管理服务的四个核心价值第一个价值是可脚本化。把服务启停写进.cmd或.ps1文件以后每次操作不再是重复劳动而是一次执行、处处复用。第二是批量处理。图形界面一次只能操作一个服务命令行可以用Get-Service配合管道一次性将满足条件的服务全部重启或配置。第三是精度高。服务启动失败、依赖服务缺失、账号权限不足最终的底层原因往往要靠精确命令去查比如sc queryex能看到服务对应的进程 PID图形界面给不出这种粒度。第四是合规审计。对安全要求比较高的环境服务被谁启动、启动类型何时变更系统事件日志里都有记录命令行的执行输出和脚本文件可以当作操作审批的依据。1.3 先记住三个主力工具的分工Windows 命令行下操作服务最常用的工具其实就三个sc.exe、net.exe和 PowerShell 服务相关的 cmdlet。旁边还有 CIM/WMI 接口作为“高级检索入口”。工具定位典型命令适用场景sc.exe服务控制管理器原生命令能力最全sc query、sc config、sc failure创建、删除服务修改启动类型、恢复策略、SDDL 权限net.exe老牌命令简单直接net start、net stop快速启停服务输出简短适合手工场景PowerShell cmdlet面向对象管道友好Get-Service、Start-Service、Restart-Service批量处理、条件筛选、结果格式化CIM/WMI高级属性访问接口Get-CimInstance Win32_Service查看启动账户、完整路径、延迟启动等底层信息我的使用原则很简单日常查询和批量启停优先 PowerShell涉及服务的创建、删除、恢复策略、依赖调整等底层操作直接上sc.exe它跟服务控制管理器SCM的交互最底层、最可靠不会因为封装层而丢掉参数。2. 查询服务状态sc query、net start 与 PowerShell 的三种玩法2.1 服务名和显示名最常见的命令错误来源命令行操作服务第一件事是把服务名搞对。services.msc 里看到的名称通常是“显示名”Display Name比如 Windows Update、Print Spooler、Windows Time。但 SCM 内部真正用的标识是“服务名”Service Name也就是注册表里HKLM\SYSTEM\CurrentControlSet\Services下面的子键名。Windows Update 的服务名是wuauservPrint Spooler 的服务名是SpoolerWindows Time 的服务名是W32Time。很多人执行sc start Windows Update报“指定的服务未安装”就是因为把显示名当成了服务名。获取准确服务名的姿势有三个sc query state all | findstr /i SERVICE_NAME这条命令会把所有服务名列出来配合显示名的关键词去扫。也可以直接用 PowerShellGet-Service | Select-Object Name, DisplayName, Status, StartType | Sort-Object Name查得更细一点用 CIMGet-CimInstance Win32_Service | Select-Object Name, DisplayName, State, StartMode, PathName我在排障时基本不看显示名了只看 SCM 里的服务名省得两边对不上。2.2 查询服务状态的常用命令姿势不带参数的sc query会列出所有正在运行的服务适合快速确认某台机器当前活着哪些服务sc query要列出所有服务无论是运行还是停止用sc query state all这里必须强调一个 sc 最反人类的地方state和后面的值之间必须留一个空格写成state all而不是stateall。很多网上的命令抄下来会报“错误标记不到正确的参数”十有八九就是这个空格问题。我电脑上吃过这个亏之后已经养成肌肉记忆了。查单个服务的完整状态用sc queryex Spoolersc queryex比sc query多返回一项关键信息服务的进程 PID。看到 PID 后就可以跟tasklist等其他工具联动做进程级排查。PowerShell 里等价的查询方式Get-Service -Name Spooler | Format-List *如果还想知道服务的可执行文件路径、启动账户、延迟启动标记、退出码那就直接调用 Win32_ServiceGet-CimInstance Win32_Service -Filter NameSpooler | Select-Object Name, DisplayName, State, StartMode, StartName, PathName, ProcessId, DelayedAutoStart这条命令是我个人最高频使用的服务查询语句很多图形界面里要翻好几个页面才能看到的信息一行全出来了。2.3 按进程反查服务和按服务找进程服务排障时经常遇到“进程在跑但服务显示已停止”或者“服务怎么都起不来但进程已经存在”的怪现象。这时候需要从 PID 反查服务最简单的命令是tasklist /svc输出里每行除了进程名和 PID后面会列出该进程承载的服务名格式像这样svchost.exe 1234 Services wuauserv, UsoSvc sc.exe 5678 N/A N/A如果怀疑某个进程对应哪个服务可以加findstr过滤tasklist /svc | findstr /i wuauserv反过来已知服务名要拿进程 PID用sc queryex更直接sc queryex wuauserv查出来的 PID 再拿到tasklist /fi PID eq 1234里确认可执行文件信息。这个过程在服务异常卡死时特别有用后面讲应急处理时还会再提到。2.4 服务状态码别再只看“正在运行”sc query输出里的 STATE 字段是数字加文本比如STATE : 4 RUNNING这里的数字是有意义的状态码状态说明1STOPPED服务已停止2START_PENDING服务正在启动还没进入运行态3STOP_PENDING服务正在停止还没完全退出4RUNNING服务正在运行5CONTINUE_PENDING服务正在从暂停态恢复6PAUSE_PENDING服务正在进入暂停态7PAUSED服务已暂停我遇到过不少同事看到服务“正在启动”一直卡着就直觉认为系统坏了。实际上状态是2 START_PENDING时说明服务已经在处理启动逻辑要么是程序初始化慢要么是卡在了前序操作上。此时应该查进程、查事件日志而不是反复点击“重新启动”按钮放大问题。3. 启动与停止服务依赖关系是最大的隐性门槛3.1 sc 启停和 net 启停用哪个快速启停net start和net stop是最老牌的命令net start wuauserv net stop wuauserv但net命令有几个天然的短板它没有查询单个服务详细状态的参数也没有管理恢复策略的能力输出信息也比较简略。要在命令行里更精细地控制服务生命周期推荐用sc start和sc stopsc start wuauserv sc stop wuauserv两者最大的差别在于对依赖服务的处理。net stop在停止某个服务时如果这个服务有依赖服务它会自动尝试先停止所有下游依赖服务并在输出里提示“以下服务也将会停止”。而sc stop更“死板”遇到有依赖服务的场景它会直接返回失败要求你明确自己要做的事。生产环境里我更推荐sc stop因为每一项“可能被连带停止”的风险都应该被明确感知而不是被命令悄悄带过。启动服务同理sc start会等 SCM 反馈实际状态比如刚启动的瞬间查到的是2 START_PENDING等服务完全进入运行态后才是4 RUNNING。这给脚本留出了轮询状态的空间。3.2 PowerShell 里的启停与重启技巧PowerShell 的启停命令更贴近日常用语Start-Service -Name wuauserv Stop-Service -Name wuauserv Restart-Service -Name wuauserv如果服务当前是停止状态直接执行Restart-Service会报错。常见的做法是加-Force它会先尝试停止服务再启动跳过某些常规检查。注意这个-Force并不是万能的它不能解决 SCM 对服务句柄的占用问题但确实能省去很多“先停再启”的脚本步骤。批量重启一组服务时PowerShell 的管道优势就体现出来了(ServiceA, ServiceB, ServiceC) | Get-Service | Restart-Service -Force这里的对象列表让命令变得特别干净。但也要提醒一句批量重启前务必先确认服务没有互相依赖或者依赖关系已经被序化处理否则就是“按下葫芦浮起瓢”。3.3 服务卡在“停止中”的应急处理服务停止按钮点了以后一直停在3 STOP_PENDING是 Windows 服务管理里最常见的“假死”场景。进程不退出SCM 会一直等待直到系统服务超时时间到达才会把状态更新为“已停止”。遇到这种卡死我的处理链路是这样先拿到服务进程 PIDsc queryex MySvc假设拿到 PID 是 8848先用普通方式尝试结束taskkill /pid 8848如果结束不了再加强制参数taskkill /f /pid 8848强制结束进程后立刻查一次服务状态sc queryex MySvc这时候大概率状态变成1 STOPPEDSCM 已经把“所有权”回收了。需要警告的是taskkill /f是最后手段等于直接把进程杀了不保证服务内部数据落盘可能导致配置未保存或数据文件损坏。对于数据库、消息队列这类有状态服务宁可整机重启也不要轻易/f。核心服务超时时间默认是 30 秒左右有些服务启动慢经常在 30 秒边缘被 SCM 判定“没有及时响应启动请求”。可以通过注册表把这个超时时间调大reg add HKLM\SYSTEM\CurrentControlSet\Control /v ServicesPipeTimeout /t REG_DWORD /d 120000 /f改完需要重启系统生效。一般不建议动这个值除非你非常清楚自己的服务就是这么慢。3.4 服务禁用了怎么办sc start遇到状态为 disabled 的服务会直接报 1058 错误[SC] StartService FAILED 1058: 无法启动服务因为该服务已被禁用。这不是权限问题而是服务启动类型被设成了“禁用”。解决办法是先把启动类型改成手动或自动sc config MySvc start demand sc start MySvc这里要强调sc config和sc start一样需要管理员权限普通命令行执行会得到“拒绝访问”的错误。4. 服务的创建、删除与配置修改sc config 的完整实操4.1 用 sc create 把可执行程序注册成 Windows 服务很多人在 Windows 上部署中间件或自研程序时习惯直接双击 exe然后守着命令行窗口。关掉窗口进程就没了遇到关机重启还得手动再跑非常原始。正确的做法是把程序注册成 Windows 服务交给 SCM 托管。最经典的命令是sc create MyDaemon binPath D:\apps\daemon\daemon.exe type own start auto DisplayName My Demo Service注意几点细节。第一binPath后面先空一格再写带引号的完整路径如果程序启动时需要带参数也写在同一段引号里sc create MyDaemon binPath D:\apps\daemon\daemon.exe --run-as-service start auto第二type own表示创建独立的用户服务而不是共享进程的服务。第三start auto是开机自动启动如果只想手动拉起改成start demand。创建成功后服务会出现在 services.msc 里也能用sc query MyDaemon查到。此时它还没有启动执行sc start MyDaemon如果程序自身不兼容 SCM 的服务协议启动会失败并报 1053 错误。很多第三方工具在设计时没有考虑“作为服务运行”需要老老实实走进程守护或者其他方式不能硬塞进 SCM。4.2 启动类型与启动账户的正确写法服务创建之后随时可以用sc config调整配置。最常用的就是改启动类型sc config MyDaemon start delayed-autostart支持的取值包括auto自动、demand手动、disabled禁用、delayed-auto延迟自动。另外还有boot和system两类那是给驱动程序用的普通服务不需要碰。服务运行账户默认是 LocalSystem权限极高。自建服务建议谨慎使用 LocalSystem尽量用NT AUTHORITY\LocalService或NT AUTHORITY\NetworkService这种最小权限账户sc config MyDaemon obj NT AUTHORITY\NetworkService如果服务需要访问网络上其他机器会选择 NetworkService如果只需要本地资源选 LocalService 更收敛。对于域账号还要提供密码sc config MyDaemon obj DOMAIN\svc_account password NewPassword123这里有一个很多人不知道的坑服务账户必须在系统上持有“作为服务登录”权限否则启动时直接报 1069 登录失败。用sc create创建服务时如果账户是本地账号SCM 会自动授予这个权限但如果换域账号通常要在域策略或本地安全策略里提前配置或者用ntrights这类工具授权。4.3 用 PowerShell 完成服务创建与日常属性调整PowerShell 里创建服务的命令是New-ServiceNew-Service -Name MyDaemon -BinaryPathName D:\apps\daemon\daemon.exe -DisplayName My Demo Service -StartupType Automatic日常属性调整可以用Set-ServiceSet-Service -Name MyDaemon -StartupType Manual -Description 这是服务的详细描述注意 PowerShell 的Set-Service能管的属性就那么几个启动类型、描述某些版本还能改显示名。服务账户、恢复策略、依赖关系这些底层属性它管不了。遇到这类需求我一般绕回sc config或 CIM 接口别和 PowerShell 死磕。特别是恢复策略下面第五节会专门讲。4.4 删除服务与“删除挂起”的坑删除服务前先停止服务这是基本动作sc stop MyDaemon sc delete MyDaemon正常情况下输出“DeleteService 成功”。如果服务进程还在运行或还有句柄被占用删除操作只会把服务标记为“删除挂起”此时你在services.msc里可能仍能看到这个服务甚至还能查到状态。重启系统后才能真正消失。删除服务前建议先备份配置防止误删后悔sc qc MyDaemon MyDaemon_qc_backup.txt sc sdshow MyDaemon MyDaemon_sd_backup.txtsc qc保存的是服务的启动类型、路径、启动账户、依赖关系sc sdshow保存的是服务的安全描述符SDDL。这两份文件在手哪天要还原照着反过来写一遍就有据可依。删除驱动类服务时这个习惯尤其重要。5. 生产环境必配的三件事依赖、故障恢复与延迟启动5.1 梳理服务依赖避免停一个带倒一片服务不是孤岛。比如一个业务平台依赖 MySQL 和 Redis而 MySQL 又依赖某个网络服务那么启动顺序就形成了链条。命令行查依赖关系有两个方向看某个服务依赖了哪些服务sc qc MyDaemon输出里的DEPENDENCIES字段会列出它的前置服务。看哪些服务依赖当前服务sc enumdepend MyDaemonsc enumdepend的输出会把所有下游服务列出来。生产环境停服务和启服务之前建议两条命令都跑一遍确认影响范围。我曾经在一次维护中直接sc stop一个基础服务被 SCM 拒绝原因是上面挂着三个业务服务。当时要是用net stop倒是能自动往下停但那样就会把三个业务服务统统带停责任反而更大。先查依赖再操作是服务管理的基本职业素养。5.2 用 sc failure 配置服务崩溃后的自动拉起Windows 服务进程崩溃了SCM 默认就把服务标记为停止不会自动重启。生产环境里这显然不可接受。恢复策略的配置命令是sc failure MyDaemon reset 86400 actions restart/5000/restart/10000/restart/60000这里的含义是当服务第一次失败时等待 5 秒后重启第二次失败等待 10 秒后重启第三次失败等待 60 秒后重启。reset 86400表示如果服务持续稳定运行了 24 小时没有新失败失败计数会重置为 0。恢复动作除了restart还有reboot和run。reboot是重启整台机器适合某些必须重启才能恢复的状态run是执行一段命令通常用来调用告警脚本比如先发钉钉通知再重试服务。run动作的字段顺序容易写错我建议用到时先执行一下sc failure /?看官方语法避免拼错导致策略静默失效。配置完成后可以查看sc qfailure MyDaemon恢复策略最终会写到注册表HKLM\SYSTEM\CurrentControlSet\Services\MyDaemon\FailureActions里。有一个容易漏的点部分服务还需要把“失败后的操作已启用”标记打开否则恢复动作不会真正触发。必要时可以这样处理sc failureflag MyDaemon flag yes不过绝大多数普通服务是不需要手动开这个标记的只有少数自我保护型服务会遇到。5.3 延迟自动启动避免开机风暴开机时一堆自动启动服务同时拉起来会显著拖慢系统启动还可能因为用户登录还没完成导致服务依赖失败。Windows 的“延迟自动启动”就是一种错峰启动机制sc config MyDaemon start delayed-auto配置后查一下确认sc qc MyDaemon | findstr /i DELAY输出中会出现START_TYPE : 100 DELAYED_AUTO_START。延迟自动启动的服务会在所有普通自动启动服务完成后再开始启动适合那些不着急、但又需要开机就绪的业务服务。5.4 给服务写清晰的描述和显示名自己创建的服务启动类型再好、恢复策略再完善如果描述是一条空字符串三个月后没人能想起来它是干什么的值班排障时更是噩梦。给服务写清楚描述一行命令的事sc description MyDaemon 订单中心后台服务负责处理订单状态机流转依赖 MySQL 和 Redis。同理显示名也可以改得更业务化sc config MyDaemon DisplayName 订单中心后台服务这两条信息会在 services.msc、PowerShell、事件日志里一直伴随服务属于低成本高回报的操作。6. 服务故障排查从错误码到事件日志的完整链路6.1 提权和“拒绝访问 5”服务操作里最高频的报错就是[SC] StartService FAILED 5: 拒绝访问。这个错误大多数时候只有一个原因当前命令行窗口没有管理员权限。Windows 的 UAC 对 SCM 的写操作卡得很死普通用户身份只能查询不能启停、创建、配置服务。打开管理员命令行的方式Win R 输入cmd按 Ctrl Shift Enter 启动管理员窗口PowerShell 同理。执行前可以先确认身份whoami /groups | findstr /i S-1-16-12288如果输出里有S-1-16-12288说明当前令牌包含高完整性级别是有管理员权限的。还有一种更隐蔽的“拒绝访问”情况服务的“安全描述符”里明确禁止当前用户执行 start/stop 操作。这就要看服务的 SDDLsc sdshow MyDaemon输出是一串以D:开头的安全描述符字符串。正常用户不需要去解读每一个 SID但需要知道个别软件会加固服务权限普通管理员账号也被挡在门外。这时候不要盲目sc sdset改权限先把原先的 SDDL 完整备份确认没有其他安全策略约束后再动手。6.2 常见错误码速查服务排障时错误码就是第一线索。下面这些是我在生产环境里碰到概率最高的错误码常见含义典型原因排查方向5拒绝访问未提权或服务安全描述符受限换管理员窗口sc sdshow检查1053服务没有及时响应启动请求服务初始化太慢或程序死锁sc queryex查进程抓 dump1058服务被禁用启动类型是 Disabledsc config start demand/auto1068依赖服务或组无法启动前置服务没起来或被禁用sc qc看依赖逐层启动1069服务登录失败服务账号密码错误或缺少 Logon-as-Service 权限检查账号密码核对本地策略1072服务已被标记为删除删服务时句柄未释放重启系统或释放服务句柄举个例子数据库服务起不来先看是不是 1068。如果是立刻sc qc看依赖列表然后把依赖的服务逐项启动。如果依赖项是“组”类型还需要确认这组里的服务是否在线。6.3 结合事件日志定位服务异常错误码只是结论背后的原因往往藏在事件日志里。打开事件查看器的操作太啰嗦直接用 PowerShell 查Get-WinEvent -LogName System -MaxEvents 200 | Where-Object { $_.ProviderName -eq Service Control Manager } | Select-Object TimeCreated, Id, LevelDisplayName, Message | Format-List这条命令会把最近累计的 SCM 事件全部拉出来按时间倒序展示。没有 PowerShell 的机器用wevtutil也一样wevtutil qe System /q:*[System[Provider[NameService Control Manager]]] /c:20 /rd:true /f:text四个事件 ID 值得刻进脑子里7045系统安装了一个新服务。这条最适合安全审计谁在什么时候往机器上注册了新服务一查一个准。7040服务的启动类型发生了变化。7036服务进入运行或停止状态。7000 和 7009服务启动失败、启动请求超时。我曾经排查一台机器的异常重启最终就是通过 7045 事件发现多了一个隐蔽的服务顺藤摸瓜处理掉的。服务管理的安全价值很多时候就是这么体现出来的。6.4 远程服务管理的基本姿势一台机器服务异常手边只有自己电脑也可以直接远程操作目标机器的服务。sc命令本身就支持服务器名参数sc \\server01 query MyDaemon sc \\server01 config MyDaemon start auto sc \\server01 stop MyDaemonPowerShell 的Get-Service也支持-ComputerNameGet-Service -Name MyDaemon -ComputerName server01但远程服务操作的前提是网络可达、RPC 端口开放、当前账号在目标机器上有管理员权限。跨域环境里可以用runas /netonly启动一个带指定凭据的命令行再执行远程 sc 命令避免把密码明文写进脚本。Sysinternals 工具包里的psservice也支持远程启停服务用法更友好适合对内置命令不熟的朋友。7. 最后一个锦囊把常用服务操作写进脚本7.1 我常用的 PowerShell 服务工具箱服务管理命令虽然不难但每次都敲一大串Get-CimInstance的过滤器也很烦。我把常用函数写成 PowerShell profile 里的自定义命令输入四个字母就能查完整信息。function Get-SvcDetail { param([string]$Name) Get-CimInstance Win32_Service -Filter Name$Name | Select-Object Name, DisplayName, State, StartMode, StartName, PathName, ProcessId, DelayedAutoStart } function Restart-SvcSafely { param([string[]]$Names) foreach ($n in $Names) { Stop-Service $n -Force -ErrorAction SilentlyContinue Start-Service $n Get-Service $n | Select-Object Name, Status } }有了这两个函数平时排查就是Get-SvcDetail Spooler一键批量重启就是Restart-SvcSafely ServiceA, ServiceB, ServiceCPowerShell profile 文件路径一般是$PROFILE第一次使用需要先创建。命令if (!(Test-Path $PROFILE)) { New-Item -Path $PROFILE -ItemType File -Force } notepad $PROFILE把函数贴进去保存重启 PowerShell 后就能直接调用。7.2 多服务启停的执行顺序处理写批量脚本时最怕的就是服务之间隐藏的依赖关系。我的做法是先声明一个有序列表然后按顺序执行$svcStartOrder (RedisSvc, MySQLSvc, OrderCenterSvc) $svcStartOrder | ForEach-Object { Write-Host 正在启动 $_ ... Start-Service -Name $_ Start-Sleep -Seconds 3 }每两个服务之间加一个Start-Sleep不是为了装样子而是给服务留出初始化时间避免后面服务启动时前置服务还没完全就绪。这个“启动顺序 等待窗口”的组合拳解决了很多莫名其妙的 1068 错误。我自己的习惯是把服务操作相关的命令都收进一个叫service.ps1的脚本统一用参数传入服务名这样在一台机器上反复部署测试会非常舒服。如果你也经常被 Windows 服务折腾建议从今天开始放弃鼠标右键先把最常用的十几条命令背熟很多看似复杂的服务问题其实一条sc queryex就能讲清楚了。命令行时代那些老手艺放到今天依然比点鼠标高效得多。