per cpu data シンボルのアドレス
kallsyms
を見てみると,メモリ先頭のユーザ空間のアドレスにあるシンボルが複数存在していることが確認できます.
% sudo cat /proc/kallsyms | head 0000000000000000 A irq_stack_union 0000000000000000 A __per_cpu_start 0000000000004000 A exception_stacks 0000000000009000 A gdt_page 000000000000a000 A espfix_waddr 000000000000a008 A espfix_stack 000000000000a020 A cpu_llc_id 000000000000a028 A cpu_llc_shared_map 000000000000a030 A cpu_core_map 000000000000a038 A cpu_sibling_map
なんかおかしいなと思ってましたが,__per_cpu_start
と__per_cpu_end
に囲まれた領域にあるシンボルはコアごとに存在するper cpu dataのシンボルのようです.
ソースは追ってませんが,ドキュメントをみると,
DEFINE_PER_CPU(int, x); int z; z = this_cpu_read(x);
このコードは,
mov ax, gs:[x]
のようにgs
セグメントを介したアクセスになります.
x86_64だとgsはIA32_GS_BASE_MSR
に設定します(GDTは32bitしか対応してないため).
簡単なカーネルモジュールでIA32_GS_BASE_MSR
の確認してみました.
#define MSR_GS_BASE 0xc0000101 static unsigned long long rdmsr(int msr) { unsigned long msrl = 0, msrh = 0; asm volatile ("rdmsr" : "=a"(msrl), "=d"(msrh) : "c"(msr)); return ((unsigned long long)msrh << 32) | msrl; } static int hello_init(){ long long gs = rdmsr(MSR_GS_BASE); pr_info("gs: %llx\n", gs); return 0; }
% for i in `seq 0 1 7`; sudo taskset -c $i insmod hello.ko && sudo rmmod hello && dmesg|tail -n1 [35721.154116] gs: ffff8a729f200000 [35721.207429] gs: ffff8a729f240000 [35721.267899] gs: ffff8a729f280000 [35721.323081] gs: ffff8a729f2c0000 [35721.374967] gs: ffff8a729f300000 [35721.432406] gs: ffff8a729f340000 [35721.479421] gs: ffff8a729f380000 [35721.532804] gs: ffff8a729f3c0000
コアごとに0x40000 (=256KB)の領域が確保されているようです. またこの空間はメモリマップ的にはdirect mapping領域内に存在しています.