数组形式

支持SMP的现代操作系统使用每个cpu上的数据,对于给定的处理器其数据是唯一的;一般来说,每个cpu的数据存放在一个数组中,数组总的每一项对应着系统上的一个存在的处理器;按当前处理器号确定这个数组的当前元素;使用方式如下:

 unsigned long my_percpu[NR_CPUS];

 int cpu;

 cpu = get_cpu(); /* 获取当前处理器,并禁止抢占 */
my_percpu[cpu]++; /* 对变量做处理 */
put_cpu(); /* 激活内核抢占 */

上面代码并没有出现锁,这是因为所操作的数据对当前处理器来说是唯一的;除了当前处理器之外,没有其他处理器可接触到这个数据,不存在并发访问的问题,所以当前处理器可以再不用锁的情况下安全访问它;

现在,内核抢占成了唯一需要关注的问题了,内核抢占会引起下面的两个问题:

1. 如果代码被其他处理器抢占并重新调度,那么这时cpu变量就会无效,因为它指向的是错误的处理器;(通常,代码获得当前处理器后是不可以睡眠的);

2. 如果另一个任务抢占了代码,那么有可能在同一处理器上发生并发访问my_percpu的情况,显然属于一个竞态;

上述代码中在调用get_put()时,禁止了内核抢占;相对的调用put_cpu()时又会重新激活当前处理器号;

新的接口

2.6内核开始为了方便创建和操作每个cpu数据,而引进了新的操作接口,称为percpu,该接口归纳了前面所述的操作行为,简化了创建和操作每个cpu的数据;

但前面说的创建和访问每个cpu的方法仍然有效,不过大型对称多处理器计算机要求对每个cpu数据操作更简单,功能更强大,所以新接口应运而生;

编译时的每个cpu数据

编译期间定义每个cpu变量:

 DEFINE_PER_CPU(type,name)

这个语句为系统中每个cpu都创建了一个类型为type,名称为name的变量实例,如果需要在别处声明变量,则应该使用下面的宏:

 DECLARE_PER_CPU(type,name)

可以利用get_cpu_var()和put_cpu_var()函数来操作变量;

 get_cpu_var(name)++; /*禁止抢占,操作cpu变量*/
put_cpu_var(name); /*完成,重新激活内核抢占*/

还可以通过per_cpu(name, cpu)获取别的处理器上的每个cpu数据:

 per_cpu(name, cpu)++;/* 增加指定处理器上的数据值 */

注意:per_cpu()函数既不会禁止内核抢占,也不会提供任何形式的锁保护;如果一个处理器可以接触到其他处理器上的数据,那就必须给数据上锁;

运行时的每个cpu数据

 #define alloc_percpu(type)                        \
(typeof(type) __percpu *)__alloc_percpu(sizeof(type), \
__alignof__(type)) void __percpu *__alloc_percpu(size_t size, size_t align) void free_percpu(void __percpu *__pdata)

alloc_percpu()给系统中每个处理器分配一个指定类型对象的实例,它是__alloc_percpu的一个封装,原始函数接收两个参数:一个是要分配的实际字节数,一个是分配时要按多少字节对齐;而封装后的alloc_percpu()是按照字节对齐–按照给定的类型的自然边界对齐;

free_percpu()将释放所有处理器上指定的每个cpu数据;

alloc_percpu()或者是__alloc_percpu()会返回一个指针,它用来间接引用动态创建的每个cpu数据,内核提供了两个宏利用指针来获取每个cpu数据:

 /*
* Must be an lvalue. Since @var must be a simple identifier,
* we force a syntax error here if it isn't.
*/
#define get_cpu_var(var) \
(*({ \
preempt_disable(); \
this_cpu_ptr(&var); \
})) /*
* The weird & is necessary because sparse considers (void)(var) to be
* a direct dereference of percpu variable (var).
*/
#define put_cpu_var(var) \
do { \
(void)&(var); \
preempt_enable(); \
} while ()

get_cpu_var()返回一个指向当前处理器数据的特殊实例,它同时会禁止内核抢占;而在put_cpu_var()中会重新激活内核抢占;

每个cpu数据好处

1. 减少了数据锁定;每个处理器访问每个处理器的数据,可以不需要任何锁;

2. 大大减少了缓存失败;percpu接口缓存对齐所有数据,以便确保在访问一个处理器的数据时,不会将林我国一个处理器的数据带入同一个缓存线上;

注意:不能再访问每个cpu数据过程中睡眠,否则,醒来之后可能已经到达其他处理器上了;

