在具有两个以上处理器组的双插槽系统上通过 ctypes 在 Python 中使用 GetLogicalProcessorInformationEx()

Using GetLogicalProcessorInformationEx() in Python via ctypes on a two socket system with more than two processor groups

所以我正在尝试通过套接字获取处理器分组。我有一个用 C++ 编写的 POC,输出如下。

[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,],[0,1,2,3,4,5,6,7,8,9,10,11,],]
[[12,13,14,15,16,17,18,19,20,21,22,23,],[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,],]

虽然我可以像下面那样做一些事情,但我更愿意保持代码的连续性并保持项目的纯粹性 python。另外,为了体验,这似乎是一个很好的谜题。

socket_groups = eval(run_cmd_popen("get_logical_processor_information.exe", return_output=True).strip("\n"))

我到目前为止的工作,但只在一个套接字系统上,对于两个套接字系统,第二个套接字似乎没有任何数据,并打印以下内容.

[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,][0,1,2,3,4,5,6,7,8,9,10,11,]]
[]

这是我目前的情况:

import os
import math
import platform
from ctypes.wintypes import BYTE, INT, WORD, DWORD, BOOL, PDWORD, UINT
from ctypes import Structure, Union, WinDLL, c_uint64, POINTER, byref, get_last_error, WinError

from wmi import WMI


if platform.machine().endswith('64'):
    KAFFINITY = c_uint64
else:
    KAFFINITY = UINT

wmi = WMI()
NUM_SOCKETS = 0
for wmi_obj in wmi.query(f'SELECT * FROM Win32_processor '):
    if wmi_obj is not None:
        NUM_SOCKETS += 1

NUM_CPUS = os.cpu_count()
NUM_CPU_GROUPS = math.ceil(NUM_CPUS / 64)
ANYSIZE_ARRAY = NUM_CPU_GROUPS

ERROR_INSUFFICIENT_BUFFER = 122

RELATION_CACHE = 2
RELATION_NUMA_NODE = 1
RELATION_PROCESSOR_CORE = 0
RELATION_PROCESSOR_PACKAGE = 3
RELATION_GROUP = 4
RELATION_ALL = 0xffff


