基于共享内存SHM下的内存可见性问题踩坑

Posted 高桐@BILL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于共享内存SHM下的内存可见性问题踩坑相关的知识,希望对你有一定的参考价值。

踩坑类BLOG,往往采用“问题背景+问题现象+问题思考+问题解决”的思路来分享,以针对自己在开发过程中所遇到的各类问题;

一、问题背景

假设有这样一个使用场景,需要多进程之间进行数据通信,在多进程写的Writer端加文件锁,那么由于互斥的关系,在某一刻实际上只有一个进程在对SHM内存进行操作。而对多进程读的Reader端加文件锁,相同地,在同一时刻实际上也只有一个进程对SHM内存进行操作。在不做限制的情况下,Writer端和Reader端以非常高的频率对该SHM内存进行操作。我们假设,Writer端在每次对内存操作完成后,会在SHM中写标记flag_wr,并自增1进行记录;而Reader端在每次堆内存操作完之后,同样地,会在SHM中写标记flag_rd,并自增1进行记录;

二、问题现象

无论是Writer端还是Reader端;我们在每次操作内存的时候都打印一下SHM中flag_wr和flag_rd的值到终端;通过观察终端输出的日志,我们发现;在Writer端和Reader端都在频繁操作SHM内存的时候,Writer端存在打印flag_rd的数据不变的情况,而此时读端的flag_rd是一直自增的;另一方面是在Reader端存在打印flag_wr的数据不变的情况,而此时Writer端的flag_wr也是一直自增的。那么此时我们想到了,内存可见性问题;

三、问题思考

为什么会会有内存可见性问题呢?

我们知道cache高速缓存是cpu的一部分,一般cpu操作内存的数据(读写)会先判断数据是否在cache上有副本,有的话避免操作主内存直接从cache上操作副本。然后再在特定时机将cache的数据同步至主内存。由于Writer端在操作完flag_wr后,Reader端立即去读flag_wr的时候,cpu直接从cache副本取数据,而不是从内存中读取,就会出现内存可见性的问题。也就是说主内存的同一数据在不同的cache上的副本在同一时间存在不一致的可能性。

四、问题解决

在操作的SHM数据结构中对应的flag_wr和flag_rd的定义的地方,通过添加volatile修饰符对两个变量进行修饰。

4.1 volatile修饰符的作用

volatile是一个类型修饰符,其作用是作为指令关键字,确保本条指令不会因为编译器的优化而省略,且要求每次直接读值。简单来说,就是防止编译器对代码进行优化,强制从内存中读取数据,而不是从cache中去取。

以上是关于基于共享内存SHM下的内存可见性问题踩坑的主要内容,如果未能解决你的问题,请参考以下文章

JUC并发编程 共享模型之内存 -- Java 内存模型 & 原子性 & 可见性(可见性问题及解决 & 可见性 VS 原子性 & volatile(易变关键字))(代码

1 Java线程的内存可见性

内存可见性问题分析

到底什么是内存可见性?

细说Java多线程之内存可见性

内存共享