在 linux 中,per-cpu变量用在多 处理器 系统中,用来为系统中的每个cpu都生成一个变量的副本,以避开多处理器互斥中的加锁问题,另一个是cpu本地的变量可以充分利用cpu的硬件缓存,提高性能。本贴讨论一下Linux 内核 对per-cpu变量的 代码 实现。

1.静态per-cpu变量
静态per-cpu变量通过DEFINE_PER_CPU和DECLARE_PER_CPU宏在内核 源码 中定义和声明一个per-cpu变量。这些变量与普通变量的主要区别是放在一个特殊的section里。
静态percpu变量比较好理解,内核的代码也比较简洁明快。

相对静态per-cpu变量,还有 动态 分配的per-cpu变量。普通变量动态分配很简单,用kmalloc或者kzalloc都可以的,其实per-cpu变量的动态分配也是需要利用Linux内核底层的分配函数,页面分配器。从这个角度而言,percpu memory allocator与slab memory allocator是一个层面的东西,都建立在page memory allocator基础之上。不过对于大部分驱动 程序 员而言,使用kmalloc与kzalloc的机会要远远大于percpu memory allocator。

为了描述,这里做个定义,CPU0与CPU1变量副本的 空间 大小完全一样,本贴统称这两个副本空间为副本空间,每个CPU变量副本所在空间为单元空间。
在内核初始化期间调用的setup_percpu_areas函数中,reserve和dynamic空间大约定义的大小是8KB和12KB,static空间由系统中定义的静态per-cpu变量的多少来决定。

Linux内核对percpu memory allocator使用了所谓chunk的实现方式,它实现了统一的静态per-cpu和动态per-cpu变量的实现(其实静态per-cpu变量的实现不需要chunk,但是为了统一,也把它放到chunk的管理体系,就算是大一统吧).

chunk干什么事呢?chunk是一个管理数据结构,就称之为容器吧。看看具体的数据结构还是很有必要:

点击(此处)折叠或打开

  1. struct pcpu_chunk {
  2. struct list_head list; /* linked to pcpu_slot lists */
  3. int free_size; /* free bytes in the chunk */
  4. int contig_hint; /* max contiguous size hint */
  5. void *base_addr; /* base address of this chunk */
  6. int map_used; /* # of map entries used */
  7. int map_alloc; /* # of map entries allocated */
  8. int *map; /* allocation map */
  9. void *data; /* chunk data */
  10. bool immutable; /* no [de]population allowed */
  11. unsigned long populated[]; /* populated bitmap */
  12. };

list:用来把chunk链接起来形成链表。每一个链表又都放到pcpu_slot数组中,根据chunk中空闲空间的大小决定放到数组的哪个 元素 中。
contig_hint:该chunk所管理的副本空间中空闲空间大小。
base_addr:简单地说,副本空间首地址。副本空间也是由一个chunk来管,称之为first chunk中,副本空间中的dynamic空间用来给动态per-cpu变量使用
map_used:为了对chunk所管理的副本空间分配情况的跟踪,用来表示可以管理的个数
map_alloc:已经分配的小块个数,因为每个分配的小块都是给动态per-cpu使用的,所以其实是已经分配的变量的个数
map:整数数组,用来表示副本空间分配情况。正数表示该空间空闲,负数就已经分配给一个变量了
data:指向分配的页数据
大体上就这些。

动态分配一个per-cpu变量时,在pcpu_slot空间查找空闲空间可以满足需要的chunk,如果找不到这样的chunk,那么重新分配一个chunk,用kzalloc函数。
对一个新的chunk都会调用pcpu_get_vm_areas分配VM空间地址:

点击(此处)折叠或打开

  1. static struct pcpu_chunk *pcpu_create_chunk(void)
  2. {
  3. struct pcpu_chunk *chunk;
  4. struct vm_struct **vms;
  5. chunk = pcpu_alloc_chunk();
  6. if (!chunk)
  7. return NULL;
  8. vms = pcpu_get_vm_areas(pcpu_group_offsets, pcpu_group_sizes,
  9. pcpu_nr_groups, pcpu_atom_size, GFP_KERNEL);
  10. if (!vms) {
  11. pcpu_free_chunk(chunk);
  12. return NULL;
  13. }
  14. chunk->data = http://blog.chinaunix.net/vms;
  15. chunk->base_addr = vms[0]->addr - pcpu_group_offsets[0];
  16. return chunk;
  17. } static struct pcpu_chunk *pcpu_create_chunk(void)
  18. {
  19. struct pcpu_chunk *chunk;
  20. struct vm_struct **vms;
  21. chunk = pcpu_alloc_chunk();
  22. if (!chunk)
  23. return NULL;
  24. vms = pcpu_get_vm_areas(pcpu_group_offsets, pcpu_group_sizes,
  25. pcpu_nr_groups, pcpu_atom_size, GFP_KERNEL);
  26. if (!vms) {
  27. pcpu_free_chunk(chunk);
  28. return NULL;
  29. }
  30. chunk->data = http://blog.chinaunix.net/vms;
  31. chunk->base_addr = vms[0]->addr - pcpu_group_offsets[0];
  32. return chunk;
  33. }

