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_ZEROCPU_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 0resq 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? 的回答中总结了我解决这个问题的方法。