zoukankan      html  css  js  c++  java
  • 我的学习

    一、linux中的每cpu变量

      看linux内核代码的时候,会发现大量的per_cpu(name, cpu),get_cpu_var(name)等出现cpu字眼的语句。从语句的意思可以看出是要使用与当前cpu相关的一个变量,不过查看这个变量的定义,总是有这样一个宏:DEFINE_PER_CPU(type, name),将这个宏展开成下面的语句:

      __attribute__((__section__(".data.percpu"))) __typeof__(type) per_cpu__##name

    ,这个语句就是在.data.percpu段中定义type类型的per_cpu##name变量。看到这里,我就不明白了,既然只是在一个段中定义了一个变量,那为什么每个cpu都有一个这样的变量呢?

     

    二、linux中的每cpu变量的实现

      这应该算是linux内核产生每cpu变量的一个技巧吧!首先我们来看下链接linux内核的链接脚本,这个脚本主要用来控制gcc怎样链接linux中的各个段并最终产生linux内核映像文件的,不明白链接脚本的可以到网上搜下,大把的资料。这个脚本文件叫vmlinux.lds.S,放在arch/i386/kernel目录中,在这个文件中有下面一段代码:

     

    __per_cpu_start =
    .;
     
      .data.percpu  : {
    *
    (.data.percpu) }
     
      __per_cpu_end
    = .;

     

      这段代码定义了两个符号,分别是__per_cpu_start和_per_cpu_end,它们标识了段data.percpu的起始和结束地址。而段.data.percpu是通过各个对象文件中的.data.percpu段合并起来的,也就是说前面我们定义的per_cpu##name变量终止都会放在.data.percpu段中,而这个段的起始地址和结束地址分别是__per_cpu_start和_per_cpu_end。到了这一步,貌似还是没有看出per_cpu##name变量怎么会对每个cpu都有一个。

      在linux初始化的时候,会调用函数setup_per_cpu_areas来真正的把per_cpu##name变量赋值给每个cpu,具体代码如下:

    复制代码
    static void __init setup_per_cpu_areas(void
    )
    {
        unsigned
    long
    size, i;
     
    char *
    ptr;
     
    /* Created by linker magic */ extern char
    __per_cpu_start[], __per_cpu_end[];
     
     
    /* Copy section for each CPU (we discard the original) */    size = ALIGN(__per_cpu_end -
    __per_cpu_start, SMP_CACHE_BYTES);
    #ifdef CONFIG_MODULES
     
    if (size <
    PERCPU_ENOUGH_ROOM)
            size
    = PERCPU_ENOUGH_ROOM;#endif
     
        ptr
    = alloc_bootmem(size *
    NR_CPUS);
     
     
    for (i = 0; i < NR_CPUS; i++, ptr +=
    size) {
            __per_cpu_offset[i]
    = ptr -
    __per_cpu_start;
            memcpy(ptr, __per_cpu_start, __per_cpu_end
    -
    __per_cpu_start);
        }
    }
    #endif /* !__GENERIC_PER_CPU */
    复制代码

      代码中引用了由链接器产生的变量__per_cpu_start和 __per_cpu_end,在它们之间的内存空间存放了所有的每cpu变量,总大小为size。然后内核通过alloc_bootmem给每个cpu都分配了一个这么大小的内存空间。下面的for循环把__per_cpu_start和 __per_cpu_end之间的所有cpu变量拷贝一份到每个cpu对应的内存空间中,并用__per_cpu_offset[i]来存放第i个cpu对应的每cpu变量的起始地址。

    不过为什么__per_cpu_offset[i]存放的是ptr - __per_cpu_start,而不是ptr,原因很简单,当我们用per_cpu##name来访问某个cpu上的每cpu变量时,我们应该这样访问:获取该cpu对应每cpu变量的起始地址+per_cpu##name的偏移量。我们现在展开宏per_cpu(var, cpu):

    *(&per_cpu__##var + __per_cpu_offset[cpu])=*(ptr+&per_cpu__##var- __per_cpu_start)

    这样就访问了在cpu上的var变量了。

     

    三、每cpu变量的作用

      从上面可以看出,为了定义一个变量,绕了一个很大的弯,为什么要定义这样的每cpu变量?这其实和linux内部的同步有关,因为如果我们把变量定义成所有cpu都可以访问的,那么就必须用同步机制来保证cpu对这个变量的互斥访问,很明显这是要花费时间的,linux内核为了能够减少这种时间开销,就在每个cpu都定义了一个一模一样的变量,这样每个cpu都使用自己的变量,而不会去访问其它cpu上的变量,也就没有了同步的开销。不过在使用每cpu变量时,必须保证禁用内核抢占。因为内核抢占还是会使每cpu变量产生竞争条件,例如一个内核控制路径获得了它的每cpu变量本地副本的地址,然后它又被抢占跑到另一个cpu上去了,但仍然使用原来cpu上的每cpu变量。


     





  • 相关阅读:
    【火炉炼AI】机器学习003-简单线性回归器的创建,测试,模型保存和加载
    【火炉炼AI】机器学习002-标记编码方法
    【火炉炼AI】机器学习001-数据预处理技术(均值移除,范围缩放,归一化,二值化,独热编码)
    HOJ 13830 DNA Sequencing
    HDU 6078 Wavel Sequence
    hoj 13814 Cake cut
    HDU6071 Lazy Running
    HDU 6070 Dirt Ratio
    HDU 6070 Dirt Ratio
    HDU 6059 Kanade's trio
  • 原文地址:https://www.cnblogs.com/chengxuyuancc/p/3421791.html
Copyright © 2011-2022 走看看