先说说我遇到的问题,有一台电脑不知道发了什么疯,在开机40-50分钟后会变得非常卡。我打开资源监视器看了下,原来是一个名为scvhost.exe的进程作怪,将这个进程结束掉(事实上Windows会重启这个进程),电脑就又会恢复正常了。
产生这个问题的具体原因现在还没有找到,不过我却想了一个治标的办法,就是写一个Windows服务监视系统内的进程内存占用情况,在剩余可用内存小于15%时,如发现有进程的内存占用超过总内存的45%(也可以是30%或其他数字),则杀死此进程。
我使用的操作系统为Win7旗舰版,VisualStudio版本为2012。
新建项目,找到VisualC#下的Windows服务,建立一个Windows服务项目:
关于建立Windows服务项目,我参考了这篇博客:http://yqin.iteye.com/blog/509583
我建立的项目结构如下:
ServiceMain.cs,为项目自动创建的cs文件(原名为Service1,我改名为ServiceMain),ServiceMain类继承自ServiceBase
1、CanPauseAndContinue设置为True
2、CanShutdown设置为True
3、ServiceName设置为ProcessMonitor
4、在设计器界面单击鼠标右键,点击“添加安装程序”,生成ProjectInstall.cs
ProjectInstall.cs,ProjectInstall类继承自System.Configuration.Install.Installer,其中:
System.ServiceProcess.ServiceInstaller类实例serviceInstaller
1、Description设置为“进程杀手”
2、DisplayName设置为ProcessMonitor
3、StartType设置为Automatic(包括自动、手动、禁用,这里选择自动)
System.ServiceProcess.ServiceProcessInstaller类实例serviceProcessInstaller
1、Account设置为LocalSystem(指定运行此服务的用户类型)
下面开始写代码,在ServiceMain.cs中添加如下代码:
using Microsoft.VisualBasic.Devices;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
namespace ProcessMonitor
{
public partial class ServiceMain : ServiceBase
{
/// <summary>
/// 进程监测计时器
/// </summary>
System.Timers.Timer timerScanner = new System.Timers.Timer();
LinkedList<int> lstProc = new LinkedList<int>();
/// <summary>
/// 剩余内存小于此百分比时才会杀死高内存占用进程
/// </summary>
private double PERCENT_AT = 0.15;
/// <summary>
/// 杀死内存占用百分比高于此数值的进程
/// </summary>
private double PERCENT_PT = 0.45;
public ServiceMain()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
base.OnStart(args);
EventLog.WriteEntry("PROCESS MONITOR: START");
timerScanner.Enabled = true;
timerScanner.Elapsed += timerScanner_Tick;
timerScanner.Interval = 10000;
timerScanner.Start();
}
protected override void OnPause()
{
EventLog.WriteEntry("PROCESS MONITOR: PAUSE");
base.OnPause();
}
protected override void OnContinue()
{
base.OnContinue();
EventLog.WriteEntry("PROCESS MONITOR: CONTINUE");
}
protected override void OnStop()
{
EventLog.WriteEntry("PROCESS MONITOR: STOP");
base.OnStop();
}
private void timerScanner_Tick(object sender, EventArgs e)
{
try
{
EventLog.WriteEntry("PROCESS MONITOR: CHECK PROCESSES...");
ComputerInfo cInfo = new ComputerInfo();
long totalPhyMem = Convert.ToInt64(cInfo.TotalPhysicalMemory);
long availablePhyMem = Convert.ToInt64(cInfo.AvailablePhysicalMemory);
double percentATMem = availablePhyMem * 1.0 / totalPhyMem;
if (percentATMem < PERCENT_AT)
{
foreach (Process proc in Process.GetProcesses())
{
long procMem = proc.WorkingSet64;
double percentPTMem = procMem * 1.0 / totalPhyMem;
if (percentPTMem > PERCENT_PT && !lstProc.Contains(proc.Id))
{
EventLog.WriteEntry("PROCESS MONITOR: KILL PROCESS " + proc.ProcessName);
proc.Kill();
}
}
}
}
catch (Exception ex)
{
EventLog.WriteEntry(ex.Message, EventLogEntryType.Error);
}
}
}
}
关于这段代码,有以下几点说明:
1、需要手动添加引用Microsoft.VisualBasic,支持对命名空间Microsoft.VisualBasic.Devices的引用。
2、Timer千万不要从可视化编辑器的组件库中拖入,因为Windows服务不支持System.Windows.Forms.Timer,我们使用的Timer是System.Timers.Timer。
3、不要在代码中使用诸如MessageBox之类的UI,写日志可以使用EventLog,记录下的日志可以在Windows事件查看器中查到。
有关EventLog的使用说明,可参考相关MSDN页面:
https://msdn.microsoft.com/en-us/library/system.diagnostics.eventlog(VS.71).aspx
代码编写完毕后,生成解决方案,此时可在Debug目录中找到编译好的ProcessMonitor.exe。
安装服务可以通过VS提供的InstallUtil工具实现,用管理员身份打开VS开发人员命令提示,输入命令:
InstallUtil ProcessMonitor.exe所在绝对路径
安装完毕后,就可以在Windows的服务列表中找到此服务了
启动该服务后,ProcessMonitor就可以正式开始工作了!
我们可在事件查看器中找到EventLog打印的相关日志:
如果需要卸载此服务,在安装命令的基础上,添加一个命令行参数/u即可:
InstallUtil /u ProcessMonitor.exe所在绝对路径
这些内容都会被同时写入ProcessMonitor.exe同目录下的日志文件ProcessMonitor.InstallLog中。
附:本程序的工程文件可在此地址下载:http://pan.baidu.com/s/1kVCV0ov
END