如何在Linux内核中实现percpu指针?

如何在Linux内核中实现percpu指针?,linux,linux-kernel,smp,Linux,Linux Kernel,Smp,在多处理器上,每个核心可以有自己的变量。我认为它们在不同的地址中是不同的变量,尽管它们在相同的过程中,并且具有相同的名称 但我想知道,内核是如何实现这一点的?它是否分配了一块内存来存放所有的percpu指针,并且每次它都使用shift或其他方法将指针重定向到某个地址?正常的全局变量不是每个CPU的。自动变量在堆栈上,不同的CPU使用不同的堆栈,因此它们自然会得到单独的变量 我猜您指的是Linux的每CPU可变基础架构。 这里有很多神奇之处(asm generic/percpu.h): 宏RELO

在多处理器上,每个核心可以有自己的变量。我认为它们在不同的地址中是不同的变量,尽管它们在相同的过程中,并且具有相同的名称


但我想知道,内核是如何实现这一点的?它是否分配了一块内存来存放所有的percpu指针,并且每次它都使用shift或其他方法将指针重定向到某个地址?

正常的全局变量不是每个CPU的。自动变量在堆栈上,不同的CPU使用不同的堆栈,因此它们自然会得到单独的变量

我猜您指的是Linux的每CPU可变基础架构。
这里有很多神奇之处(
asm generic/percpu.h
):

RELOC\u HIDE(ptr,offset)
只需按给定的偏移量(以字节为单位)前进
ptr
(不考虑指针类型)

它有什么作用

  • 定义
    DEFINE_PER_CPU(int,x)
    时,在特殊
    .data.perpu
    部分中创建一个整数
    \u PER_CPU_x
  • 当内核被加载时,这个部分被加载多次——每个CPU加载一次(上面的代码中没有这一部分)
  • 每个cpu偏移量的
    \u
    数组由拷贝之间的距离填充。假设使用1000字节的每cpu数据,
    \u每cpu\u偏移量[n]
    将包含
    1000*n
  • 加载期间,符号
    每\u cpu\uuuuux
    将重新定位到cpu 0的
    每\u cpu\uuuux
  • 当在cpu 3上运行时,
    \uu get\u cpu\u var(x)
    将转换为
    *RELOC\u HIDE(&per\u cpu\uux,\uu per\u cpu\u offset[3])
    。这从CPU 0的
    x
    开始,添加CPU 0的数据和CPU 3的数据之间的偏移量,并最终取消对结果指针的引用

  • 谢谢你的回答,但我还有一些问题,是smp的新手,所以,不要冒犯你的想法。首先,我认为同一个进程应该有相同的堆栈,这里是POSIX中的线程定义“…和自动变量,对同一进程中的所有线程都是可访问的。”。自动变量由线程共享。不同的处理器可能有不同的堆栈段寄存器,但内容应该相同。第二,我们是否可以说,如果需要,我们也可以访问其他cpu的变量,只需回滚percpu获得的偏移量?当两个线程调用函数
    foo
    ,该函数有一个自动变量
    x
    ,有两个堆栈和两个
    x
    实例。每个线程都有一个不同的地址,如果两个线程都有地址,那么它们都可以访问这两个线程。使用Linux的每cpu变量,
    每cpu(var,cpu)
    允许您访问任何cpu的变量。data.percpu部分如何确定percpu变量是在堆栈还是堆上声明的?在加载过程中,每个cpu都有自己的GDT表。此gdt表中的每个条目都表示可以从此CPU上的线程访问的内存段。GDT表项0存储每个cpu内存的内存段。为了访问每cpu内存,linux使用gs:{variable offset}访问每cpu变量,如下所示:mov%gs:0x41(%rcx),%dlI一直在研究代码。自从这个答案被写出来后,情况似乎有了一些变化
    DEFINE_PER_CPU(int,x)
    定义一个名为
    x
    的符号,而不是
    PER_CPU(int,x)
    。加载内核时,不是将
    x
    重新定位到CPU 0的副本上,
    \u per\u CPU\u offset[0]
    保留链接器分配给
    x
    的地址与实际存储CPU 0副本的地址之间的差异。另外:x86内核不总是访问每个cpu的偏移量,而是将该偏移量存储为段
    fs
    的基。然后,在段
    fs
    中取消对指针的引用将直接指向当前CPU的副本!
    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())