关于强制停止服务的问题

强制停止服务

当停止某个服务的时候,因为某些为止的原因,服务的状态会一直处于STOP_PENDING,此时,就需要强制停止该服务

杀死服务对应的进程

用中文在百度搜索“杀死服务”,以及用英文在google搜索“kill service”,基本原理都一样:

找到目标服务的进程ID---PID,然后杀死即可

CMD命令

sc queryex servicename
taskkill /f /pid [PID]

PowerShell

$processId = Get-CimInstance -Class win32_service | ?{$_.Name -eq "YOURSERVICENAME"} | select -ExpandProperty ProcessId
kill $processId

如果我们要杀的是系统服务呢?

在我们的场景中,Winrm服务因为某些原因无法停止,于是我们就copy-paste以上代码去处理,结果引发了新的问题---无法访问网络路径。
简单来说,本来同一个域的server,是可以通过SMB协议访问,访问路径类似这样:\ServerName\C$\Windows。结果在杀死Winrm服务之后,无法访问了。

为什么呢?难道误杀了不成?
执行以下代码用于验证,是否其他服务也收到影响

Get-Service

"before kill"
$winrmSvc = Get-CimInstance -ClassName win32_service | ?{$_.Name -eq "winrm"}
$winrmPID = $winrmSvc.ProcessId
Stop-Process -Id $winrmPID -Force 
"after kill"    

Get-Service

结果:

WHAT?
除了Winrm服务停止之后,还有其他3个服务也被停止了。
这3个服务看样子并没有与Winrm有啥关系。
影响我们访问网络路径的服务是LanmanWorkstation(试的)
启动该服务后,另外2个服务也自动启动了。

为什么会停止其他服务?

Winrm服务和LanmanWorkstation服务有啥关系?
经过一番查证,发现二者关系就如卡巴斯基和巴基斯坦的关系。

点开瞧瞧

WHAT?
为啥binPath一毛一样?参数还一样?
是不是就是因为他们是同一个进程,所以就给一并杀了?

binPath一毛一样的还有其他服务,不管进程执行的Command Line一样,ProcessID还一样!

多个service,共用一个进程(ID)!!!

为啥?问就是为了节约资源

Svchost.exe (Service Host, or SvcHost) is a system process that can host from one or more Windows services in the Windows NT family of operating systems.[1] Svchost is essential in the implementation of shared service processes, where a number of services can share a process in order to reduce resource consumption.
https://en.wikipedia.org/wiki/Svchost.exe

经过验证,发现在客户机上(win10这样的)没有这样的情况

而在服务器上(windows server 2012 R2这样的)是共享一个进程的

解决办法

  1. 重启
  2. 杀掉共享进程前,保存其他running的service,然后重启这些service(造成波动)
  3. 待研究

问题以及更新

  1. 按照解决方案二操作,需要注意2点

杀掉共享进程前,保存其他running的service,然后重启这些service
(1)杀掉进程后立刻启动服务,可能会出错。可稍微等等,等多少时间呢,经过我的测试,1ms就可以。当然,建议多设置点时间。同时增加重试机制,确保服务一定启动。
(2)需要重新启动的服务不光只有共享进程的那些服务,还需要包括依赖这些服务的服务,以及依赖的依赖...递归会用吧。

function Get-ServiceNameChain {
    [cmdletbinding()]
    param([System.String[]]$ServiceNameList)

    $ServiceNameList| %{
        $_
        $currentService = Get-Service $_
        $dependentServices = $currentService.DependentServices
        if($dependentServices)
        {
            $dependentServiceNameList=@()
            $dependentServices| ?{$_.StartType -eq "Automatic"} | %{$dependentServiceNameList += ($_.Name)}
            Get-ServiceNameChain $dependentServiceNameList
        }
    }
}
  1. 方案二终究不是个好办法,会同时造成很多服务停止(服务依赖链上的所有服务都会停止),而且你无法保证一定能够全部正确的重启(启动要注意顺序)
    所以,最好的办法就是

把你要强制停止的服务所在的进程进行分离,不要共享进程。

顺着这个思路,找到了这个文章:
https://docs.microsoft.com/en-us/windows/application-management/svchost-service-refactoring
微软说可以修改注册表可以分离进程,但文章针对的是Desktop SKU(就是我们用的win10之类的系统,非Windows Server)

使用文中关键词“Separating SvcHost services”在google搜索,得到:
https://superuser.com/questions/860117/isolate-hosted-service-svchost-exe-in-its-own-process
https://www.quora.com/How-do-I-divide-svchost-exe-processes-into-distinct-processes

二者答案都是:

sc config your_service_name type= own
//注意,等号后面的空格是必须的

哦,这个ServiceType有点印象,创建service的时候有这个参数
我们来看看它的可设置的值:

来源:https://docs.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicetype?view=dotnet-plat-ext-6.0

上面type= own其实就是设置值为Win32OwnProcess

这...还能同时设置...

设置了发现并没有作用,偶然杀了之前的进程后,发现好像有作用。

lesson learn: 改了系统配置,没作用就重启电脑试试,先别着急放弃。

至此,最佳方案出炉。

posted @ 2022-08-02 21:27  talentzemin  阅读(950)  评论(0编辑  收藏  举报