Linux驱动之 原子操作
Linux驱动之 原子操作学习记录:
概念:
- 原子操作是指在执行过程中不会被别的代码所中断的操作,即它是最小的执行单位。
- 最简单的原子操作就是一条条的汇编指令(不包括一些伪指令,伪指令会被汇编器解释成多条汇编指令)。
在linux中原子操作对应的数据结构为atomic t,定义如下:
typedef struct{int counter;
}atomic_t;
之所以定义这么一个数据类型,是为了让原子操作函数只接受 atomic_t 类型的操作数,如果传入的不是 atomic_t 类型数据,在程序编译阶段就不会通过;另一个原因就是确保编译器不会对相应的值进行访问优化,确保对它的访问都是对内存的访问,而不是对寄存器的访问。
分为 位 和 整型变量 两类原子操作。
主要整型原子操作:
atomic_t v = ATOMIC_INIT(0); //定义原子变量v, 并初始化为0
atomic_t v = ATOMIC_INIT(0); //定义原子变量v, 并初始化为0
atomic_set(atomic_t *v, int i); //赋值操作,设置原子变量v的值为i
atomic_read(atomic_t *v); //读操作,获得原子变量的值,返回
void atomic_add(int i, atomic_t *v); //原子变量+i
void atomic_sub(int i, atomic_t *v); //原子变量-i
void atomic_inc(atomic_t *v); //原子变量+1
void atomic_dec(atomic_t *v); //原子变量-1
赋值操作宏:
ARM 处理器有直接对内存地址进行赋值的指令(STR)。
/*** atomic_set - set atomic variable* @v: pointer of type atomic_t* @i: required value* Atomically sets the value of @v to @i.*/
#define atomic_set(v, i) (((v)->counter) = (i))
读操作宏:
用 volatile 来防止编译器对变量访问的优化,确保是对内存的访问,而不是对寄存器的访问。
/*** atomic_read - read atomic variable* @v: pointer of type atomic_t* Atomically reads the value of @v.*/
#define atomic_read(v) (*(volatile int *)&(v)->counter)
对原子变量执行自增,自减和减操作后 ,测试其是否为0,为 0 则返回 true,否则返回 false :
int atomic_inc_and_test(atomic_t *v); //自增
int atomic_dec_and_test(atomic_t *v); //自减
int atomic_sub_and_test(int i, atomic_t *v); //减i值
对原子变量进行加/减,自增/自减操作,并返回新的值:
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_sub_return(atomic_t *v);
加操作源码实现过程:
/*** atomic_add_return - add integer to atomic variable* @i: integer value to add* @v: pointer of type atomic_t* Atomically adds @i to @v and returns the result*/
static inline int atomic_add_return(int i, atomic_t *v)
{unsigned long flags;int temp;// 通过关闭中断防止其他进程打断代码的执行raw_local_irq_save(flags); /* Don't trace it in an irqsoff handler */temp = v->counter;temp += i;v->counter = temp;// 恢复中断原始的状态 raw_local_irq_restore(flags);return temp;
}
位原子操作:
void set_bit(nr, void *addr); //将addr地址的nr位 置为1
void clear_bit(nr, void *addr); //将addr地址的nr位 清0
void change_bit(nr, void *addr); //对addr地址的nr位 反置
int test_bit(nr, void *addr); //返回addr地址的nr位
//先设值,后返回。
int test_and_set_bit(nr, void *addr); //将addr地址的第nr位设置成1,并返回原来这一位的值
int test_and_clear_bit(nr, void *addr);//将addr地址的第nr位请0,并返回原来这一位的值
int test_and_change_bit(nr, void *addr);//将addr地址的第nr位反置,并返回原来这一位的值
原子变量,实现设备单进程使用
demo.c
原子变量定义:
#include <asm/atomic.h>
static atomic_t v = ATOMIC_INIT(1); //定义原子变量并初始化为1
打开设备函数,加入原子操作
/** @description : 打开设备* @param - inode : 传递给驱动的inode* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量* 一般在open的时候将private_data指向设备结构体。* @return : 0 成功;其他 失败*/
static int hello_open (struct inode *inode, struct file *filep)
{printk("hello_open()\n");//第一个进程进来,对原子变量v执行自减1-1 = 0,为 0 则返回 true,取反,if 条件不成立,执行后面程序//第二个进程来打开驱动程序,0-1 = 负1,返回 false ,取反,if 条件成立,运行里面的代码,将原子变量加一恢复到 0,程序返回if (!atomic_dec_and_test(&v)){atomic_inc(&v); //原子变量+1 return -EBUSY;}printk("hello_open() atomic v = %d \n",v);return 0;
}
关闭/释放设备函数,加入原子操作
/** @description : 关闭/释放设备* @param - filp : 要关闭的设备文件(文件描述符)* @return : 0 成功;其他 失败*/
static int hello_release (struct inode *inode, struct file *filep)
{printk("hello_release()\n");//退出时, 自增,恢复原子变量值为 1:atomic_inc(&v);printk("hello_release() atomic v = %d \n",v);return 0;
}
第二个进程就无法使用这个设备驱动了:
./a.out
dmesg:
root@ubuntu16:/home/cxx/driver/8_atomic# dmesg41296.690268] chrdev_hello init!
[41296.690269] hello_init() atomic v = 1 //初始化时原子变量值
[41296.690271] chrdev_hello major=243,minor=0
[41300.945360] hello_open() //应用程序打开设备
[41300.945362] hello_open()atomic v = 0 //值
[41314.207845] hello_open()
[41323.295260] hello_open()
[41431.191540] hello_release() //应用程序释放该设备
[41431.191542] hello_release() atomic v = 1 //值恢复
root@ubuntu16:/home/cxx/driver/8_atomic#
root@ubuntu16:/home/cxx/driver/8_atomic# rmmod cdev_hello
root@ubuntu16:/home/cxx/driver/8_atomic# dmesg41296.690268] chrdev_hello init!
[41296.690269] hello_init() atomic v = 1
[41296.690271] chrdev_hello major=243,minor=0
[41300.945360] hello_open()
[41300.945362] hello_open()atomic v = 0
[41314.207845] hello_open()
[41323.295260] hello_open()
[41431.191540] hello_release()
[41431.191542] hello_release() atomic v = 1 42017.464401] chrdev_hello exit!
[42017.464402] hello_exit() atomic v = 1
root@ubuntu16:/home/cxx/driver/8_atomic#
在Armv6开始支持多核,通过ldrex与strex指令来保证数据操作的原子性,比如自旋锁的上锁操作、原子变量操作等。在Armv6之前,都是单核,为保证数据的原子性,需要进行关中断操作。对于多核平台,关中断操作只能关闭本核中断,要想对数据进行原子操作,必须借助ldrex指令与strex。
独占加载和存储寄存器:
LDREX{cond} Rt, [Rn {, #offset}]
STREX{cond} Rd, Rt, [Rn {, #offset}]
LDREXB{cond} Rt, [Rn] 字节加载
STREXB{cond} Rd, Rt, [Rn] 字节存储
LDREXH{cond} Rt, [Rn] 半字加载
STREXH{cond} Rd, Rt, [Rn] 半字存储
LDREXD{cond} Rt, Rt2, [Rn] 双字加载
STREXD{cond} Rd, Rt, Rt2, [Rn] 双字存储
/*
* @cond 是一个可选的条件代码(请参阅条件执行)。
* @Rd 是存放返回状态的目标寄存器。
* @Rt 是要加载或存储的寄存器。
* @Rt2 为进行双字加载或存储时要用到的第二个寄存器。
* @Rn 是内存地址所基于的寄存器。
* @offset 为应用于 Rn 中的值的可选偏移量。offset 只可用于 Thumb-2 指令中。如果省略 offset,则认为偏移量为 0。
*/
参考原文链接:https://blog.csdn.net/daocaokafei/article/details/108278107
Linux驱动之 原子操作相关推荐
- 树莓派linux led字符设备驱动(原子操作)
一.原子操作 linux驱动中为了处理对共享资源的并发访问,引入了原子操作.原子操作就是指不能再进一步分割的操作,一般原子操作用于变量或者位操作. Linux 内核提供了两组原子操作 API 函数 ...
- Linux驱动开发必看详解神秘内核(完全转载)
Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html IT168 技术文档]在开始步入Lin ...
- linux驱动工程面试必问知识点
linux内核原理面试必问(由易到难) 简单型 1:linux中内核空间及用户空间的区别?用户空间与内核通信方式有哪些? 2:linux中内存划分及如何使用?虚拟地址及物理地址的概念及彼此之间的转化, ...
- Linux驱动编程 step-by-step
第三次看了LDD3了(虽然现在已经是kernel3.0但从这本书商还是能学到很多) 每次都有一些收获 现在终于能够写一写代码了 驱动程序的作用: 简单来说 驱动程序就是使计算机与设备通信的特殊的代码, ...
- linux驱动面试题整理
1.字符型驱动设备你是怎么创建设备文件的,就是/dev/下面的设备文件,供上层应用程序打开使用的文件? 答:mknod命令结合设备的主设备号和次设备号,可创建一个设备文件. 评:这只是其中一种方式,也 ...
- Linux驱动快速入门
本公众号分享的所有技术仅用于学习交流,请勿用于其他非法活动,如果错漏,欢迎留言指正 应用层:<LUNIX环境高级编程第二版> <Linux程序设计(第四版)> 内核层:< ...
- 正点原子linux驱动教程,正点原子 手把手教你学Linux之驱动开发篇
简 介 该课程是正点原子手把手教你学Linux系列课程,该课程配套开发板为正点原子alpha/mini Linux开发板. 手把手教你学Linux之驱动开发篇: 第1讲 Linux驱动开发与裸机开发区 ...
- Linux驱动笔试知识
Linux驱动笔试知识 1.Linux设备中字符设备与块设备有什么主要的区别?请分别列举一些实际的设备说出它们是属于哪 一类设备 字符设备字符设备是个能够像字节流(类似文件)一样被访问的设备,字符设备 ...
- linux驱动系列学习之OLED(i2c接口)(八)
一.OLED简介 本次使用的开发板正点原子Linux阿波罗.屏幕是i2c接口的四针.分辨率为128×64的oled液晶屏.通信接口为i2c.具体的i2c框架使用请参考前面的文章.oled的详细简介请参 ...
最新文章
- java中sql去除游标_java.sql.SQLException:-ORA-01000:已超过最大打开游标
- jdbc详解:1、创建数据库connection连接
- php fwrite服务器上写不进去_使用PHP来简单的创建一个RPC服务
- 苹果6s最大屏幕尺寸_iPhone 6s:经典的小屏旗舰,百元价位也能做苹果党
- 服务器系统格式,服务器系统编码格式
- C++读取WINDOWS注册表
- 软件测试学习之悟空CRM项目测试用例编写
- 阿里云 python_阿里云python sdk
- 基因组数据质控中:先进行SNP缺失质控还是样本缺失质控?
- 戴维斯大学计算机排名,加利福尼亚大学戴维斯分校计算机科学专业排名第37(2020年USNEWS美国排名)...
- vue-baidu-map 百度地图(定位替换图标,添加标签)
- Gartner曾劭清:云计算市场依然存在太多变局
- 快速高效入门3D建模学习教程,让你最快从小白到建模大师!
- 金启孮:普通话其实是满州人的蹩脚汉语
- 循环彩灯c语言程序,PLC彩灯实例,一个简单的顺序控制
- FT232H USB转串口,I2C,JTAG高速芯片
- Elasticsearch5.0 安装 以及 问题集锦
- QQ合并的聊天记录可以通过什么方式转换成链接,点开链接就能看合并的聊天记录?
- 通达信资金净流入公式_资金净流入选股公式源码-通达信公式 -程序化交易(CXH99.COM)...
- intel万兆以太网网卡吞吐量测试
热门文章
- 2022年全国职业院校技能大赛 网络搭建与应用赛项 公开赛卷 (十套合卷)
- Windows7+Ubuntu10.04双系统安装指南
- 最小二乘法的原理及python实现
- 学习笔记-CCS-MSP430F5529[快速入门篇二]
- VBA多条件选择及自动填表及计算汇报
- 庆祝JavaCard技术领导SmartCard技术超过五年
- Mask Scoring RCNN训练自己的数据
- “师创杯”山东理工大学第九届ACM程序设计竞赛 正式赛 F.校赛~校赛~【思维+规律题】
- banner设圆角_illustrator大气、科技感的Banner设计教程
- python datetime日期时间去掉 时分秒