在具有两个以上处理器组的双套接字系统上通过ctypes在Python中使用GetLogicalProcessorInfo



所以我试图通过套接字获得处理器分组。我在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://learn.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://learn.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://learn.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://learn.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://learn.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. (https://stackoverflow.com/questions/1546355/using-enums-in-ctypes-structure/1546467#1546467)
('Reserved', BYTE * 18),
('GroupCount', WORD),
('DUMMYUNIONNAME ', _DUMMYUNIONNAME_CACHE_RELATIONSHIP)
)

class _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP(Union):
'''https://learn.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://learn.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://learn.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://learn.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://learn.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. (https://stackoverflow.com/questions/1546355/using-enums-in-ctypes-structure/1546467#1546467)
('Size', DWORD),
('DUMMYUNIONNAME', _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
)

def GetLogicalProcessorInformationEx(relation_type):
'''https://learn.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的回答/帮助!

这是我的工作解决方案:

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://learn.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://learn.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://learn.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://learn.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://learn.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. (https://stackoverflow.com/questions/1546355/using-enums-in-ctypes-structure/1546467#1546467)
('Reserved', BYTE * 18),
('GroupCount', WORD),
('DUMMYUNIONNAME ', _DUMMYUNIONNAME_CACHE_RELATIONSHIP)
)

class _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP(Union):
'''https://learn.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://learn.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://learn.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://learn.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://learn.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. (https://stackoverflow.com/questions/1546355/using-enums-in-ctypes-structure/1546467#1546467)
('Size', DWORD),
('DUMMYUNIONNAME', _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
)

def _GetLogicalProcessorInformationEx(relation_type):
'''https://learn.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://learn.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

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