CPU_ZERO "undefined symbol" 在 NASM 中使用 pthread_setaffinity_np
CPU_ZERO "undefined symbol" using pthread_setaffinity_np in NASM
我在 Ubuntu 18.04 下的 NASM 中使用 pthreads 库。线程创建工作正常,但我想使用 pthread_setaffinity_np 将每个线程分配给一个单独的核心。
以下是我用来初始化线程的代码部分。它按编写的方式编译,但在 运行 时我得到 "undefined symbol: CPU_ZERO."
使用 C 中的例子,我在程序的顶部插入了 %define _GNU_SOURCE,但我仍然得到未定义的符号 CPU_ZERO 错误。
section .data align=16
; For thread scheduling:
cpuset: times 4 dq 0
section .text
label_0:
mov rdi,ThreadID ; ThreadCount
mov rsi,pthread_attr_t ; Thread Attributes
mov rdx,Test_fn ; Function Pointer
mov rcx,pthread_arg
call pthread_create wrt ..plt
; Set affinity mask
mov rdi,cpuset
call CPU_ZERO wrt ..plt
call pthread_self wrt ..plt
push rax
mov rdi,rax
mov rsi,cpuset
call CPU_SET wrt ..plt
pop rax
mov rdi,rax
mov rsi,32
mov rdx,cpuset
call pthread_setaffinity_np wrt ..plt
; check the result with pthread_getaffinity_np
mov rax,[tcounter]
add rax,8
mov [tcounter],rax
mov rbx,[Number_Of_Cores]
cmp rax,rbx
jl label_0
我的问题是:如何在 NASM(或任何其他汇编语言;我可以翻译成 NASM)中使用 CPU_ZERO 和 CPU_SET。
感谢您的帮助。
CPU_ZERO
和 CPU_SET
是 C 宏,不是您可以调用的函数。
您必须滚动自己的函数来执行等效的归零/设置。
这些是 CPP 宏,不是函数。您可以从全部大写的名称中看出。而事实上 the man page 称它们为宏。
像往常一样,手册页的 notes section 包含对 asm 有用的详细信息:
Since CPU sets are bit masks allocated in units of long words, the
actual number of CPUs in a dynamically allocated CPU set will be
rounded up to the next multiple of sizeof(unsigned long)
. An
application should consider the contents of these extra bits to be
undefined.
Notwithstanding the similarity in the names, note that the constant
CPU_SETSIZE
indicates the number of CPUs in the cpu_set_t
data type
(thus, it is effectively a count of the bits in the bit mask), while
the setsize argument of the CPU_*_S() macros is a size in bytes.
在我的系统上(Arch Linux、glibc 2.29-4)
/usr/include/bits/cpu-set.h
说
...
#define __CPU_SETSIZE 1024
#define __NCPUBITS (8 * sizeof (__cpu_mask))
...
typedef __CPU_MASK_TYPE __cpu_mask; // ultimately unsigned long via some other headers
...
typedef struct
{
__cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS];
} cpu_set_t;
所以 cpu_set_t
是 1024 位 = 128 字节 = times 16 dq 0
或 resq 16
, 至少在我的系统上使用该内核配置.
CPU_ZERO
在您的情况下是免费的;您的静态分配 cpu_set_t
是静态零初始化的。由于某种原因,您将它放在 .data
而不是 .bss
中,因此可执行文件实际上必须包含这些零,但差异相同。
如果您确实想将堆栈中的一个归零,例如,rep stosd
是一种简单的方法,或者 xorps xmm0, xmm0
和 8x movups
存储也可以。
由于高性能不是必需的(CPU affinity-setting 代码可能只运行一次),bts
是在位图中设置位的非常方便的方法(CPU_SET
)。对于内存目标,它需要一个可以超出寻址模式选择的 dword
的位索引。 bts mem, reg
速度慢且微编码(如 Skylake 上的 10 uops),但代码大小不错。 bts mem, imm
只有3微指令,而or byte [mem + i/8], 1<<(i%8)
只有2微指令。
or
还允许您一次设置多于 1 位, 或更简单地 mov
存储一些包含所需模式的 0 和 1 的字节。
但是TL:DR:它只是一个位图,您可以根据自己的喜好使用 asm 对其进行操作,甚至可以使用非零值对其进行静态初始化。
我在 Reproduce these C types in assembly? 的回答中总结了我解决这个问题的方法。
我在 Ubuntu 18.04 下的 NASM 中使用 pthreads 库。线程创建工作正常,但我想使用 pthread_setaffinity_np 将每个线程分配给一个单独的核心。
以下是我用来初始化线程的代码部分。它按编写的方式编译,但在 运行 时我得到 "undefined symbol: CPU_ZERO."
使用 C 中的例子,我在程序的顶部插入了 %define _GNU_SOURCE,但我仍然得到未定义的符号 CPU_ZERO 错误。
section .data align=16
; For thread scheduling:
cpuset: times 4 dq 0
section .text
label_0:
mov rdi,ThreadID ; ThreadCount
mov rsi,pthread_attr_t ; Thread Attributes
mov rdx,Test_fn ; Function Pointer
mov rcx,pthread_arg
call pthread_create wrt ..plt
; Set affinity mask
mov rdi,cpuset
call CPU_ZERO wrt ..plt
call pthread_self wrt ..plt
push rax
mov rdi,rax
mov rsi,cpuset
call CPU_SET wrt ..plt
pop rax
mov rdi,rax
mov rsi,32
mov rdx,cpuset
call pthread_setaffinity_np wrt ..plt
; check the result with pthread_getaffinity_np
mov rax,[tcounter]
add rax,8
mov [tcounter],rax
mov rbx,[Number_Of_Cores]
cmp rax,rbx
jl label_0
我的问题是:如何在 NASM(或任何其他汇编语言;我可以翻译成 NASM)中使用 CPU_ZERO 和 CPU_SET。
感谢您的帮助。
CPU_ZERO
和 CPU_SET
是 C 宏,不是您可以调用的函数。
您必须滚动自己的函数来执行等效的归零/设置。
这些是 CPP 宏,不是函数。您可以从全部大写的名称中看出。而事实上 the man page 称它们为宏。
像往常一样,手册页的 notes section 包含对 asm 有用的详细信息:
Since CPU sets are bit masks allocated in units of long words, the actual number of CPUs in a dynamically allocated CPU set will be rounded up to the next multiple of
sizeof(unsigned long)
. An application should consider the contents of these extra bits to be undefined.Notwithstanding the similarity in the names, note that the constant
CPU_SETSIZE
indicates the number of CPUs in thecpu_set_t
data type (thus, it is effectively a count of the bits in the bit mask), while the setsize argument of the CPU_*_S() macros is a size in bytes.
在我的系统上(Arch Linux、glibc 2.29-4)
/usr/include/bits/cpu-set.h
说
...
#define __CPU_SETSIZE 1024
#define __NCPUBITS (8 * sizeof (__cpu_mask))
...
typedef __CPU_MASK_TYPE __cpu_mask; // ultimately unsigned long via some other headers
...
typedef struct
{
__cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS];
} cpu_set_t;
所以 cpu_set_t
是 1024 位 = 128 字节 = times 16 dq 0
或 resq 16
, 至少在我的系统上使用该内核配置.
CPU_ZERO
在您的情况下是免费的;您的静态分配 cpu_set_t
是静态零初始化的。由于某种原因,您将它放在 .data
而不是 .bss
中,因此可执行文件实际上必须包含这些零,但差异相同。
如果您确实想将堆栈中的一个归零,例如,rep stosd
是一种简单的方法,或者 xorps xmm0, xmm0
和 8x movups
存储也可以。
由于高性能不是必需的(CPU affinity-setting 代码可能只运行一次),bts
是在位图中设置位的非常方便的方法(CPU_SET
)。对于内存目标,它需要一个可以超出寻址模式选择的 dword
的位索引。 bts mem, reg
速度慢且微编码(如 Skylake 上的 10 uops),但代码大小不错。 bts mem, imm
只有3微指令,而or byte [mem + i/8], 1<<(i%8)
只有2微指令。
or
还允许您一次设置多于 1 位, 或更简单地 mov
存储一些包含所需模式的 0 和 1 的字节。
但是TL:DR:它只是一个位图,您可以根据自己的喜好使用 asm 对其进行操作,甚至可以使用非零值对其进行静态初始化。
我在 Reproduce these C types in assembly? 的回答中总结了我解决这个问题的方法。