目录

一、锁的使用

1、使用锁的缺点

2、解决方法

二、per-CPU使用

1)动态

2)静态

3)注意事项

4)引用使用

三、per-CPU在内核中的使用

四、实例

程序输出


一、锁的使用

当操作共享可写数据时,需要加锁进行保护。

1、使用锁的缺点

  • 性能会受到影响,更糟糕的是,这些不利影响可能会在数百核的高端多核系统上成倍增加。现实生活中的一种场景,是繁忙高速公路上的单一收费站或繁忙十字路口的红绿灯,严重影响通行性能。
  • 锁的竞争:增加系统内锁的数量,有利于降低两个或多个进程/线程之间对特定锁的争用。
    但出现死锁的机会大增。
  • 一些列问题:性能问题,死锁,优先级转换风险、优先级高的需要等到优先级低的进程等。

2、解决方法

lock-free技术:per-CPU与lock-free数据结构RCU

        原理:通过拷贝一份变量

二、per-CPU使用

头文件 #include <linux/percpu.h>

有两种申请方式:动态申请和静态申请

1)动态

申请

  • alloc_percpu()
  • alloc_percpu_gfp()
  • devm_alloc_percpu(),第一个参数dev

释放

  • void free_percpu(void __percpu *__pdata)

2)静态

  • DEFINE_PER_CPU(int, pcpa);

3)注意事项

void *p;
val = get_cpu_var(pcpa);
p = vmalloc(20000);
pr_info("cpu1: pcpa = %+d\n", val);
put_cpu_var(pcpa);
vfree(p);
  1. get_cpu_var()/put_cpu_var() 之间的数据必须是原子的并且不能阻塞
  2. 所以禁用内核抢占,不允许任何类型的阻塞(或休眠)
  3. vmalloc、printk() 或者pr_foo<>是 可能会睡眠

4)引用使用

get_cpu_var()会引用preempt_disable(),禁止内核竞争
put_cpu_var()会引用preempt_enable()

增加per-CPU的变量

get_cpu_var(pcpa) ++;
put_cpu_var(pcpa);

或者per_cpu(var,cpu) 如遍历每个CPU核的pcpa的变量

for_each_online_cpu(i) {val = per_cpu(pcpa, i);pr_info(" cpu %2d: pcpa = %+d\n", i, val);
}

通过指针指向变量

{get,put}_cpu_ptr()

三、per-CPU在内核中的使用

current 变量

// arch/x86/include/asm/current.h

struct task_struct;DECLARE_PER_CPU(struct task_struct *, current_task);static __always_inline struct task_struct *get_current(void)
{return this_cpu_read_stable(current_task);
}#define current get_current()

current_task的变量什么时候更新? 上下文切换的时候

//源码文件arch/x86/kernel/process_64.c

__visible __notrace_funcgraph struct task_struct *
__switch_to(struct task_struct *prev_p, struct task_struct *next_p)
{[ ... ]this_cpu_write(current_task, next_p);[ ... ]
}

四、实例

两个线程使用共享变量,使用per-CPU,最后各自的输出是正确的