class _GROUP_AFFINITY(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_affinity
        typedef struct _GROUP_AFFINITY {
            KAFFINITY Mask;
            WORD      Group;
            WORD      Reserved[3];
        } GROUP_AFFINITY, *PGROUP_AFFINITY;
    '''
    _fields_ = (
        ('Mask', KAFFINITY),
        ('Group', WORD),
        ('Reserved', WORD * 3)
    )


class _PROCESSOR_GROUP_INFO(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_group_info
        typedef struct _PROCESSOR_GROUP_INFO {
            BYTE      MaximumProcessorCount;
            BYTE      ActiveProcessorCount;
            BYTE      Reserved[38];
            KAFFINITY ActiveProcessorMask;
        } PROCESSOR_GROUP_INFO, *PPROCESSOR_GROUP_INFO;
    '''
    _fields_ = (
        ('MaximumProcessorCount', BYTE),
        ('ActiveProcessorCount', BYTE),
        ('Reserved', BYTE * 38),
        ('ActiveProcessorMask', KAFFINITY),
    )


class _GROUP_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_relationship
        typedef struct _GROUP_RELATIONSHIP {
            WORD                 MaximumGroupCount;
            WORD                 ActiveGroupCount;
            BYTE                 Reserved[20];
            PROCESSOR_GROUP_INFO GroupInfo[ANYSIZE_ARRAY];
        } GROUP_RELATIONSHIP, *PGROUP_RELATIONSHIP;
    '''
    _fields_ = (
        ('MaximumGroupCount', WORD),
        ('ActiveGroupCount', WORD),
        ('Reserved', BYTE * 20),
        ('GroupInfo', _PROCESSOR_GROUP_INFO * ANYSIZE_ARRAY)
    )


class _DUMMYUNIONNAME_CACHE_RELATIONSHIP(Union):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship
        union {
            GROUP_AFFINITY GroupMask;
            GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
        } DUMMYUNIONNAME;
    '''
    _fields_ = (
        ('GroupMask', _GROUP_AFFINITY),
        ('GroupMasks', _GROUP_AFFINITY * ANYSIZE_ARRAY)  # * number of groups
    )


class _CACHE_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship
        typedef struct _CACHE_RELATIONSHIP {
            BYTE                 Level;
            BYTE                 Associativity;
            WORD                 LineSize;
            DWORD                CacheSize;
            PROCESSOR_CACHE_TYPE Type;
            BYTE                 Reserved[18];
            WORD                 GroupCount;
            union {
                GROUP_AFFINITY GroupMask;
                GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
            } DUMMYUNIONNAME;
        } CACHE_RELATIONSHIP, *PCACHE_RELATIONSHIP;
    '''
    _fields_ = (
        ('Level', BYTE),
        ('Associativity', BYTE),
        ('LineSize', WORD),
        ('CacheSize', DWORD),
        ('Type', INT),  # PROCESSOR_CACHE_TYPE is an Enum type, which, At least for GCC, is just a simple numeric type. (
        ('Reserved', BYTE * 18),
        ('GroupCount', WORD),
        ('DUMMYUNIONNAME ', _DUMMYUNIONNAME_CACHE_RELATIONSHIP)
    )


class _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP(Union):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship
        union {
            GROUP_AFFINITY GroupMask;
            GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
        } DUMMYUNIONNAME;
    '''
    _fields_ = (
        ('GroupMask', _GROUP_AFFINITY),
        ('GroupMasks', _GROUP_AFFINITY * ANYSIZE_ARRAY)  # * number of groups
    )


class _NUMA_NODE_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship
        typedef struct _NUMA_NODE_RELATIONSHIP {
            DWORD NodeNumber;
            BYTE  Reserved[18];
            WORD  GroupCount;
            union {
                GROUP_AFFINITY GroupMask;
                GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
            } DUMMYUNIONNAME;
        } NUMA_NODE_RELATIONSHIP, *PNUMA_NODE_RELATIONSHIP;
    '''
    _fields_ = (
        ('NodeNumber', DWORD),
        ('Reserved', BYTE * 18),
        ('GroupCount', WORD),
        ('DUMMYUNIONNAME', _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP)
    )


class _PROCESSOR_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_relationship
        typedef struct _PROCESSOR_RELATIONSHIP {
            BYTE           Flags;
            BYTE           EfficiencyClass;
            BYTE           Reserved[20];
            WORD           GroupCount;
            GROUP_AFFINITY GroupMask[ANYSIZE_ARRAY];
        } PROCESSOR_RELATIONSHIP, *PPROCESSOR_RELATIONSHIP;
    '''
    _fields_ = (
        ('Flags', BYTE),
        ('EfficiencyClass', BYTE),
        ('Reserved', BYTE * 20),
        ('GroupCount', WORD),
        ('GroupMask', _GROUP_AFFINITY * ANYSIZE_ARRAY)  # * number of groups
    )


class _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Union):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
            union {
                PROCESSOR_RELATIONSHIP Processor;
                NUMA_NODE_RELATIONSHIP NumaNode;
                CACHE_RELATIONSHIP     Cache;
                GROUP_RELATIONSHIP     Group;
            } DUMMYUNIONNAME;
    '''
    _fields_ = (
        ('Processor', _PROCESSOR_RELATIONSHIP),
        ('NumaNode', _NUMA_NODE_RELATIONSHIP),
        ('Cache', _CACHE_RELATIONSHIP),
        ('Group', _GROUP_RELATIONSHIP)
    )


class _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
        typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX {
            LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
            DWORD                          Size;
            union {
                PROCESSOR_RELATIONSHIP Processor;
                NUMA_NODE_RELATIONSHIP NumaNode;
                CACHE_RELATIONSHIP     Cache;
                GROUP_RELATIONSHIP     Group;
            } DUMMYUNIONNAME;
        } SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
    '''
    _anonymous_ = 'DUMMYUNIONNAME',
    _fields_ = (
        ('Relationship', INT),  # _LOGICAL_PROCESSOR_RELATIONSHIP is an Enum type, which, At least for GCC, is just a simple numeric type. (
        ('Size', DWORD),
        ('DUMMYUNIONNAME', _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
    )


def GetLogicalProcessorInformationEx(relation_type):
    '''https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
        BOOL GetLogicalProcessorInformationEx(
            [in]            LOGICAL_PROCESSOR_RELATIONSHIP           RelationshipType,
            [out, optional] PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
            [in, out]       PDWORD                                   ReturnedLength
        );
    '''
    dll = WinDLL('kernel32', use_last_error=True)
    dll.GetLogicalProcessorInformationEx.argtypes = INT, POINTER(_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX), PDWORD
    dll.GetLogicalProcessorInformationEx.restype = BOOL

    byte_len = DWORD()

    # Call with null buffer to get required buffer size
    result = dll.GetLogicalProcessorInformationEx(relation_type, None, byref(byte_len))
    if (err := get_last_error()) != ERROR_INSUFFICIENT_BUFFER:
        raise WinError(err)

    # Allocate buffer
    allocated_structure = (_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * NUM_SOCKETS)()

    global byte_len_for_advance
    byte_len_for_advance = byte_len

    # Now do the call with the allocated buffer to fill it up
    result = dll.GetLogicalProcessorInformationEx(relation_type, allocated_structure, byref(byte_len))
    if not result:
        raise WinError(get_last_error())

    return allocated_structure


def _print_mask(mask):
    # print(f"{mask=}")
    digits = [int(x) for x in mask]

    group_core_list = []
    print("[", end='')
    core = 0
    for i in digits:
        if i == 1:
            print(f"{core},", end='')
            group_core_list.append(core)
        core += 1
    print("]", end='')

    # print(group_core_list)


if __name__ == "__main__":
    enum_info = GetLogicalProcessorInformationEx(RELATION_PROCESSOR_PACKAGE)
    for p_info in enum_info:
        print("[", end='')
        for group_index in range(0, p_info.Processor.GroupCount):
            _print_mask(f"{p_info.Processor.GroupMask[group_index].Mask:8b}")
        print("]\n", end='')
    breakpoint

我想我可能需要做一些事情,将指针转移到下一个结构?但我不知道该怎么做。此外,我不确定 _print_mask() 是否会在我获得第二个套接字信息时完全按照我的要求进行操作,但这是以后的问题。

想上网吗?


编辑

感谢 Mark Tolonen 的 answer/help!

这是我的工作解决方案:

from typing import Dict, List
from ctypes.wintypes import BYTE, INT, WORD, DWORD, BOOL, PDWORD, WPARAM
from ctypes import Structure, Union, WinDLL, c_char_p, POINTER, byref, cast, get_last_error, WinError, create_string_buffer

KAFFINITY = WPARAM   # WPARAM is 32-bit or 64-bit unsigned depending on platform

ANYSIZE_ARRAY = 1

ERROR_INSUFFICIENT_BUFFER = 122

RELATION_CACHE = 2
RELATION_NUMA_NODE = 1
RELATION_PROCESSOR_CORE = 0
RELATION_PROCESSOR_PACKAGE = 3
RELATION_GROUP = 4
RELATION_ALL = 0xffff


class _GROUP_AFFINITY(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_affinity
        typedef struct _GROUP_AFFINITY {
            KAFFINITY Mask;
            WORD      Group;
            WORD      Reserved[3];
        } GROUP_AFFINITY, *PGROUP_AFFINITY;
    '''
    _fields_ = (
        ('Mask', KAFFINITY),
        ('Group', WORD),
        ('Reserved', WORD * 3)
    )


class _PROCESSOR_GROUP_INFO(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_group_info
        typedef struct _PROCESSOR_GROUP_INFO {
            BYTE      MaximumProcessorCount;
            BYTE      ActiveProcessorCount;
            BYTE      Reserved[38];
            KAFFINITY ActiveProcessorMask;
        } PROCESSOR_GROUP_INFO, *PPROCESSOR_GROUP_INFO;
    '''
    _fields_ = (
        ('MaximumProcessorCount', BYTE),
        ('ActiveProcessorCount', BYTE),
        ('Reserved', BYTE * 38),
        ('ActiveProcessorMask', KAFFINITY),
    )


class _GROUP_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_relationship
        typedef struct _GROUP_RELATIONSHIP {
            WORD                 MaximumGroupCount;
            WORD                 ActiveGroupCount;
            BYTE                 Reserved[20];
            PROCESSOR_GROUP_INFO GroupInfo[ANYSIZE_ARRAY];
        } GROUP_RELATIONSHIP, *PGROUP_RELATIONSHIP;
    '''
    _fields_ = (
        ('MaximumGroupCount', WORD),
        ('ActiveGroupCount', WORD),
        ('Reserved', BYTE * 20),
        ('GroupInfo', _PROCESSOR_GROUP_INFO * ANYSIZE_ARRAY)
    )


class _DUMMYUNIONNAME_CACHE_RELATIONSHIP(Union):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship
        union {
            GROUP_AFFINITY GroupMask;
            GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
        } DUMMYUNIONNAME;
    '''
    _fields_ = (
        ('GroupMask', _GROUP_AFFINITY),
        ('GroupMasks', _GROUP_AFFINITY * ANYSIZE_ARRAY)
    )


class _CACHE_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship
        typedef struct _CACHE_RELATIONSHIP {
            BYTE                 Level;
            BYTE                 Associativity;
            WORD                 LineSize;
            DWORD                CacheSize;
            PROCESSOR_CACHE_TYPE Type;
            BYTE                 Reserved[18];
            WORD                 GroupCount;
            union {
                GROUP_AFFINITY GroupMask;
                GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
            } DUMMYUNIONNAME;
        } CACHE_RELATIONSHIP, *PCACHE_RELATIONSHIP;
    '''
    _fields_ = (
        ('Level', BYTE),
        ('Associativity', BYTE),
        ('LineSize', WORD),
        ('CacheSize', DWORD),
        ('Type', INT),  # PROCESSOR_CACHE_TYPE is an Enum type, which, At least for GCC, is just a simple numeric type. (
        ('Reserved', BYTE * 18),
        ('GroupCount', WORD),
        ('DUMMYUNIONNAME ', _DUMMYUNIONNAME_CACHE_RELATIONSHIP)
    )


class _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP(Union):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship
        union {
            GROUP_AFFINITY GroupMask;
            GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
        } DUMMYUNIONNAME;
    '''
    _fields_ = (
        ('GroupMask', _GROUP_AFFINITY),
        ('GroupMasks', _GROUP_AFFINITY * ANYSIZE_ARRAY)
    )


class _NUMA_NODE_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship
        typedef struct _NUMA_NODE_RELATIONSHIP {
            DWORD NodeNumber;
            BYTE  Reserved[18];
            WORD  GroupCount;
            union {
                GROUP_AFFINITY GroupMask;
                GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
            } DUMMYUNIONNAME;
        } NUMA_NODE_RELATIONSHIP, *PNUMA_NODE_RELATIONSHIP;
    '''
    _fields_ = (
        ('NodeNumber', DWORD),
        ('Reserved', BYTE * 18),
        ('GroupCount', WORD),
        ('DUMMYUNIONNAME', _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP)
    )


class _PROCESSOR_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_relationship
        typedef struct _PROCESSOR_RELATIONSHIP {
            BYTE           Flags;
            BYTE           EfficiencyClass;
            BYTE           Reserved[20];
            WORD           GroupCount;
            GROUP_AFFINITY GroupMask[ANYSIZE_ARRAY];
        } PROCESSOR_RELATIONSHIP, *PPROCESSOR_RELATIONSHIP;
    '''
    _fields_ = (
        ('Flags', BYTE),
        ('EfficiencyClass', BYTE),
        ('Reserved', BYTE * 20),
        ('GroupCount', WORD),
        ('GroupMask', _GROUP_AFFINITY * ANYSIZE_ARRAY)
    )


class _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Union):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
            union {
                PROCESSOR_RELATIONSHIP Processor;
                NUMA_NODE_RELATIONSHIP NumaNode;
                CACHE_RELATIONSHIP     Cache;
                GROUP_RELATIONSHIP     Group;
            } DUMMYUNIONNAME;
    '''
    _fields_ = (
        ('Processor', _PROCESSOR_RELATIONSHIP),
        ('NumaNode', _NUMA_NODE_RELATIONSHIP),
        ('Cache', _CACHE_RELATIONSHIP),
        ('Group', _GROUP_RELATIONSHIP)
    )


class _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
        typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX {
            LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
            DWORD                          Size;
            union {
                PROCESSOR_RELATIONSHIP Processor;
                NUMA_NODE_RELATIONSHIP NumaNode;
                CACHE_RELATIONSHIP     Cache;
                GROUP_RELATIONSHIP     Group;
            } DUMMYUNIONNAME;
        } SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
    '''
    _anonymous_ = 'DUMMYUNIONNAME',
    _fields_ = (
        ('Relationship', INT),  # _LOGICAL_PROCESSOR_RELATIONSHIP is an Enum type, which, At least for GCC, is just a simple numeric type. (
        ('Size', DWORD),
        ('DUMMYUNIONNAME', _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
    )


def _GetLogicalProcessorInformationEx(relation_type):
    '''https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
        BOOL GetLogicalProcessorInformationEx(
            [in]            LOGICAL_PROCESSOR_RELATIONSHIP           RelationshipType,
            [out, optional] PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
            [in, out]       PDWORD                                   ReturnedLength
        );
    '''
    dll = WinDLL('kernel32', use_last_error=True)

    # Manage the allocated buffer as a c_char_p for easier pointer manipulation
    dll.GetLogicalProcessorInformationEx.argtypes = INT, c_char_p, PDWORD
    dll.GetLogicalProcessorInformationEx.restype = BOOL

    byte_len = DWORD()

    # Call with null buffer to get required buffer size
    result = dll.GetLogicalProcessorInformationEx(relation_type, None, byref(byte_len))
    if (err := get_last_error()) != ERROR_INSUFFICIENT_BUFFER:
        raise WinError(err)

    # Allocate byte buffer
    buffer = create_string_buffer(byte_len.value)

    # Now do the call with the buffer to fill it up
    result = dll.GetLogicalProcessorInformationEx(relation_type, buffer, byref(byte_len))
    if not result:
        raise WinError(get_last_error())

    return buffer, byte_len.value   # return buffer and length


def get_socket_to_group_mappings() -> List[Dict[int, List[int]]]:
    '''
    '''
    # The returned structure array can vary in element size, so walk the buffer by the size of its element.
    buffer, buffer_len = _GetLogicalProcessorInformationEx(RELATION_PROCESSOR_PACKAGE)

    offset = 0
    sockets = []
    while offset < buffer_len:
        # Cast the current offset to the structure and extract contents
        processor_info = cast(byref(buffer, offset), POINTER(_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)).contents

        # print(f"{processor_info.Size=} {processor_info.Processor.GroupCount=}")

        # Cast the group mask array to the correct size
        group_infos = cast(byref(processor_info.Processor.GroupMask), POINTER(_GROUP_AFFINITY * processor_info.Processor.GroupCount)).contents

        groups = {}
        for group_info in group_infos:
            # print(f"{group_affinity.Mask=:016X} {group_affinity.Group=}")

            core_index = 0
            group_cores = []
            # Convert the mask to the binary representation of an 8-digit, zero-padded on the left string.
            # Then turn that string of 1's and 0's into a list, and reverse it.
            for i in [int(x) for x in f"{group_info.Mask:8b}"][::-1]:
                if i == 1:
                    # If the this core is part of this socket in this group (indicated by a 1 in the mask).
                    # Add the groups core number to the groups core list
                    group_cores.append(core_index)
                core_index += 1

            # Add this group to this sockets groups dict
            groups[group_info.Group] = group_cores

        # Add this sockets group info to the systems socket list
        sockets.append(groups)

        # Advance by the size consumed
        offset += processor_info.Size

    return sockets

我只有一个带有 8 个逻辑处理器的单核,所以我无法验证我的一切是否适合您的情况,但试试这个:

from ctypes.wintypes import BYTE, INT, WORD, DWORD, BOOL, PDWORD, UINT, WPARAM
from ctypes import (Structure, Union, WinDLL, c_uint64, POINTER, byref, cast,
                    get_last_error, WinError, c_char_p, create_string_buffer)

KAFFINITY = WPARAM   # WPARAM is 32-bit or 64-bit unsigned depending on platform
ANYSIZE_ARRAY = 1    # actual definition

#
# unchanged portion elided
#

def GetLogicalProcessorInformationEx(relation_type):
    '''https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
        BOOL GetLogicalProcessorInformationEx(
            [in]            LOGICAL_PROCESSOR_RELATIONSHIP           RelationshipType,
            [out, optional] PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
            [in, out]       PDWORD                                   ReturnedLength
        );
    '''
    dll = WinDLL('kernel32', use_last_error=True)
    # manage the allocated buffer as a c_char_p for easier pointer manipulation
    dll.GetLogicalProcessorInformationEx.argtypes = INT, c_char_p, PDWORD
    dll.GetLogicalProcessorInformationEx.restype = BOOL

    byte_len = DWORD()

    # Call with null buffer to get required buffer size
    result = dll.GetLogicalProcessorInformationEx(relation_type, None, byref(byte_len))
    if (err := get_last_error()) != ERROR_INSUFFICIENT_BUFFER:
        raise WinError(err)

    # Allocate byte buffer
    buffer = create_string_buffer(byte_len.value)

    # Now do the call with the buffer to fill it up
    result = dll.GetLogicalProcessorInformationEx(relation_type, buffer, byref(byte_len))
    if not result:
        raise WinError(get_last_error())

    return buffer,byte_len.value   # return buffer and length


if __name__ == "__main__":
    # The returned structure array can vary in element size,
    # so walk the buffer by the size of its element.
    buffer,len = GetLogicalProcessorInformationEx(RELATION_PROCESSOR_PACKAGE)
    offset = 0
    while offset < len:
        # cast the current offset to the structure and extract contents
        info = cast(byref(buffer,offset), POINTER(_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)).contents
        print(f'{info.Size=} {info.Processor.GroupCount=}')

        # cast the group mask array to the correct size
        gaffs = cast(byref(info.Processor.GroupMask),POINTER(_GROUP_AFFINITY * info.Processor.GroupCount)).contents
        for gaff in gaffs:
            print(f'{gaff.Mask=:016X} {gaff.Group=}')

        # advance by the size consumed
        offset += info.Size

在我的一个核心、8 个逻辑进程系统上的输出:

info.Size=48 info.Processor.GroupCount=1
gaff.Mask=00000000000000FF gaff.Group=0

如果这对您的复杂系统不起作用,post 我的解决方案返回的 buffer,len 由您的系统返回,我会修复它。