percpu变量的关键就是:要求根据CPU的个数,在内存中生成多份拷贝,并且能够根据变量名和CPU编号,正确的对各个CPU的变量进行寻址。

采用per-cpu变量有下列好处:所需数据很可能存在于处理器的缓存中,因此可以更快速地访问。如果在多处理器系统中多个CPU可能同时访问变量,会引发一些通信方面的问题,采用percpu变量恰好规避了这个问题。

这里读者只需要记住percpu变量每个CPU都有一个就行了,看完本篇文章后,再回来阅读前面加粗的话,相信会有更深刻的理解。

首先来看如何定义一个percpu变量:

DEFINE_PER_CPU(int, cpu_number);

#define DEFINE_PER_CPU(type, name) \
__attribute__((__section__(".data.percpu"))) __typeof__(type) per_cpu__##name

上面的代码定义了一个类型为int,名字为per_cpu_cpu_number的percpu变量。__typeof__(type)得到的就是int,而per_cpu__##name得到的就是per_cpu_cpu_number,并且通过__section__把这个变量放到名为.data.percpu的数据段。因此在编译时所有的percpu变量都会统一放到.data.percpu数据段,当内核启动后,会根据检测到的cpu个数从数据段.data.percpu为各个CPU单独复制一份。start_kernel函数调用setup_per_cpu_areas()来完成这个工作,setup_per_cpu_areas定义如下:

//__per_cpu_offset数组用来保存CPU对应 percpu段 相对ptr的偏移
unsigned long __per_cpu_offset[NR_CPUS] __read_mostly; static void __init setup_per_cpu_areas(void)
{
unsigned long size, i;
char *ptr;
//取CPU的数量
unsigned long nr_possible_cpus = num_possible_cpus(); /* Copy section for each CPU (we discard the original) */
/* 计算每个CPU所需要的percpu段大小。
*
* @PERCPU_ENOUGH_ROOM:__per_cpu_end - __per_cpu_start + PERCPU_MODULE_RESERVE
* @ALIGN():使percpu段大小向上对齐
*/
size = ALIGN(PERCPU_ENOUGH_ROOM, PAGE_SIZE);
//分配内存
ptr = alloc_bootmem_pages(size * nr_possible_cpus); for_each_possible_cpu(i) {
/* __per_cpu_start对于每个CPU来讲是个定值,它和__per_cpu_end都是由链接器生成的
* 计算每个CPU的__per_cpu_start相对于真实存放percpu段线性地址 的偏移
* 请参考代码下方的示意图
* memcpy拷贝所有percpu变量至每个CPU自己的percpu段
*/
__per_cpu_offset[i] = ptr - __per_cpu_start;
memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
ptr += size;
}
}

那么在编译期源程序如何访问percpu变量呢?内核定义了几个宏来实现对percpu变量的访问:

//smp_processor_id()可以返回当前活动处理器的ID,用作下面的cpu参数。
#define per_cpu(var, cpu) (*({ \
extern int simple_identifier_##var(void); \
RELOC_HIDE(&per_cpu__##var, __per_cpu_offset(cpu)); }))

这里我们举一个例子进行说明:假设访问per_cpu(var,1),由于percpu变量在定义时由DEFINE_PER_CPU展开成per_cpu_var,因此RELOC_HIDE的第一个参数就是编译时per_cpu_var的地址,第二个参数__per_cpu_offset(cpu)为前面setup_per_cpu_areas计算出来的,它表示运行时复制的percpu数据区首地址与编译期确定的首地址的差值,这两个值相加就是实际存储percpu变量的地址。示意图如下:

在更(geng)新的内核(2.6.32)中,percpu变量的定义(DEFINE_PER_CPU)和存取(per_cpu(var, cpu))方式没有发生改变,主要是__per_cpu_offset数组的初始化方式发生了改变,见arch\x86\kernel\setup_percpu.c中函数:setup_per_cpu_areas()。

最新文章

  1. Wen前端性能优化
  2. MFC对话框中显示BMP,JPG图片
  3. PHP-递归扫描目录和删除目录
  4. 正在调用的 ServicedComponent 配置不正确(请使用 regsvcs 重新注册)
  5. 微软ASP.NET MVC 学习地址
  6. HTTP首部及各状态码
  7. SQL Server 获取服务器信息
  8. SRM 582 Div II Level Three: ColorTheCells, Brute Force 算法
  9. 简单了解Hibernate
  10. jupyter的交互小工具-----ipyleaflet
  11. 传输控制协议(TCP) -- 连接建立及终止过程
  12. LIRe 源代码分析 2:基本接口(DocumentBuilder)
  13. SublimeText插件Pandoc导出PDF中文报错或者中文不显示解决方法
  14. redis 实现发布订阅的功能
  15. Fiddler 安装与配置
  16. M端错误提醒 -- pop 使用
  17. python2.x和3.x的区别(不定时更新)
  18. 汉化 的 空指针 bug
  19. easyui datagrid种编辑器combobox选择的值不显示解决方案
  20. USACO08MAR土地购买 与 APIO2010特别行动队

热门文章

  1. 大多数人不知道的:HashMap链表成环的原因和解决方案
  2. Docker搭建disconf环境,三部曲之一:极速搭建disconf
  3. Python作业本——前言
  4. c语言文件的基本操作
  5. openlivewriter安装配置
  6. Python(Head First)学习笔记:一
  7. 动态设置 view 在布局中位置
  8. 【转载】pandas中的循环
  9. Winform中使用FastReport的DesignReport时怎样给通过代码Table添加数据
  10. Java连载32-对象、类及其关系与定义