Linux设备驱动程序 之 per-cpu变量的更多相关文章

  1. linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)

    原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...

  2. Linux设备驱动程序学习之分配内存

    内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题. 我已经在第一个scull模块中使用了 kmalloc 和 kfree 来分配和释放内存空间. kmalloc 函数内幕 ...

  3. Linux设备驱动程序学习----2.内核模块与应用程序的对比

    内核模块与应用程序的对比 更多内容请参考Linux设备驱动程序学习----目录 1. 内核模块与应用程序的对比 内核模块和应用程序之间的不同之处: 大多数中小规模的应用程序是从头到尾执行单个任务,而模 ...

  4. Linux设备驱动程序 第三版 读书笔记(一)

    Linux设备驱动程序 第三版 读书笔记(一) Bob Zhang 2017.08.25 编写基本的Hello World模块 #include <linux/init.h> #inclu ...

  5. Linux设备驱动程序学习----1.设备驱动程序简介

    设备驱动程序简介 更多内容请参考Linux设备驱动程序学习----目录 1. 简介   Linux系统的优点是,系统内部实现细节对所有人都是公开的.Linux内核由大量复杂的代码组成,设备驱动程序可以 ...

  6. Linux设备驱动程序学习----3.模块的编译和装载

    模块的编译和装载 更多内容请参考Linux设备驱动程序学习----目录 1. 设置测试系统 第1步,要先从kernel.org的镜像网站上获取一个主线内核,并安装到自己的系统中,因为学习驱动程序的编写 ...

  7. linux设备驱动程序-i2c(2)-adapter和设备树的解析

    linux设备驱动程序-i2c(2)-adapter和设备树的解析 (注: 基于beagle bone green开发板,linux4.14内核版本) 在本系列linux内核i2c框架的前两篇,分别讲 ...

  8. linux设备驱动程序--hello-world

    linux字符设备驱动程序--hello_world 基于4.14内核, beagleBone green平台 PC端的设备驱动程序 有过电脑使用经验的人都知道,当我们将外部硬件设备比如鼠标键盘插入到 ...

  9. 嵌入式Linux设备驱动程序:在运行时读取驱动程序状态

    嵌入式Linux设备驱动程序:在运行时读取驱动程序状态 Embedded Linux device drivers: Reading driver state at runtime 在运行时了解驱动程 ...

  10. 嵌入式Linux设备驱动程序:用户空间中的设备驱动程序

    嵌入式Linux设备驱动程序:用户空间中的设备驱动程序 Embedded Linux device drivers: Device drivers in user space Interfacing ...

随机推荐

  1. vue、react中循环遍历为什么会有key,key有什么作用?

    先讲一下,vue和react都是在操作虚拟dom,并且根据diff算法进行新旧dom对比,从而更新dom,以vue举例: vue官方文档中写到有 key 的特殊属性主要用在 Vue 的虚拟 DOM 算 ...

  2. elment 中tree组件展开所有和收缩所有

    upAll () { // 全部展开 遍历变成true let self = this; // 将没有转换成树的原数据 let treeList = this.sourceData; for (let ...

  3. python多线程与多进程异步事件框架

    多线程简单实现 #!/usr/bin/env python # -*- coding: UTF-8 -*- import logging import queue import threading f ...

  4. 7.生产者消费者 案例 (使用Lock 同步锁 方式,使用Condition完成线程之间的通信)

    /* * 生产者消费者 案例 (使用Lock 同步锁 方式,使用Condition完成线程之间的通信) * */ public class TestProductorAndConsumerForLoc ...

  5. MySQL无法启动问题解决Warning: World-writable config file ‘/etc/my.cnf’ is ignored

    今天重启一台内网服务器,发现mysql无法正常重启,执行systemctl start mysql,报错如下 Starting LSB: start and stop MySQL... Dec 11 ...

  6. c# 执行调用Oracle Procedure传参及回传值

    ////定義參數               //IDataParameter[] parameters =               //             {               ...

  7. rt-thread下调试elmfat 问题记录

    硬件平台:stm32f107   SPI flash:w25q32 RTT版本:v2.1 w25q32的驱动大神们已经写好(w25qxx.c),我只需要照猫画虎的实现相应SPI的驱动程序即可(bsp例 ...

  8. 面对runc逃逸漏洞,华为云容器为您保驾护航

    背景信息 基于runc运行时的容器存在安全漏洞,攻击者可以通过恶意容器镜像等方式获取宿主机root执行权限.漏洞CVE-2019-5736的详细信息,请参见 https://cve.mitre.org ...

  9. win10锁屏壁纸文件夹Assets中无文件问题的解决方法

    一.前言 win10在锁屏时会有很多精美的壁纸,在网上查找到win10锁屏壁纸存放目录为 : C:\Users\你的用户名\AppData\Local\Packages\Microsoft.Windo ...

  10. day 03 turtle 画鹅

    turtle 画鹅 import turtle t=turtle turtle.speed(10) t. setup(800,600) #画头 turtle.penup() turtle.goto(0 ...