#define THRD0_ITERS 3static int thrd_work(void *arg)
{int i, val;long thrd = (long)arg;struct drv_ctx *ctx;if (set_cpuaffinity(thrd) < 0) {pr_err("setting cpu affinity mask for our kthread %ld failed\n", thrd);return -ENOSYS;}SHOW_CPU_CTX();if (thrd == 0) { /* kthread #0 runs on CPU 0 */for (i=0; i<THRD0_ITERS; i++) {/* Operate on our perpcu integer */val = ++ get_cpu_var(pcpa);pr_info("  thrd_0/cpu0: pcpa = %+d\n", val);put_cpu_var(pcpa);ctx = get_cpu_ptr(pcp_ctx);ctx->tx += 100;pr_info("  thrd_0/cpu0: pcp ctx: tx = %5d, rx = %5d\n",ctx->tx, ctx->rx);put_cpu_ptr(pcp_ctx);}} else if (thrd == 1) { /*kthread #1 runs on CPU 1 */for (i=0; i<THRD1_ITERS; i++) {val = -- get_cpu_var(pcpa);pr_info("  thrd_1/cpu1: pcpa = %+d\n", val);ctx = get_cpu_ptr(pcp_ctx);ctx->rx += 200;pr_info("  thrd_1/cpu1: pcp ctx: tx = %5d, rx = %5d\n",ctx->tx, ctx->rx);put_cpu_ptr(pcp_ctx);}}

程序输出

benshushu:2_percpu# insmod percpu_var.ko
[ 3180.878394] percpu_var: loading out-of-tree module taints kernel.
[ 3180.888442] percpu_var: module verification failed: signature and/or required key missing - tainting kernel
[ 3180.912374] percpu_var:init_percpu_var(): inserted
[ 3180.928186] percpu_var:thrd_work(): *** kthread PID 837 on cpu 1 now ***
[ 3180.928853] percpu_var:thrd_work():   thrd_1/cpu1: pcpa = -1
[ 3180.933631] percpu_var:thrd_work(): *** kthread PID 836 on cpu 0 now ***
[ 3180.939866] percpu_var:thrd_work():   thrd_0/cpu0: pcpa = +1
[ 3180.939947] percpu_var:thrd_work():   thrd_0/cpu0: pcp ctx: tx =   100, rx =     0
[ 3180.939968] percpu_var:thrd_work():   thrd_0/cpu0: pcpa = +2
[ 3180.939977] percpu_var:thrd_work():   thrd_0/cpu0: pcp ctx: tx =   200, rx =     0
[ 3180.939984] percpu_var:thrd_work():   thrd_0/cpu0: pcpa = +3
[ 3180.939992] percpu_var:thrd_work():   thrd_0/cpu0: pcp ctx: tx =   300, rx =     0
[ 3180.940691] percpu_var:disp_vars(): 000) [thrd_0/0]:836   |  ...0   /* disp_vars() */
[ 3180.940786] percpu_var:disp_vars():  cpu  0: pcpa = +3, rx =     0, tx =   300
[ 3180.940801] percpu_var:disp_vars():  cpu  1: pcpa = -1, rx =     0, tx =     0
[ 3180.940811] percpu_var:disp_vars():  cpu  2: pcpa = +0, rx =     0, tx =     0
[ 3180.940822] percpu_var:disp_vars():  cpu  3: pcpa = +0, rx =     0, tx =     0
[ 3180.940838] percpu_var:thrd_work(): Our kernel thread #0 exiting now...
[ 3180.956032] percpu_var:thrd_work():   thrd_1/cpu1: pcp ctx: tx =     0, rx =   200
[ 3180.956454] percpu_var:thrd_work():   thrd_1/cpu1: pcpa = -2
[ 3180.956821] percpu_var:thrd_work():   thrd_1/cpu1: pcp ctx: tx =     0, rx =   400
[ 3180.959607] percpu_var:thrd_work():   thrd_1/cpu1: pcpa = -3
[ 3180.960655] percpu_var:thrd_work():   thrd_1/cpu1: pcp ctx: tx =     0, rx =   600
[ 3180.963535] percpu_var:disp_vars(): 001) [thrd_1/1]:837   |  .N.0   /* disp_vars() */
[ 3180.965357] percpu_var:disp_vars():  cpu  0: pcpa = +3, rx =     0, tx =   300
[ 3180.969093] percpu_var:disp_vars():  cpu  1: pcpa = -3, rx =   600, tx =     0
[ 3180.970835] percpu_var:disp_vars():  cpu  2: pcpa = +0, rx =     0, tx =     0
[ 3180.971269] percpu_var:disp_vars():  cpu  3: pcpa = +0, rx =     0, tx =     0
[ 3180.971561] percpu_var:thrd_work(): Our kernel thread #1 exiting now...

解析

  • 两个线程共享的数据 val与ctx,互不干扰
  • 线程0执行后,val=3;    ctx->tx += 100, 最后结果 300
  • 线程1执行后,val=-3;ctx->rx += 200; 最后结果 600

