per-CPU变量的静态和动态分配原理

来源:互联网 发布:喜欢的工作知乎 编辑:程序博客网 时间:2024/05/21 15:47

per-CPU是2.6内核中引入的,访问per-CPU变量几乎不需要锁,每个处理器都在其自己的副本上工作。这些副本是如何生成的呢?本文尝试解答这个问题。


静态per-CPU结构设计思路大体可以分为两个阶段:编译阶段和运行时阶段

在编译阶段,实际上只生成了一个CPU原本。系统中所有per-CPU结构都放到了一个叫做"data.percpu"的section中,在ld.S链接脚本有如下内容: 


  . = ALIGN(32);
  __per_cpu_start = .;
  .data.percpu  : { *(.data.percpu) }
  __per_cpu_end = .;
  . = ALIGN(4096);
  __init_end = .;

这个链接脚本知道,.data.percpu Section是处于init数据段的,在系统初始化结束后将被回收。那么,系统如何维持per-CPU数据呢?这个任务在运行时完成。在系统初始化阶段有一个函数会分配 NR_CPU * ( __per_cpu_end - __per_cpu_start)大小的内存,然后将data.percpu段中的CPU原本拷贝NR_CPU份到这块内存中。从此以后系统通过get_cpu_var访问per-CPU变量的时候就会根据自己的cpu_id找到对应的拷贝。


可见,静态per-CPU变量的locality非常好,CPU之间在Cache级都不彼此干扰。对于静态生成的per-CPU变量需要使用get_cpu_var来访问。


在讲述动态per-CPU结构之前不妨思考下,如何能把动态per-CPU的locality设计得跟静态的一样呢? 由于不知道系统中将会有多少动态结构出现,所以不宜采用预留内存的方式,这为我们的设计带来了很大挑战。实际上,Linux也没有完全解决这个问题,但还是做了最大程度的优化,手法也比较赞。考虑到per-CPU变量的访问模式,效率应该和静态方式不相上下。下面看看linux的处理方式。


动态per-CPU结构相对于静态结构来说,设计上更直观,但效率上要低一些。每次调用alloc_percpu(type)的时候会生成一个维度为NR_CPUS的指针数组,每个指针指向一个kzalloc/kmalloc_node出来的type型对象。Linux在这里采取了一个优化手段:如果第i个cpu在线(linux支持cpu的hot-plug),那么就采用kmalloc_node来分配空间,这个空间与cpu i的亲和性很高;如果cpu i不在线,则采用通用的kzalloc分配了。下面是空间分配代码:


[c-sharp] view plaincopyprint?
  1. int node = cpu_to_node(cpu);  
  2. BUG_ON(pdata->ptrs[cpu]);  
  3. if (node_online(node))  
  4.     pdata->ptrs[cpu] = kmalloc_node(size, gfp|__GFP_ZERO, node);  
  5. else  
  6.     pdata->ptrs[cpu] = kzalloc(size, gfp);  


对于动态生成的per-CPU变量需要用per_cpu_ptr来访问。



Ref: http://blog.csdn.net/qinzhonghello/archive/2008/12/30/3649293.aspx

Ref: http://blog.chinaunix.net/u/12325/showart.php?id=1274548