当前位置:网站首页>学习内存屏障
学习内存屏障
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);的有效数据会不会出错。
边栏推荐
- RestCloud ETL解决shell脚本参数化
- 程序分析与优化 - 8 寄存器分配
- R language uses GLM function to build Poisson logarithm linear regression model, processes three-dimensional contingency table data to build saturation model, uses step function to realize stepwise re
- 使用卷积对数据进行平滑处理
- R language uses ggplot2 to visualize the results of Poisson regression model and count results under different parameter combinations
- Optimizing for vectorization
- Shell script multi process concurrent writing method example (high level cultivation)
- [tcapulusdb knowledge base] tcapulusdb doc acceptance - transaction execution introduction
- [CEPH] cephfs internal implementation (IV): how is MDS started-- Undigested
- 1. accounting basis -- several major elements of accounting (general accounting theory, accounting subjects and accounts)
猜你喜欢
![[tcapulusdb knowledge base] tcapulusdb doc acceptance - create business introduction](/img/05/8ec56393cac534cb5a00c10a1a9f32.png)
[tcapulusdb knowledge base] tcapulusdb doc acceptance - create business introduction

Redis-集群

How to load the contour CAD drawing of the engineering coordinate system obtained by the designer into the new earth
Advanced operation of MySQL database basic SQL statement tutorial

【TcaplusDB知识库】TcaplusDB运维单据介绍

IDEA本地代理后,无法下载插件

程序分析与优化 - 8 寄存器分配
MySQL数据库基本SQL语句教程之高级操作

1. accounting basis -- several major elements of accounting (general accounting theory, accounting subjects and accounts)
![[tcapulusdb knowledge base] Introduction to tcapulusdb data structure](/img/64/4d7ec393d8469cdadc89078a8cf4b1.png)
[tcapulusdb knowledge base] Introduction to tcapulusdb data structure
随机推荐
【TcaplusDB知识库】TcaplusDB运维单据介绍
程序分析与优化 - 8 寄存器分配
Unity unitywebrequest download package
一篇博客彻底掌握:粒子滤波 particle filter (PF) 的理论及实践(matlab版)
shell脚本多进程并发写法实例(高阶修炼)
The tablestack function of the epidisplay package of R language makes a statistical summary table (descriptive statistics of groups, hypothesis test, etc.), does not set the by parameter to calculate
Sikuli 基于图形识别的自动化测试技术
There are so many vulnerabilities in tcp/ip protocol?
【ceph】CEPHFS 内部实现(一):概念篇--未消化
5张图诠释了容器网络
功能:crypto-js加密解密
Pytorch深度学习代码技巧
Analysis of ble packet capturing debugging information
Principle of TCP reset attack
R language uses GLM function to build Poisson logarithm linear regression model, processes three-dimensional contingency table data to build saturation model, uses step function to realize stepwise re
RestCloud ETL解决shell脚本参数化
【ceph】mkdir|mksnap流程源码分析|锁状态切换实例
北京房山区专精特新小巨人企业认定条件,补贴50万
Using restcloud ETL shell component to schedule dataX offline tasks
[async/await] - the final solution of asynchronous programming