pcpu_group_offsets[0]对于非变态的系统都是0.

所以,动态分配per-cpu变量时,先在chunk所管理的副本空间(在VM区中),然后用到哪个页面就往那个对应的vm上提交物理页面。
副本空间上实行小额分配,实际上就是有新变量分配,就在副本空间里头找,找到以后看这个vm处的地址有没有被映射到物理地址,没有就提交页面,否则不提(都提了干吗还提交呢?!),判断vm处是否提交了物理页面用bit map跟踪,chunk的数据结构中的后两个成员用来干这事。

OK,分配一个新变量之后,返回给你的是一个vm区中的地址,要让每个cpu访问到自己的vm区,得用内核自己定义的宏,其实核心思想就是用smp_get_processorid等来获得对应cpu变量在变量副本中的偏移地址,然后返回来了。

要想验证上面说的对不对,可以在内核中打印出alloc_percpu返回的地址,是否在VM区。

FQA

访问per-cpu变量为什么要禁止内核抢占?
这个和进程迁移相关。如果访问per-cpu变量的进程被抢占(如发生中断而重新调度),该进程已经得到自己per-cpu变量副本的偏移地址,当它被恢复执行并有可能迁移到别的CPU上,这时候该偏移地址对新的CPU是无效的。
per-cpu变量还需要保护吗?
per-cpu 变量虽然能保护变量被多个core 访问,但是它并不能保护同一核心上异步事件的访问,如ISR,deferred functions。在这样的情况下,同步原语还是需要的。

----------------------------------------------------
struct module 中有个percpu变量,不知道如何用!
如在load_module 函数实现中:
......
        if (pcpuindex) {
                /* We have a special allocation  for  this section. */
                percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size,
                                         sechdrs[pcpuindex].sh_addralign,
                                         mod->name);
                if (!percpu) {
                        err = -ENOMEM;
                        goto free_mod;
                }
                sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
                 mod->percpu = percpu;         
......
}
-------------------------------------------------------------------

模块的per-cpu  section是ELF文件中一个特殊的section,属于data区,模块加载时,会根据系统中CPU个数,将这个section中的数据复制相应的份数,存放在CORE section区域。这个主要在SMP系统中,不同CPU可以访问模块per-cpu section中的数据而无需使用CPU间的互斥机制。
也谈不上什么高级的用法,跟内核中定义的per-cpu变量没有任何区别,只不过平时很少使用到。在模块里面加上
DEFINE_PER_CPU(int, hea);

再readelf -S xxx.ko就可以看到这个per-cpu section了。内核因为自己在初始化时对这些静态定义的per-cpu变量进行了复制,模块因为没有这个阶段,所有由内核模块加载器来完成。此处讨论仅限于静态定义的per-cpu变量,因为动态分配的话,本身就会产生多个副本空间,这个无论对于内核还是模块都完全一样的机制,所以不会有这个问题。

