一、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 = ; 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变量。

linux内核中的每cpu变量的更多相关文章

  1. linux内核同步之每CPU变量、原子操作、内存屏障、自旋锁【转】

    转自:http://blog.csdn.net/goodluckwhh/article/details/9005585 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 一每 ...

  2. 向linux内核中添加外部中断驱动模块

    本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...

  3. Linux内核中双向链表的经典实现

    概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核 ...

  4. Linux内核中的fastcall和asmlinkage宏

    代码中看见:#define _fastcall 所以了解下fastcall -------------------------------------------------------------- ...

  5. [翻译] Linux 内核中的位数组和位操作

    目录 Linux 内核里的数据结构 原文链接与说明 Linux 内核中的位数组和位操作 位数组声明 体系结构特定的位操作 通用位操作 链接 Linux 内核里的数据结构 原文链接与说明 https:/ ...

  6. linux内核中链表代码分析---list.h头文件分析(一)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...

  7. Linux内核中的软中断、tasklet和工作队列具体解释

    [TOC] 本文基于Linux2.6.32内核版本号. 引言 软中断.tasklet和工作队列并非Linux内核中一直存在的机制,而是由更早版本号的内核中的"下半部"(bottom ...

  8. Linux内核中锁机制之完成量、互斥量

    在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等 ...

  9. Linux内核中锁机制之内存屏障、读写自旋锁及顺序锁

    在上一篇博文中笔者讨论了关于原子操作和自旋锁的相关内容,本篇博文将继续锁机制的讨论,包括内存屏障.读写自旋锁以及顺序锁的相关内容.下面首先讨论内存屏障的相关内容. 三.内存屏障 不知读者是是否记得在笔 ...

随机推荐

  1. sql筛选查询A表中B表已经存在的数据

    SELECT *FROM A LEFT OUTER JOIN B ON A.ID = B.IDWHERE B.ID IS NULL 开发实例: SELECT Position_Car.Area, Po ...

  2. 前端自动化工具gulp自动添加版本号

    之前,我介绍了学习安装并配置前端自动化工具Gulp,觉得gulp确实比grunt的配置简单很多,于是我决定再深入学习一下gulp,就去网上查了资料,发现gulp还可以自动添加版本号,这个功能就为我平时 ...

  3. [LintCode] Linked List Cycle 单链表中的环

    Given a linked list, determine if it has a cycle in it. ExampleGiven -21->10->4->5, tail co ...

  4. Struct2 csv文件上传读取中文内容乱码

    网络上搜索下,发现都不适合 最终改写代码: FileInputStream fis = null; InputStreamReader isr = null; BufferedReader br= n ...

  5. SVN Server for Migration

    SVN Server: http://mxsuse01/svn/repos/erp/Oracle_EMS Report SVN (Put to SVN Sort) 1. *.RDF 2. *CP.LD ...

  6. Git分布式版本控制系统学习笔记

    Git是一个开源的分布式版本控制系统,用以有效.高速的处理从很小到非常大的项目版本管理.[4]  Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本 ...

  7. 导向矢量(Steering Vector)

    导向矢量是阵列天线的所有阵元对具有单位能量窄带信源的响应. 由于阵列响应在不同方向上是不同的,导向矢量与信源的方向是相互关联的,这种关联的独特性依赖于阵列的几何结构.对于同一阵元阵列,导向矢量的每一个 ...

  8. 新书到手 TRANSACTION PROCESSING:CONCEPTS AND TECHNIQUES

    新书到手 TRANSACTION PROCESSING:CONCEPTS AND TECHNIQUES Jim Gray大神的著作 本文版权归作者所有,未经作者同意不得转载.

  9. Hadoop 和 HDInsight:Windows Azure 中的大数据

    世界的大数据包含一个庞大而充满活力的生态系统,但一个开放源码项目上面有这一切,那就是 Hadoop 的王朝. Hadoop 是事实上的标准的分布式的数据运算.Hadoop 提供了一个 MapReduc ...

  10. [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序

    [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序 本节导读: 本节主要说明使用异步进行程序设计的优缺点及如何通过异步编程. 使用 ...