当前位置:网站首页>学习内存屏障
学习内存屏障
2022-06-26 15:09:00 【make-n】
看了下面的博客和评论:记录对内存屏障的理解。
https://blog.csdn.net/world_hello_100/article/details/50131497
【1】编译器屏障:优化等级O2,O3时可能改变指令实际执行顺序,引入指令和代码逻辑不符问题。
解决方法1:添加编译器 barrier:
#define barrier() __asm__ __volatile__("" ::: "memory")
解决方法2:
还可以使用 volatile 这个关键字来避免编译时内存乱序访问(而无法避免后面要说的运行时内存乱序访问)。
在 Linux 内核中,提供了一个宏 ACCESS_ONCE 来避免编译器对连续的 ACCESS_ONCE 实例进行指令重排。ACCESS_ONCE(x)作为左值使用。
#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
/******* 分割符 ******/
ACCESS_ONCE(x) = r;
ACCESS_ONCE(y) = x;
【2】运行时乱序
在乱序执行时,一个处理器真正执行指令的顺序由可用的输入数据决定,而非程序员编写的顺序。
乱序处理器(Out-of-order processors)处理指令通常有以下几步:
1,指令获取
2,指令被分发到指令队列
3,指令在指令队列中等待,直到输入操作对象可用(一旦输入操作对象可用,指令就可以离开队列,即便更早的指令未被执行)
4,指令被分配到适当的功能单元并执行
5,执行结果被放入队列(而不立即写入寄存器堆)
6,只有所有更早请求执行的指令的执行结果被写入寄存器堆后,指令执行的结果才被写入寄存器堆(执行结果重排序,让执行看起来是有序的)
在单CPU上,指令的获取和结果的回写是有序的,不存在CPU执行指令乱序的问题。但是在多处理器上每个CPU有自己的cache内存,当CPU写操作时,是写到cache,不能保证cache的一致性,就会产生问题,必须通过一个 cache 一致性协议来避免数据不一致,而这个协议通讯的过程就可能导致乱序访问的出现,也就是这里说的运行时内存乱序访问是因多核cache不一致引起的。
实际的应用程序开发中,开发者可能完全不知道 Memory barrier 就可以开发正确的多线程程序,这主要是因为各种同步机制中已经隐含了 Memory barrier(但和实际的 Memory barrier 有细微差别),这就使得不直接使用 Memory barrier 不会存在任何问题。但是如果你希望编写诸如无锁数据结构,那么 Memory barrier 还是很有用的。
Memory barrier 常用场合包括:
实现同步原语(synchronization primitives)
实现无锁数据结构(lock-free data structures)
驱动程序
内存屏障接口
通用 barrier,保证读写操作有序的(屏障前后有读,又有写的操作,保证这两个操作的有序性),mb()
写操作 barrier,仅保证写操作有序的(屏障前后都是写操作,保证这两个写操作的有序性),wmb()
读操作 barrier,仅保证读操作有序的(屏障前后都是读操作,保证这两个读操作的有序性),rmb()
分析一下无锁结构:
/** * __kfifo_put - puts some data into the FIFO, no locking version * @fifo: the fifo to be used. * @buffer: the data to be added. * @len: the length of the data to be added. * * This function copies at most @len bytes from the @buffer into * the FIFO depending on the free space, and returns the number of * bytes copied. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these functions. */
unsigned int __kfifo_put(struct kfifo *fifo,
const unsigned char *buffer, unsigned int len)
{
unsigned int l;
len = min(len, fifo->size - fifo->in + fifo->out);
/** Ensure that we sample the fifo->out index -before- we * start putting bytes into the kfifo.*/
/*这里保证 先读取到正确的fifo->out,计算出正确的len,然后写数据到kfifo, 如果读取到的kfifo错误,计算出kfifo的 可写空间偏小 */
smp_mb();
/* first put the data starting from fifo->in to buffer end */
l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
/* then put the rest (if any) at the beginning of the buffer */
memcpy(fifo->buffer, buffer + l, len - l);
/** Ensure that we add the bytes to the kfifo -before- * we update the fifo->in index. */
/*这里保证前后的 写操作的有序,先写数据,再更新in index */
smp_wmb();
fifo->in += len;
return len;
}
EXPORT_SYMBOL(__kfifo_put);
/** * __kfifo_get - gets some data from the FIFO, no locking version * @fifo: the fifo to be used. * @buffer: where the data must be copied. * @len: the size of the destination buffer. * * This function copies at most @len bytes from the FIFO into the * @buffer and returns the number of copied bytes. * * Note that with only one concurrent reader and one concurrent * writer, you don't need extra locking to use these functions. */
unsigned int __kfifo_get(struct kfifo *fifo,
unsigned char *buffer, unsigned int len)
{
unsigned int l;
len = min(len, fifo->in - fifo->out);
/** Ensure that we sample the fifo->in index -before- we * start removing bytes from the kfifo.*/
/* 先读取到正确的fifo->in,计算正确的数据长度,然后读取kfifo 的数据, 保证两个读操作的有序性*/
smp_rmb();
/* first get the data from fifo->out until the end of the buffer */
l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
/* then get the rest (if any) from the beginning of the buffer */
memcpy(buffer + l, fifo->buffer, len - l);
/** Ensure that we remove the bytes from the kfifo -before- * we update the fifo->out index.*/
/*先读到kfifo的数据,然后才写fifo->out index,一个读,一个写操作*/
smp_mb();
fifo->out += len;
return len;
}
EXPORT_SYMBOL(__kfifo_get);
最后这里顺带说一下此实现使用到的一些和本文主题无关的技巧:
1,使用 与& 操作来求取环形缓冲区的下标,相比取余操作来求取下标的做法效率要高不少。使用与操作求取下标的前提是环形缓冲区的大小必须是 2 的 N 次方,换而言之就是说环形缓冲区的大小为一个仅有一个 1 的二进制数,那么 index & (size – 1) 则为求取的下标(这不难理解)
2,使用了 in 和 out 两个索引且 in 和 out 是一直递增的(此做法比较巧妙),这样能够避免一些复杂的条件判断(某些实现下,in == out 时还无法区分缓冲区是空还是满)
【疑问】:
in 和 out 是一直递增的,in溢出后归0,out未溢出,计算出
len = min(len, fifo->in - fifo->out);的有效数据会不会出错。
边栏推荐
- [CEPH] cephfs internal implementation (II): example -- undigested
- [tcapulusdb knowledge base] Introduction to tcapulusdb general documents
- 【TcaplusDB知识库】TcaplusDB单据受理-事务执行介绍
- TCP congestion control details | 1 summary
- 【TcaplusDB知识库】TcaplusDB常规单据介绍
- Restcloud ETL extraction de données de table de base de données dynamique
- Unity C# 网络学习(九)——WWWFrom
- [tcapulusdb knowledge base] tcapulusdb doc acceptance - create business introduction
- Unity C # e-learning (10) -- unitywebrequest (2)
- 整理了一批脚本标准的函数模块(2021版)
猜你喜欢
MySQL数据库基本SQL语句教程之高级操作

