You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何枚举分配给当前进程的所有JobObject?

问题:获取当前进程关联的所有JobObject并计算内存限制聚合值

我用C#写程序时,需要获取分配给当前进程的所有JobObject列表,进而计算内存限制的聚合值(比如最小值)。但现有QueryInformationJobObject函数仅能返回最后一个关联JobObject的限制,多JobObject场景下无法满足需求。示例代码如下:

[DllImport("kernel32.dll", SetLastError = true), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateJobObjectW(IntPtr securityAttrs, [MarshalAs(UnmanagedType.LPWStr)] string name);

[DllImport("kernel32.dll", SetLastError = true), SuppressUnmanagedCodeSecurity]
public static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);

[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool QueryInformationJobObject(IntPtr hJob, JobObjectInfoType infoClass, IntPtr info, UInt32 infoLength, IntPtr returnLength);

public static IntPtr CreateAndAssignJobObject(string name)
{
    var jobObjectHandle = CreateJobObjectW(securityAttrs: IntPtr.Zero, name);
    var processHandle = Process.GetCurrentProcess().Handle;
    AssignProcessToJobObject(jobObjectHandle, processHandle);
    return jobObjectHandle;
}

public static void LimitJobObjectMemory(IntPtr jobObjectHandle, ulong memoryLimit)
{
    // 设置JobObject内存限制的逻辑
}

/// <summary>
/// 获取JobObject的扩展限制信息
/// </summary>
private static JOBOBJECT_EXTENDED_LIMIT_INFORMATION GetExtendedLimitInformation()
{
    var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
    int infoSize = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
    IntPtr extendedInfoPtr = Marshal.AllocHGlobal(infoSize);

    try
    {
        Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);

        if (!QueryInformationJobObject(
                 IntPtr.Zero,
                 JobObjectInfoType.ExtendedLimitInformation,
                 extendedInfoPtr,
                 (UInt32)infoSize,
                 IntPtr.Zero))
        {
            throw new Exception($"QueryInformationJobObject failed; err={Marshal.GetLastWin32Error()}");
        }

        extendedInfo = (JOBOBJECT_EXTENDED_LIMIT_INFORMATION)Marshal.PtrToStructure(extendedInfoPtr, typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
    }
    finally
    {
        Marshal.FreeHGlobal(extendedInfoPtr);
    }

    return extendedInfo;
}

public static void Main()
{
    var job1Handle = CreateAndAssignJobObject("job1");
    LimitJobObjectMemory(job1Handle, 4_000_000_000);

    var job2Handle = CreateAndAssignJobObject("job2");
}

代码逻辑:创建第一个带内存限制的JobObject(job1Handle),再创建第二个JobObject(job2Handle),此时查询进程限制仅能获取最后一个JobObject的设置,请问如何获取所有关联JobObject的内存限制并计算聚合值,或枚举进程所属的所有JobObject?


解决方案

Windows原生API没有直接提供枚举进程所属所有JobObject的函数,可根据场景选择以下两种方案:

方案1:跟踪自身创建的JobObject(推荐)

如果所有JobObject都是由当前程序创建的,直接维护一个全局列表存储所有Job句柄,后续遍历列表查询每个Job的内存限制即可。这种方法简单可靠,无需额外权限。

实现代码

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

public class JobObjectManager
{
    // 维护所有创建的JobObject句柄列表
    private static readonly List<IntPtr> _jobHandles = new List<IntPtr>();

    [DllImport("kernel32.dll", SetLastError = true), SuppressUnmanagedCodeSecurity]
    public static extern IntPtr CreateJobObjectW(IntPtr securityAttrs, [MarshalAs(UnmanagedType.LPWStr)] string name);

    [DllImport("kernel32.dll", SetLastError = true), SuppressUnmanagedCodeSecurity]
    public static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool QueryInformationJobObject(IntPtr hJob, JobObjectInfoType infoClass, IntPtr info, UInt32 infoLength, out UInt32 returnLength);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoClass, IntPtr info, UInt32 infoLength);

    public enum JobObjectInfoType
    {
        ExtendedLimitInformation = 9
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
    {
        public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
        public IO_COUNTERS IoInfo;
        public UIntPtr ProcessMemoryLimit;
        public UIntPtr JobMemoryLimit;
        public UIntPtr PeakProcessMemoryUsed;
        public UIntPtr PeakJobMemoryUsed;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct JOBOBJECT_BASIC_LIMIT_INFORMATION
    {
        public Int64 PerProcessUserTimeLimit;
        public Int64 PerJobUserTimeLimit;
        public UInt32 LimitFlags;
        public UIntPtr MinimumWorkingSetSize;
        public UIntPtr MaximumWorkingSetSize;
        public UInt32 ActiveProcessLimit;
        public UIntPtr Affinity;
        public UInt32 PriorityClass;
        public UInt32 SchedulingClass;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct IO_COUNTERS
    {
        public UInt64 ReadOperationCount;
        public UInt64 WriteOperationCount;
        public UInt64 OtherOperationCount;
        public UInt64 ReadTransferCount;
        public UInt64 WriteTransferCount;
        public UInt64 OtherTransferCount;
    }

    public static IntPtr CreateAndAssignJobObject(string name)
    {
        var jobObjectHandle = CreateJobObjectW(IntPtr.Zero, name);
        if (jobObjectHandle == IntPtr.Zero)
            throw new Exception($"CreateJobObjectW failed; err={Marshal.GetLastWin32Error()}");

        var processHandle = Process.GetCurrentProcess().Handle;
        if (!AssignProcessToJobObject(jobObjectHandle, processHandle))
            throw new Exception($"AssignProcessToJobObject failed; err={Marshal.GetLastWin32Error()}");

        // 添加到全局列表
        lock (_jobHandles)
        {
            _jobHandles.Add(jobObjectHandle);
        }
        return jobObjectHandle;
    }

    public static void LimitJobObjectMemory(IntPtr jobObjectHandle, ulong memoryLimit)
    {
        var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
        extendedInfo.BasicLimitInformation.LimitFlags = 0x1000; // JOB_OBJECT_LIMIT_JOB_MEMORY
        extendedInfo.JobMemoryLimit = (UIntPtr)memoryLimit;

        int infoSize = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
        IntPtr infoPtr = Marshal.AllocHGlobal(infoSize);
        try
        {
            Marshal.StructureToPtr(extendedInfo, infoPtr, false);
            if (!SetInformationJobObject(jobObjectHandle, JobObjectInfoType.ExtendedLimitInformation, infoPtr, (uint)infoSize))
                throw new Exception($"SetInformationJobObject failed; err={Marshal.GetLastWin32Error()}");
        }
        finally
        {
            Marshal.FreeHGlobal(infoPtr);
        }
    }

    /// <summary>
    /// 获取所有关联JobObject的内存限制最小值
    /// </summary>
    public static ulong GetMinimumJobMemoryLimit()
    {
        ulong minLimit = ulong.MaxValue;
        lock (_jobHandles)
        {
            foreach (var jobHandle in _jobHandles)
            {
                var extendedInfo = GetJobExtendedLimitInformation(jobHandle);
                if (extendedInfo.JobMemoryLimit != UIntPtr.Zero)
                {
                    ulong limit = (ulong)extendedInfo.JobMemoryLimit;
                    if (limit < minLimit)
                        minLimit = limit;
                }
            }
        }
        return minLimit == ulong.MaxValue ? 0 : minLimit;
    }

    private static JOBOBJECT_EXTENDED_LIMIT_INFORMATION GetJobExtendedLimitInformation(IntPtr jobHandle)
    {
        var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
        int infoSize = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
        UInt32 returnLength;
        IntPtr infoPtr = Marshal.AllocHGlobal(infoSize);

        try
        {
            Marshal.StructureToPtr(extendedInfo, infoPtr, false);
            if (!QueryInformationJobObject(jobHandle, JobObjectInfoType.ExtendedLimitInformation, infoPtr, (uint)infoSize, out returnLength))
                throw new Exception($"QueryInformationJobObject failed; err={Marshal.GetLastWin32Error()}");

            extendedInfo = (JOBOBJECT_EXTENDED_LIMIT_INFORMATION)Marshal.PtrToStructure(infoPtr, typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
        }
        finally
        {
            Marshal.FreeHGlobal(infoPtr);
        }
        return extendedInfo;
    }

    public static void Main()
    {
        var job1Handle = CreateAndAssignJobObject("job1");
        LimitJobObjectMemory(job1Handle, 4_000_000_000);

        var job2Handle = CreateAndAssignJobObject("job2");
        LimitJobObjectMemory(job2Handle, 2_000_000_000);

        // 获取最小内存限制,结果应为2000000000
        ulong minLimit = GetMinimumJobMemoryLimit();
        Console.WriteLine($"Minimum memory limit: {minLimit}");
    }
}

方案2:枚举系统中所有JobObject(适用于非自身创建的Job)

如果需要处理非当前程序创建的JobObject,可通过NtQuerySystemInformation枚举系统中所有Job,再检查每个Job是否包含当前进程。此方法需要管理员权限,且Windows内核结构可能随版本变化,兼容性需测试。

实现代码

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

public class SystemJobEnumerator
{
    [DllImport("ntdll.dll")]
    private static extern int NtQuerySystemInformation(int systemInformationClass, IntPtr systemInformation, int systemInformationLength, out int returnLength);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool QueryInformationJobObject(IntPtr hJob, JobObjectInfoType infoClass, IntPtr info, UInt32 infoLength, out UInt32 returnLength);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CloseHandle(IntPtr hObject);

    public enum JobObjectInfoType
    {
        BasicProcessIdList = 3
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct JOBOBJECT_BASIC_PROCESS_ID_LIST
    {
        public uint NumberOfProcessIdsInList;
        public uint Reserved;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public uint[] ProcessIdList;
    }

    private const int SystemJobObjectInformation = 61;
    private const uint JOB_OBJECT_QUERY = 0x0001;

    [StructLayout(LayoutKind.Sequential)]
    private struct SYSTEM_JOBOBJECT_INFORMATION
    {
        public uint NumberOfJobs;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public JOBOBJECT_BASIC_INFORMATION[] Jobs;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct JOBOBJECT_BASIC_INFORMATION
    {
        public IntPtr JobHandle;
        public uint JobId;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string JobName;
    }

    /// <summary>
    /// 获取当前进程所属的所有JobObject句柄
    /// </summary>
    public static List<IntPtr> GetCurrentProcessJobs()
    {
        var jobHandles = new List<IntPtr>();
        int currentPid = Process.GetCurrentProcess().Id;
        int bufferSize = 4096;
        IntPtr buffer = Marshal.AllocHGlobal(bufferSize);

        try
        {
            int returnLength;
            int status = NtQuerySystemInformation(SystemJobObjectInformation, buffer, bufferSize, out returnLength);

            // 缓冲区不足,重新分配
            while (status == 0xC0000004) // STATUS_INFO_LENGTH_MISMATCH
            {
                Marshal.FreeHGlobal(buffer);
                bufferSize = returnLength;
                buffer = Marshal.AllocHGlobal(bufferSize);
                status = NtQuerySystemInformation(SystemJobObjectInformation, buffer, bufferSize, out returnLength);
            }

            if (status != 0)
                throw new Exception($"NtQuerySystemInformation failed; status=0x{status:X8}");

            var systemJobInfo = (SYSTEM_JOBOBJECT_INFORMATION)Marshal.PtrToStructure(buffer, typeof(SYSTEM_JOBOBJECT_INFORMATION));
            int jobInfoSize = Marshal.SizeOf(typeof(JOBOBJECT_BASIC_INFORMATION));

            // 遍历所有Job
            for (int i = 0; i < systemJobInfo.NumberOfJobs; i++)
            {
                IntPtr jobInfoPtr = IntPtr.Add(buffer, sizeof(uint) + i * jobInfoSize);
                var jobInfo = (JOBOBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(jobInfoPtr, typeof(JOBOBJECT_BASIC_INFORMATION));

                // 查询Job包含的进程列表
                var processList = new JOBOBJECT_BASIC_PROCESS_ID_LIST();
                int listSize = Marshal.SizeOf(typeof(JOBOBJECT_BASIC_PROCESS_ID_LIST)) + 1024 * sizeof(uint);
                IntPtr listPtr = Marshal.AllocHGlobal(listSize);
                UInt32 returnLen;

                try
                {
                    if (QueryInformationJobObject(jobInfo.JobHandle, JobObjectInfoType.BasicProcessIdList, listPtr, (uint)listSize, out returnLen))
                    {
                        processList = (JOBOBJECT_BASIC_PROCESS_ID_LIST)Marshal.PtrToStructure(listPtr, typeof(JOBOBJECT_BASIC_PROCESS_ID_LIST));
                        // 检查当前进程是否在列表中
                        for (int j = 0; j < processList.NumberOfProcessIdsInList; j++)
                        {
                            if (processList.ProcessIdList[j] == currentPid)
                            {
                                // 复制句柄(避免原句柄被关闭)
                                IntPtr dupHandle = IntPtr.Zero;
                                if (DuplicateHandle(GetCurrentProcess(), jobInfo.JobHandle, GetCurrentProcess(), out dupHandle, JOB_OBJECT_QUERY, false, 0))
                                {
                                    jobHandles.Add(dupHandle);
                                }
                                break;
                            }
                        }
                    }
                }
                finally
                {
                    Marshal.FreeHGlobal(listPtr);
                }
            }
        }
        finally
        {
            Marshal.FreeHGlobal(buffer);
        }
        return jobHandles;
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetCurrentProcess();

    // 使用示例
    public static void Main()
    {
        try
        {
            var jobs = GetCurrentProcessJobs();
            Console.WriteLine($"当前进程关联的JobObject数量:{jobs.Count}");
            // 后续可遍历jobs查询每个Job的内存限制
            foreach (var job in jobs)
            {
                // 查询内存限制逻辑...
                CloseHandle(job); // 用完关闭句柄
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"错误:{ex.Message}");
        }
    }
}

内容的提问来源于stack exchange,提问作者Yahav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 23:55:18