linux代码之atomic
/*
* 2020/12/4 15:07 qing
*
* 原子操作需要靠硬件实现
*/
/*
* atomic
*/
Linux内核中提供了各种各样的原子操作函数。除了最原始的读取和设置之外,还包括各种运算,以及位操作等等。而且有的原子操作还要返回操作过后变量的值,
有的要返回操作之前变量的值,如果再牵涉到内存屏障的问题,将这些因素组合起来,有非常多的原子操作函数。
/*
* Bus Lock(锁总线)
*/
CPU执行原子指令时,给总线上锁,这样在释放前,可以防止其它CPU的内存操作。
/*
* Cache Lock
*/
除了和IO紧密相关的(如MMIO),大部分的内存都是可以被cache的,由前面介绍的cache一致性原理,我们知道由cacheline处于Exclusive或Modified时,
该变量只有当前CPU缓存了数据,因此当进行原子操作时,发出Read Invalidate消息,使其它CPU上的缓存无效,cacheline变成Exclusive状态然后将该
cacheline上锁,接着就可以取数据,修改并写入cacheline,如果这时有其它CPU也进行原子操作,发出read invalidate消息,但由于当前CPU的
cacheline是locked状态,因此暂时不会回复消息,这样其它CPU就一直在等待,直到当前CPU完成,使cacheline变为unlocked状态。
/*
* 基本型
*/
基本型包括非RMW(Read Modifiy Write)的读写操作,以及RMW的算术和位操作等。
非RMW的操作很简单,只有两个,即用来读取的atomic_read()和用来写入的atomic_set()。
一般对单独变量的读取或写入操作本身都是原子的,如果代码中只对单个变量进行读写操作,而从来没对它使用RMW操作,那一般说明你用错了,
没必要使用内核提供的原子操作。
算术操作:包括加、减、递增和递减,命名形式是atomic_{add,sub,inc,dec}();
Bit位操作:包括与、或、异或和与非,命名形式是atomic_{and,or,xor,andnot}();
交换操作:包括交换(atomic_xchg())、比较交换(atomic_cmpxchg())和添加了返回成功与否的比较交换(atomic_try_cmpxchg())。
除了atomic_t类型外,Linux内核还支持对64位和长整形的atomic64_t和atomic_long_t类型,它们也都有对应的基本原子操作函数,
只不过函数前缀名分别是atomic64_和atomic_long_
static inline void atomic_add(int i, atomic_t *v)
{
atomic_add_return(i, v);
}
#ifndef atomic_add_return
#define atomic_add_return(...) \
__atomic_op_fence(atomic_add_return, __VA_ARGS__)
#endif
#ifndef __atomic_op_fence
#define __atomic_op_fence(op, args...) \
({ \
typeof(op##_relaxed(args)) __ret; \
smp_mb__before_atomic(); \
__ret = op##_relaxed(args); \
smp_mb__after_atomic(); \
__ret; \
})
#endif
/*
* 返回修改过后的值
*/
如果一个原子操作函数要返回修改过后的原子变量的值,那么该函数名会含有“_return”字符串,并且是在表示具体操作字符串的后面。
例如,atomic_add_return、atomic_dec_return_relaxed等。
/*
* 要返回修改之前的值
*/
如果一个原子操作函数要返回修改之前的原子变量的值,那么该函数名会含有“_fetch”字符串,并且是在表示具体操作字符串的前面。
例如,atomic_fetch_and、atomic_fetch_or_acquire等。
/*
* 有Acquire和Release单向屏障语义
*/
如果一个原子操作函数还需要包括Acquire或者Release单向屏障语义,那么该函数名会有“_acquire”或者“_release”后缀。
例如,atomic_xchg_acquire、atomic_cmpxchg_release等。
相反的,如果一个原子操作函数名有“_relaxed”后缀,表示这个函数没有被任何内存屏障保护,可以被任意重排序。
例如,atomic_add_return_relaxed、atomic_xchg_relaxed等。
只有当一个原子操作函数要返回值的时候才有可能添加_acquire、_release和_relaxed后缀。
/*
* 原子操作的重排序规则
*/
Linux内核的原子操作只保证对单一变量的某个操作是原子的,多个CPU同时操作时,不会出现中间的错误状态。但是,对这个原子操作本身,
并不一定保证其执行的顺序,在SMP系统下,有可能会出现重排序的问题。
因此,前面也提到过,有些原子操作函数自己就带了一定的内存屏障的语义。具体有没有带,带了多少,可以通过函数名看出来,具体规则如下:
非RMW的原子操作可以被任意重排序;
RMW的原子操作,如果没有返回值可以被任意重排序;
RMW的原子操作,如果有返回值,并且没有_relaxed、_acquire和_release后缀,是有内存屏障保护的,可以保证不被重排序;
RMW的原子操作,如果有返回值,且有_relaxed后缀,没有任何内存屏障保护,可以被任意重排序;
RMW的原子操作,如果有返回值,且有_acquire后缀,表示读(RMW中的R)操作是有Acquire单向屏障保护的;
RMW的原子操作,如果有返回值,且有_release后缀,表示写(RMW中的W)操作是有Release单向屏障保护的;
RMW的原子操作,如果有条件判断,那么条件是否的那部分会被任意重排序。
对于那些不提供内存屏障语义的原子操作来说,为了保证其本身不被重排序,还需要显式的在其前面或后面使用内存屏障。
Linux内核提供了两个函数,分别是smp_mb__before_atomic()用于原子操作函数的前面,和smp_mb__after_atomic()用于原子操作函数的后面。
/*
* ARMv8架构下原子操作的实现
*/
#define ATOMIC_INIT(i) { (i) }
/* atomic_read */
#define atomic_read(v) READ_ONCE((v)->counter)
#define __READ_ONCE(x, check) \
({ \
union { typeof(x) __val; char __c[1]; } __u; \
if (check) \
__read_once_size(&(x), __u.__c, sizeof(x)); \
else \
__read_once_size_nocheck(&(x), __u.__c, sizeof(x)); \
__u.__val; \
})
#define READ_ONCE(x) __READ_ONCE(x, 1)
static __always_inline
void __read_once_size(const volatile void *p, void *res, int size)
{
__READ_ONCE_SIZE;
}
#define __READ_ONCE_SIZE \
({ \
switch (size) { \
case 1: *(__u8 *)res = *(volatile __u8 *)p; break; \
case 2: *(__u16 *)res = *(volatile __u16 *)p; break; \
case 4: *(__u32 *)res = *(volatile __u32 *)p; break; \
case 8: *(__u64 *)res = *(volatile __u64 *)p; break; \
default: \
barrier(); \
__builtin_memcpy((void *)res, (const void *)p, size); \
barrier(); \
} \
})
/* atomic_set */
#define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
#define WRITE_ONCE(x, val) \
({ \
union { typeof(x) __val; char __c[1]; } __u = \
{ .__val = (__force typeof(x)) (val) }; \
__write_once_size(&(x), __u.__c, sizeof(x)); \
__u.__val; \
})
static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
switch (size) {
case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
default:
barrier();
__builtin_memcpy((void *)p, (const void *)res, size);
barrier();
}
}
/*
* 废弃的函数,学习一下C语法 及 指令
*/
static inline int
__ll_sc_atomic_add_return_acquire(int i, atomic_t *v)
{
unsigned long tmp;
int result;
asm volatile(
" // 将v->counter预取到CPU缓存\n" \
" prfm pstl1strm, %2\n" \
" // 将v->counter独占的读入result中\n" \
"1: ldaxr %w0, %2\n" \
" // result = result + i\n" \
" add %w0, %w0, %w3\n" \
" // 将result的值独占的存入v->counter中\n" \
" // 如果存入时独占标记被清除则将tmp置1\n" \
" stxr %w1, %w0, %2\n" \
" // 如果tmp被置1则从头再次执行一遍\n" \
" cbnz %w1, 1b\n" \
) \
: "=&r" (result), "=&r" (tmp), "+Q" (v->counter) \
: __stringify(I) "r" (i) \
: "memory"); \
\
return result; \
}
static inline int \
__ll_sc_atomic_fetch_or_release(int i, atomic_t *v) \
{ \
unsigned long tmp; \
int val, result; \
\
asm volatile( \
" // 将v->counter预取到CPU缓存\n" \
" prfm pstl1strm, %3\n" \
" // 将v->counter独占的读入result中\n" \
"1: ldxr %w0, %3\n" \
" // val = result | i\n" \
" orr %w1, %w0, %w4\n" \
" // 将val的值独占的存入v->counter中\n" \
" // 如果存入时独占标记被清除则将tmp置1\n" \
" stlxr %w2, %w1, %3\n" \
" // 如果tmp被置1则从头再次执行一遍\n" \
" cbnz %w2, 1b\n" \
: "=&r" (result), "=&r" (val), "=&r" (tmp), "+Q" (v->counter) \
: __stringify(K) "r" (i) \
: "memory"); \
\
return result; \
}
/* arch_cmpxchg */
#define __cmpxchg_wrapper(sfx, ptr, o, n) \
({ \
__typeof__(*(ptr)) __ret; \
__ret = (__typeof__(*(ptr))) \
__cmpxchg##sfx((ptr), (unsigned long)(o), \
(unsigned long)(n), sizeof(*(ptr))); \
__ret; \
})
#define arch_cmpxchg(...) __cmpxchg_wrapper( _mb, __VA_ARGS__)
arch_cmpxchg最终被宏定义成了__cmpxchg_wrapper,而它又调用了__cmpxchg_mb函数:
#define __CMPXCHG_GEN(sfx) \
static __always_inline unsigned long __cmpxchg##sfx(volatile void *ptr, \
unsigned long old, \
unsigned long new, \
int size) \
{ \
switch (size) { \
case 1: \
return __cmpxchg_case##sfx##_8(ptr, old, new); \
case 2: \
return __cmpxchg_case##sfx##_16(ptr, old, new); \
case 4: \
return __cmpxchg_case##sfx##_32(ptr, old, new); \
case 8: \
return __cmpxchg_case##sfx##_64(ptr, old, new); \
default: \
BUILD_BUG(); \
} \
\
unreachable(); \
}
__CMPXCHG_GEN()
__CMPXCHG_GEN(_acq)
__CMPXCHG_GEN(_rel)
__CMPXCHG_GEN(_mb)
#undef __CMPXCHG_GEN
linux代码之atomic相关推荐
- 如何在vs 下敲linux代码且具有提示功能
本文使用的是vs2010 如何在windows 下敲linux代码时具有提示功能那? 1 先安装vs2010 2 安装西红柿插件VA_X_Setup.官网现在地址https://www.wholeto ...
- 香蕉派 banana pi github 最新Linux 代码升级到kernel 4.2.
2019独角兽企业重金招聘Python工程师标准>>> 香蕉派 banana pi github 最新Linux 代码升级到kernel 4.2. https://github.co ...
- VSCode 阅读 Linux 代码怎么才不卡顿?这样做才能快的飞起!
Linux 内核代码用什么编辑器? 分享过怎么学习 Linux 内核代码的思路,当时顺便提了一点,奇伢是用 vscode 看内核代码.有同学对此提出了疑问: vscode 看 Linux 代码不卡吗? ...
- Socket 编程 windows到Linux代码移植遇到的问题
Socket 编程 windows到Linux代码移植遇到的问题 1.一些常用函数的移植 http://www.vckbase.com/document/viewdoc/?id=1586 2.网络 - ...
- linux代码、数据库备份
1.linux代码备份脚本 #!/bin/bash #指定运行的脚本shell #运行脚本要给用户执行权限 bakdir=/bakdir/web month=`date +%m` day=`date ...
- tar在linux编译为exe,将Linux代码移植到Windows的简单方法 1
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 一.前言 Linux拥有丰富各种源代码资源,但是大部分代码在Windows平台情况是无法正常编译的.Windows平台根本无法直接利用这些源代码资源.如果 ...
- 将Linux代码移植到Windows的简单方法
将Linux代码移植到Windows的简单方法 一.前言 Linux拥有丰富各种源代码资源,但是大部分代码在Windows平台情况是无法正常编译的.Windows平台根本无法直接利用这些源代码资源.如 ...
- 数据简化社区Google和Linux代码风格指南(附PDF公号发“代码风格”下载)
数据简化社区Google和Linux代码风格指南(附PDF公号发"代码风格"下载) 秦陇纪2019代码类 数据简化DataSimp 昨天 数据简化DataSimp导读:数据简化社区 ...
- linux在代码中表示什么意思,Linux代码中的unlikely和likely分别是什么意思?
Linux代码中的unlikely和likely分别是什么意思? 我在Linux代码中没有找unlikely和likely的定义. 在源代码中搜索出来很多无法找. 请各位赐教! 谢谢! | 内核里有很 ...
- linux代码动态分析软件,举例分析Linux动态库和静态库
函数库分为静态库和动态库两种.创建Linux静态库和Linux动态库和使用它们在这里将以举例的形式详述一下.静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库.动态库在程序编译时并不 ...
最新文章
- vim如何在底部打开新窗口
- MyBatis 流式查询
- Centos调整时间时区
- ffplay命令汇总
- Oracle之PLSQL总结
- 消息队列重要机制讲解以及MQ设计思路(kafka、rabbitmq、rocketmq)
- 解决由于sz rz导致抓包时文件容量增加
- 强悍的命令行 —— 路径相关
- 制作数据集---labelImg和labelme
- 印章识别软件_一种印章识别方法及系统技术方案
- fclose用法matlab,fclose和close之间的区别
- cadence破译时显示服务器失败,在服务器开启cadence失败 报错如下
- CODOSYS之结构化文本(ST)—— 初级篇(二)简介与基本概念
- Magento创建主题
- urllib3爬取网页源代码(爬虫)
- 计算机音乐谱东演员,抖音计算机乐谱有哪些 抖音计算机乐谱分享
- 只有一行VNC server running on ’::1:5900' 没有其他输出
- 教你炒股票22:将8亿的大米装到5个庄家的肚里
- 机器自动翻译古文拼音 - 十大宋词 - 满江红 怒发冲冠 南宋·岳飞
- 测试/开发程序员幽默的 “自嘲“?印象流派......
热门文章
- MYSQL 视图 触发器 存储过程 事务 索引
- C++系列中的一些修修补补
- establish connection
- Android苹方圆三合一字体,橘色主题-圆形图标 内置苹方+googlesans字体 透明文件夹...
- js拆分百分数_计算百分比Javascript
- java 聊天室 私聊_Java WebSocket实现网络聊天室(群聊+私聊)
- 洛谷P3366【模板】最小生成树-克鲁斯卡尔Kruskal算法详解附赠习题
- 该如何去认知Level 2 十档行情数据?
- AngularJS总结
- HTML5游子吟网页的完整代码,《游子吟》教学设计(5页)-原创力文档