功能:crypto-js加密解密

程序分析与优化 - 8 寄存器分配

【文件】VFS四大struct:file、dentry、inode、super_block 是什么?区别?关系?--编辑中

【TcaplusDB知识库】TcaplusDB单据受理-创建业务介绍

How to load the contour CAD drawing of the engineering coordinate system obtained by the designer into the new earth

Vsomeip3 dual computer communication file configuration

RestCloud ETL抽取動態庫錶數據實踐

BLE抓包调试信息分析

【TcaplusDB知识库】TcaplusDB OMS业务人员权限介绍
随机推荐
Sikuli 基于图形识别的自动化测试技术
SAP sales data actual shipment data export sales
SAP 销售数据 实际发货数据导出 销量
Sorted out a batch of script standard function modules (version 2021)
RestCloud ETL解决shell脚本参数化
2022北京石景山区专精特新中小企业申报流程,补贴10-20万
【TcaplusDB知识库】TcaplusDB单据受理-事务执行介绍
Unity unitywebrequest download package
MongoDB系列之Window环境部署配置
R language uses ggplot2 to visualize the results of Poisson regression model and count results under different parameter combinations
数据库-序列
【SNMP】snmp trap 介绍、安装、命令|Trap的发送与接收代码实现
Function: crypto JS encryption and decryption
【TcaplusDB知识库】TcaplusDB常规单据介绍
Lexin AWS IOT expresslink module achieves universal availability
Principle of TCP reset attack
Bank of Beijing x Huawei: network intelligent operation and maintenance tamps the base of digital transformation service
SAP gui 770 下载
[CEPH] cephfs internal implementation (II): example -- undigested
【ceph】mkdir|mksnap流程源码分析|锁状态切换实例