转自:windows下限制进程的CPU和内存使用

缘起

有时候我们想要限制某个程序的CPU,使其最多占用20%的CPU;有时候我们想要限制程序的内存,使其最多占用2GB的内存。Linux下可以通过cgroup来实现,windows下怎么办呢……

这一点可以通过Windows API中的JobObject来实现~

官方文档:

作业对象 - Win32 apps

Github上别人写好的工具(不过这是用C#写的):

https://github.com/lowleveldesign/process-governor

简介

简单来说就是windows提供了一个叫做JobObject的东东,可以限制进程的CPU利用率、CPU核心、内存、网络带宽、管理员权限等等。功能还是很强大的……

使用流程如下:

  1. 通过CreateJobObjectA创建一个 JobObject
  2. 创建一个job information的结构体,设置自己要控制的值。有很多不同的结构体声明,分别用来控制不同的属性……
  3. 通过SetInformationJobObject来将刚才创建的information设给JobObject
  4. 通过AssignProcessToJobObject来将某个进程分配给Job Object,这样,这个进程就会受到Job Object的控制了

例程

这个是我随手糊的,反正跑起来就行了,所以代码写得比较烂,异常和错误也没处理,但反正效果是达到了…… 例子里只限制了CPU……(发现Github上有现成的程序之后就懒得去写内存限制了,反正就是用一个不同的information而已……)

注意限制子进程不需要管理员权限。如果要限制已经存在的进程,就需要用管理员权限运行。

snippet.cpp
#include <windows.h>
#include <stdexcept>
#include <iostream>
#include <string>
using namespace std::string_literals;
 
class Process
{
public:
    Process() = default;
    Process(const Process&) = delete;
    Process operator=(const Process&) = delete;
    virtual ~Process() {};
    virtual HANDLE handle() const = 0;
    virtual void wait() const = 0;
};
 
class AttachProcess : public Process
{
private:
    HANDLE h;
public:
    explicit AttachProcess(DWORD pid)
    {
        h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
        if (h == NULL)
            throw std::runtime_error("Failed to open process");
    }
    ~AttachProcess() { CloseHandle(h); }
    HANDLE handle() const override { return h; }
    void wait() const override
    {
        WaitForSingleObject(h, INFINITE);
    }
};
 
class ChildProcess : public Process
{
private:
    STARTUPINFOA si;
    PROCESS_INFORMATION pi;
public:
    ChildProcess(const ChildProcess&) = delete;
    ChildProcess operator=(const ChildProcess&) = delete;
    explicit ChildProcess(LPCSTR applicationPath)
    {
        ZeroMemory(&si, sizeof(si));
        si.cb = sizeof(si);
        ZeroMemory(&pi, sizeof(pi));
        if (!CreateProcessA(applicationPath,
            NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL,
            &si, &pi))
        {
            throw std::runtime_error("Failed to create child process!");
        }
    }
    void start()
    {
        ResumeThread(pi.hThread);
    }
    void wait() const override
    {
        WaitForSingleObject(pi.hProcess, INFINITE);
    }
    HANDLE handle() const override
    {
        return pi.hProcess;
    }
    ~ChildProcess()
    {
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
};
 
class JobObjectInformation
{
public:
    JobObjectInformation() = default;
    JobObjectInformation(const JobObjectInformation&) = delete;
    JobObjectInformation operator=(const JobObjectInformation&) = delete;
    virtual JOBOBJECTINFOCLASS getClass() const = 0;
    virtual DWORD length() const = 0;
    virtual LPVOID informationPtr() const = 0;
    virtual ~JobObjectInformation() {};
};
 
class JobObject
{
public:
    JobObject(const JobObject&) = delete;
    JobObject operator=(const JobObject&) = delete;
    JobObject()
    {
        jobHandle = CreateJobObjectA(NULL, NULL);
        if (jobHandle == NULL) {
            throw std::runtime_error("Create job failed!");
        }
    }
    HANDLE getHandle()
    {
        return jobHandle;
    }
    void assignProcess(const Process &p)
    {
        if (AssignProcessToJobObject(jobHandle, p.handle()) == 0)
            throw std::runtime_error("Failed to assgin process to job: "s + std::to_string(GetLastError()));
    }
    BOOL setInformation(const JobObjectInformation& i)
    {
        return SetInformationJobObject(jobHandle, i.getClass(), i.informationPtr(), i.length());
    }
    ~JobObject()
    {
        CloseHandle(jobHandle);
    }
 
private:
    HANDLE jobHandle{};
};
 
class CpuRateJobObjectInformation : public JobObjectInformation
{
public:
    CpuRateJobObjectInformation(int rate)
    {
        information.ControlFlags = JOB_OBJECT_CPU_RATE_CONTROL_ENABLE | JOB_OBJECT_CPU_RATE_CONTROL_HARD_CAP;
        if (rate <= 0 || rate > 100)
            throw std::runtime_error("Invalid argument");
        information.CpuRate = rate * 100;
    }
    virtual JOBOBJECTINFOCLASS getClass() const override
    {
        return JOBOBJECTINFOCLASS::JobObjectCpuRateControlInformation;
    }
    virtual DWORD length() const override
    {
        return sizeof(information);
    }
    virtual LPVOID informationPtr() const override
    {
        return (LPVOID) &information;
    }
private:
    JOBOBJECT_CPU_RATE_CONTROL_INFORMATION information{};
};
 
int main(int argc, const char **argv)
{
    JobObject jobObject;
    CpuRateJobObjectInformation information{20};
    jobObject.setInformation(information);
    ChildProcess childProcess{ "C:\\xiangqi.exe"};
 
    jobObject.assignProcess(childProcess);
    childProcess.start();
    childProcess.wait();
 
    DWORD pid;
    std::cout << "Input PID:" << std::endl;
    std::cin >> pid;
    AttachProcess attachProcess{ pid };
    try
    {
        jobObject.assignProcess(attachProcess);
    }
    catch (const std::exception& e)
    {
        std::cout << e.what();
    }
    attachProcess.wait();
    return 0;
}

参考