Linux内核对per-cpu变量的实现相关推荐

  1. linux mii 网卡驱动,网卡驱动8-MII接口以及linux内核对MII的支持

    首先,向大家推荐一些文章. 这上面说了MII.RMII.SMII.GMII等一系列的接口. 网口一般是这样 MacàPhyà网络变压器àRJ45口 但是只是从电路上不一定看得这么清楚,应为有些是集成的 ...

  2. Linux网卡驱动适配各个内核,网卡驱动8 MII接口以及linux内核对MII的支持

    首先,向大家推荐一些文章. http://blog.chinaunix.net/uid-24148050-id-131084.html http://hi.baidu.com/lds102/item/ ...

  3. linux内核对TCP的连接状态管理

    TCP协议实例连接状态存放在struct sock数据结构的state数据域中. 当TCP协议实例连接处于不同状态时,对数据包的处理不一样,所以每个输入的数据包都要来查询TCP状态机,整个状态机制划分 ...

  4. linux内核对nsm配置,H3C SecPath系列安全产品 NSQM1NSM 单板手册(V1.02)

    第4章  BIOS操作指导 4.1  NSM单板BIOS菜单 安全产品上电启动后,进入用户视图,执行xsm connect slot slotnum 命令,连接到NSM单板的串口上.slotnum表示 ...

  5. linux 内核 睡眠,linux内核对S3C2410睡眠模式的支持有哪些?

    一.S3C2410支持4种供电模式 (1)NORMAL MODE 耗电最大.可以通过关闭具体控制器的时钟来节电 (2)SLOW MODE 在此模式下可以没有内部PLL,耗电情况依赖于外部时钟的频率 ( ...

  6. 启用linux内核对usb摄像头的支持,linux利用USB摄像头

    www.linuxsir.org/main/node/221?q=node/221 上述帖子是关于摄像头(WebCam)在Linux中批准Spcaserv 架设网络视频监控服务器 (v0.1b). 在 ...

  7. linux 内核对sata驱动问题

    今天测试了一款新主板,据说是工控的,到手开始测试,发现一个小问题,格式化硬盘用的时间是35分钟左右,而IDE硬盘呢却是4分钟左右. 开始测试问题: 1 ,换另一块sata硬盘测试: 结果仍为35分钟左 ...

  8. linux内核中的每cpu变量

    一.linux中的每cpu变量 看linux内核代码的时候,会发现大量的per_cpu(name, cpu),get_cpu_var(name)等出现cpu字眼的语句.从语句的意思可以看出是要使用与当 ...

  9. linux进入节电模式,intel CPU在linux下的节能模式

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 这是吧友@peterliu1218 总结的:(由@ubuntu_隐影提供) 其实Linux内核对CPU的工作频率管理,已经跟不上现代的CPU的需求,无法在 ...

  10. 移动硬盘设置java变量_移动硬盘Linux内搭建MyEcilipse8.6开发环境

    还是在去年的时候,买了一个西数WD 3200BEV的320GB移动硬盘,闲来没事瞎折腾,在移动硬盘上安装了个Linux系统,在我的台式机和朋友的笔记本上运行成功,现在已经安装好了MyEcilipse8 ...

最新文章

  1. 马斯克的SpaceX又双叒叕融资了:喜提131亿,估值破3000亿
  2. 小白学python买什么书-小白如何高效率学习python?真心建议(附教程)
  3. 键桥通讯布局大数据 9.45亿入股上海即富
  4. Spring Cloud【Finchley】- 20使用@RefreshScope实现配置的刷新
  5. 微信小程序 - 使用npm(第三方包)
  6. QDoc C ++特定的配置变量
  7. JS常用属性方法大全
  8. CSS仿艺龙首页鼠标移入图片放大
  9. 支付宝后台如何查看自己的签约详情
  10. python函数拟合编程_Python应用实现双指数函数及拟合代码实例
  11. Apache JMeter 压试 HTTP接口
  12. NOIP 2014 Day1 T3飞扬的小鸟
  13. 将一张图片修改为合适的像素大小
  14. MySQL时间段查询,无数据补0
  15. 一个小小的签到功能,到底用MySQL还是Redis?
  16. Spring实例化bean的三种方法
  17. Clover-系统集成功能可能被安全软件阻止,请关闭安全软件后重启Clover的解决方案
  18. 手机版vmlogin怎么下载?VMlogin指纹浏览器适合哪些行业?
  19. 后缀–ize_动词后加ize的后缀有什么作用
  20. 方舟生存进化手游版服务器维护,方舟生存进化手游PVX服务器规则一览 PVX服务器怎么玩_3DM手游...

热门文章

  1. KVO.非常简单的键值监听模式
  2. c# Excel的操作
  3. Spring安全权限管理(Spring Security的配置使用)
  4. Oracle财务系统常用标准报表
  5. violate原理,java内存模型,可见性,cache二级内存模型
  6. 操作数据库为什么需要进行事务控制
  7. Magento2.x 插件 Module(模块)开发实战
  8. 如何在Windows上制作一个包含.lib和.dll的Rust Crate包
  9. (5)全局异常捕捉【从零开始学Spring Boot】
  10. [Angular2 Animation] Control Undefined Angular 2 States with void State