Ответ 1
Нормальные глобальные переменные не относятся к процессору. Автоматические переменные находятся в стеке, а разные процессоры используют разные стеки, поэтому, естественно, они получают отдельные переменные.
Я думаю, вы имеете в виду инфраструктуру переменных Linux для каждого процессора.
Большая часть магии здесь (asm-generic/percpu.h
):
extern unsigned long __per_cpu_offset[NR_CPUS];
#define per_cpu_offset(x) (__per_cpu_offset[x])
/* Separate out the type, so (int[3], foo) works. */
#define DEFINE_PER_CPU(type, name) \
__attribute__((__section__(".data.percpu"))) __typeof__(type) per_cpu__##name
/* var is in discarded region: offset to particular copy we want */
#define per_cpu(var, cpu) (*RELOC_HIDE(&per_cpu__##var, __per_cpu_offset[cpu]))
#define __get_cpu_var(var) per_cpu(var, smp_processor_id())
Макрос RELOC_HIDE(ptr, offset)
просто передает ptr
заданное смещение в байтах (независимо от типа указателя).
Что он делает?
- При определении
DEFINE_PER_CPU(int, x)
в специальном разделе.data.percpu
создается целое число__per_cpu_x
. - Когда ядро загружено, этот раздел загружается несколько раз - один раз на процессор (эта часть магии не находится в коде выше).
- Массив
__per_cpu_offset
заполняется расстояниями между копиями. Предположим, что используются 1000 байтов данных на процессор,__per_cpu_offset[n]
будет содержать1000*n
. - Символ
per_cpu__x
будет перемещен во время загрузки на CPU 0per_cpu__x
. -
__get_cpu_var(x)
, при запуске на CPU 3, перейдет на*RELOC_HIDE(&per_cpu__x, __per_cpu_offset[3])
. Это начинается с CPU 0x
, добавляет смещение между данными CPU 0 и CPU 3 и, в конечном итоге, разыгрывает результирующий указатель.