Linux内核变量中per-CPU的使用相关推荐

  1. Linux环境变量中PS1

    Linux环境变量中PS1是很重要的环境变量: PS(Prompt Sign): 是指命令提示符,例如在Fedora 12的终端下:[liutao@liutao ~]$ ,在设定PS1环境变量时,我们 ...

  2. c++ map 初始化_如何调整Linux内核启动中的驱动初始化顺序?

    如何调整Linux内核启动中的驱动初始化顺序?[问题] 此处我要实现的是将芯片的ID用于网卡MAC地址,网卡驱动是enc28j60_init. 但是,读取芯片ID的函数,在as352x_afe_ini ...

  3. Linux内核配置(二) :CPU类型配置

    5. Processor type and features 处理器类型及特性 5.1. Symmetric multi-processing support (SMP) 对称多处理器支持. 这将支持 ...

  4. Linux内核网络中数据报在协议层的处理

    1. 前言 本文主要分析数据报从 IP 协议层进入协议栈,通过udp协议层,到达 socket,最终被用户程序读取的过程.在此过程中,介绍了 IP 协议层和 UDP 协议层中监测数据和网络调优的方法. ...

  5. linux内核编译如何选择cpu类型,Ubuntu内核编译和CPU Hot-Plug特性配置全过程及遇到问题记录...

    最近编译Palacios需要linux的内核支持cpu Hot-Plug(内存热插拔)特性,无奈我机器上安装的Ubuntu10.04系统默认不支持内存热插拔特性,所以需要修改配置文件并重新编译linu ...

  6. Linux下编译build的命令,Linux内核编译中build目录设置

    配置参数 最近在分析yocto中名为poky的嵌入式自动构建系统.在对内核进行定制的时候,看到了一个在进行内核编译时挺有用的特性,之前(作为野生程序员的我)一直没有发现. 该特性就是将源码与编译工作目 ...

  7. Linux内核网络中的软中断ksoftirqd

    1. 前言 之前分享过Linux内核网络数据包的接收过程,当执行到网卡通过硬件中断(IRQ)通知CPU,告诉它有数据来了,CPU会根据中断表,调用已经注册的中断函数,这个中断函数会调到驱动程序(NIC ...

  8. Linux内核学习:EXT4 文件系统在 Linux 内核系统中的读写过程

    目录 1 概述 2 虚拟文件系统 与 Ext4 文件系统 2.1 sys_write( ) 代码跟踪 2.2 sys_write( ) 过程分析 2.3 sys_write( ) 的核心部分 vfs_ ...

  9. Linux内核defconfig在哪,Linux内核根目录中的配置文件.config中包含了许多宏定义,...

    满意答案 大大bigone 推荐于 2017.11.22 采纳率:52%    等级:9 已帮助:813人 一.Linux内核的配置系统由三个部分组成,分别是: 1.Makefile:分布在 Linu ...

最新文章

  1. View绘制流程的入口
  2. 十进制 转换为 二进制
  3. python学了可以干什么-学了Python都能干什么,哪个最赚钱?
  4. 数据结构简介以及抽象数据类型的实现
  5. ubuntu18.04安装openresty
  6. micropython会商用吗_NSF商用食品设备认证解析
  7. linux切换默认编辑器
  8. sklearn之逻辑回归和岭回归
  9. (转)J2EE十三个技术规范
  10. Julia: 由0.3 升级到0.4 版本的变化
  11. DELPHI利用WMI获取主板参数
  12. 一个权限管理系统如何设计
  13. OVS使用VLAN隔离VM流量
  14. HTML超大图片加载显示解决方案--图片切割转换成瓦片地图(BaiduMapTileCutter)
  15. H指数(h-index)的Python实现
  16. Html编码(#数字型)与解码小结 - 针对Puny Code(中文域名)的解码处理
  17. 第一天 :二分查找+移除元素
  18. 数据表中常见的数据类型
  19. 安徽大学在校生如何校外访问图书馆资源
  20. C语言练习-day29

热门文章

  1. python库大全(转)
  2. 极海APM微控制器基于IAR开发环境搭建与工程调试配置方法
  3. 007-安装百度云,搜狗输入法,播放器
  4. 基于JAVA跨境电商网站计算机毕业设计源码+数据库+lw文档+系统+部署
  5. 通过FFMpeg将MOV视频转为黑白通道的mp4(可设置上下/左右)
  6. ThreadPoolExecutor最佳实践--如何选择线程数
  7. css设置了超出隐藏省略号无效解决
  8. 2021年高考防骗预警,6大骗局,防骗指南,考生、家长请注意
  9. 男女比例失调,农村“光棍危机”有多严重?
  10. Qt编写安防视频监控系统17-在线地图