linux内核中的每cpu变量
一、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变量的更多相关文章
- linux内核同步之每CPU变量、原子操作、内存屏障、自旋锁【转】
转自:http://blog.csdn.net/goodluckwhh/article/details/9005585 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 一每 ...
- 向linux内核中添加外部中断驱动模块
本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...
- Linux内核中双向链表的经典实现
概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核 ...
- Linux内核中的fastcall和asmlinkage宏
代码中看见:#define _fastcall 所以了解下fastcall -------------------------------------------------------------- ...
- [翻译] Linux 内核中的位数组和位操作
目录 Linux 内核里的数据结构 原文链接与说明 Linux 内核中的位数组和位操作 位数组声明 体系结构特定的位操作 通用位操作 链接 Linux 内核里的数据结构 原文链接与说明 https:/ ...
- linux内核中链表代码分析---list.h头文件分析(一)【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...
- Linux内核中的软中断、tasklet和工作队列具体解释
[TOC] 本文基于Linux2.6.32内核版本号. 引言 软中断.tasklet和工作队列并非Linux内核中一直存在的机制,而是由更早版本号的内核中的"下半部"(bottom ...
- Linux内核中锁机制之完成量、互斥量
在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等 ...
- Linux内核中锁机制之内存屏障、读写自旋锁及顺序锁
在上一篇博文中笔者讨论了关于原子操作和自旋锁的相关内容,本篇博文将继续锁机制的讨论,包括内存屏障.读写自旋锁以及顺序锁的相关内容.下面首先讨论内存屏障的相关内容. 三.内存屏障 不知读者是是否记得在笔 ...
随机推荐
- 多线程、委托、Invoke解决winform界面卡死的问题,并带开关
一.知识点介绍 1,更新控件的内容,应该调用控件的Invoke方法. Invoke指: 在拥有控件的基础窗口句柄的线程上,用指定的参数列表执行指定委托.该方法接收一个委托类型和委托的参数,因此需要定义 ...
- gkENGINE重开!
2013年中,曾信誓旦旦的要开源gkENGINE,结果一直到了现在. 拖了一年多,问题在于 - 工作太忙... 其实在2014春节假期我还是赶了赶进度,对gles2的渲染器进行了完善,但没做完.然后留 ...
- Android6.0动态获取权限
Android6.0采用新的权限模型,只有在需要权限的时候,才告知用户是否授权,是在runtime时候授权,而不是在原来安装的时候 ,同时默认情况下每次在运行时打开页面时候,需要先检查是否有所需要的权 ...
- Smart3D系列教程6之 《案例实战演练3——倾斜数据正射影像及DSM的生产》
一.前言 Wish3D出品的系列教程中,前面两讲分别讲述说明了小物件的照片三维重建.大区域地形的三维重建,从照片的直接导入至软件到通过Excel表格将区块导入处理,从不同的模型类别.不同的导入方式演示 ...
- android 通过uri获取bitmap图片并压缩
很多人在调用图库选择图片时会在onActivityResult中用Media.getBitmap来获取返回的图片,如下: Uri mImageCaptureUri = data.getData(); ...
- express-session 保存遇到的问题
今天在用express4 试着做网站的时候,发现request.session 中一直不能保存新的值,还一直报一个错 express-session deprecated undefined resa ...
- [收藏]C++简单五子棋
#include<iostream> #include<iomanip> using namespace std; ; //棋盘行数 ; //棋盘列数 char p[X][Y] ...
- SSH配置与讲解
一.Struts 首先介绍Struts,在web项目中加入Struts的jar包,并在Web.xml中添加Struts的配置: <filter> <filter-name ...
- css确定元素水平居中和垂直居中
---恢复内容开始--- 首先,我们在了解如何通过css了解元素水平和垂直居中之前,先要了解下html都有哪些元素,这些元素与偶有哪些分类,因为不同类别的元素的水平垂直居中方法是完全不同的,究其根本当 ...
- dagger2 备注
dagger 2是android下的IOC框架,类似java服务端的spring,但功能上远没有其强大.个人理解不管是APP还是服务端依赖注入的本质都是一样的,用于解耦某个服务的定义和实现.我自己给出 ...