内存屏障 linux,Linux中内存屏障
内存屏障 由于编译器的优化和缓存的使用,导致对内存的写入操作不能及时地反映出来,也就是说当完成对内存的写入操作之后,读取出来的有可能是旧的内容。我们把这种现象称为内存屏障(Memory Barrier) 。
编译器引起的内存屏障
首先让我们来看一个例子,假设有下面这样一段代码:
代码片段2.45 内存屏障示例代码
1 int flag = 0;
2
3 void wait()
4 {
5 while (flag == 0) {
6 sleep(1000);
7 }
8 ......
9 }
10
11 void wakeup()
12 {
13 flag = 1;
14 }
由于编译器的优化,当gcc 发现sleep()函数内部不会修改flag 变量时,它可能把某个寄存器分配给内存变量flag,于是上面的代码编译后可能是这个样子(为了尽量直观易懂,这个例子中采用了C 和汇编代码结合的方式来说明这个问题)。
代码片段2.46 内存屏障示例代码
1
2
3
4
void wait()
{
movl flag, %edx;
5
6
7
8
while (%eax == 0) {
sleep(1000);
}
......
9 }
在这个例子中gcc 为了优化代码,把EDX 分配给内存变量flag,这样可以减少内存访问的次数。假设现在flag为0,线程进入睡眠状态,当它被唤醒时,会再次判断EDX 的值。在这种情况下,就算另外一个线程在某个时候调用了wakeup()把flag设置为1,这个睡眠的线程仍然不能跳出while 循环。由此可见编译器的优化带来了副作用。即便是在单CPU的系统上,也会出现问题。好在我们可以使用volatile 来避免这种情况,因此上面对flag 变量的定义可以修改为:
代码片段2.47 内存屏障示例代码
1 volatile int flag = 0;
这里关键字volatile 的作用是要避免编译器的优化,这样编译器就不会把某个寄存器分配给flag。编译后的代码就是,每一次对flag 的访问都是通过内存访问来进行的,从而避免了这个问题。
另外volatile 常常用于外部设备IO 寄存器访问,考虑下面的例子:
代码片段2.48 内存屏障示例代码
1
2
3
/* 假设0x80 为某一个外部设备寄存器的地址。*/
volatile usigned int *p_status = 0x80;
4
5
6
while (*p_status != ERROR) {
do_something();
}
在这个例子中,由于指针p_status 指向外部设备的某个寄存器,而外部设备随时有可能改变这个寄存器的值,因此也要通过volatile 阻止编译器优化。
通过使用volatile可以避免编译器在优化时把寄存器分配给不必要的内存变量,从而保证对内存的修改立即反映到相关进程。但是在某些情况下,由于涉及的变量比较多,如果把每一个变量声明为volatile 显得很烦琐。因此内核中使用另外一个方式来避免编译器优化引起的副作用。其代码如下:
代码片段2.49 节自include/linux/compiler-gcc.h
1 #define barrier() __asm__ __volatile__("": : :"memory")
这条汇编指令指令部分、输出部分、输入部分都为空,唯独损坏部分为"memory",它告诉gcc 内存已经被修改了。当gcc遇到这条指令时,gcc 会插入必要的指令重新刷新内存和它对应的寄存器。那么这个barrier()如何使用呢?我们来看内核中的一个实际例子。
代码片段2.50 节自kernel/sched.c
static inline void
context_switch(struct rq *rq,
struct task_struct *prev,
struct task_struct *next)
{
struct mm_struct *mm, *oldmm;
prepare_task_switch(rq, prev, next);
mm = next->mm;
oldmm = prev->active_mm;
11 ......
12 /* Here we just switch the register state and the stack. */
13 switch_to(prev, next, prev);
14 barrier();
15 finish_task_switch(this_rq(), prev);
16 }
在内核中context_switch()负责从当前进程切换到另外一个进程环境中,但是由于当前进程需要修改某些内核数据结构,这些修改需要及时地反映到另外一个进程中。因此在这里插入barrier(),这样gcc会采取必要的措施,以保证内存变量和对应的寄存器的一致性。 需要主意的是,这个动作是在编译期发生的。 本文摘自《独辟蹊径品内核:Linux内核源代码导读》
内存屏障 linux,Linux中内存屏障相关推荐
- Linux内核中内存管理相关配置项的详细解析3
接前一篇文章:Linux内核中内存管理相关配置项的详细解析2 5. 2:1 compression allocator (zbud) 对应配置变量为:CONFIG_ZBUD. 此项默认为选中(如果前一 ...
- Linux内核中内存分配函数
1.原理说明 Linux内核 中采 用了一种同时适用于32位和64位系统的内 存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表,如图2-1所示.四级页表分别为 ...
- Linux操作系统中内存buffer和cache的区别
我们一开始,先从Free命令说起. free 命令相对于top 提供了更简洁的查看系统内存使用情况: $ free total used free s ...
- Linux操作系统中内存buffer和cache的区别--从free命令说起(转)
原文链接:http://os.51cto.com/art/200709/56603.htm 我们一开始,先从Free命令说起. Free free 命令相对于top 提供了更简洁的查看系统内存使用情况 ...
- linux内存爆了会怎样,linux系统中内存爆满之后会如何?
在使用python写程序的时候,发现一个可以无限迭代的迭代器,从而可以直接将系统中的内存占满,那么占满之后会发生什么呢? 1. 创建无限迭代,生成列表,如下: [root@python ~]# pyt ...
- linux内核中内存耗尽OOM killer
当内存严重不足时,页分配器在多次尝试直接回收失败后,就会调用内存耗尽OOM killer,选择杀死进程,释放内存. 先看一段oom 输出的错误 [ 7981.765805] kthreadd invo ...
- 内存访问顺序 - part2: 屏障及Linux kernel中屏障的使用
文章目录 屏障是什么 Linux Kernel 中的屏障 Linux 屏障 API 一般的屏障 强制性屏障 SMP 条件屏障 隐式屏障 其他屏障 屏障的开销 未来的文章 本文翻译自 Memory ac ...
- linux用8m内存读取1g日志,检查 Linux 中内存使用状况的 8 条命令
Linux 并不像 Windows,你常常不会有图形界面可供使用,特别是在服务器环境中.html 做为一名 Linux 管理员,知道如何获取当前可用的和已经使用的资源状况,好比内存.CPU.磁盘等,是 ...
- linux+查内存数量,检查 Linux 中内存使用情况的 8 条命令 | Linux 中国
作为一名 Linux 管理员,知道如何获取当前可用的和已经使用的资源情况,比如内存.CPU.磁盘等,是相当重要的. -- Magesh Maruthamuthu Linux 并不像 Windows,你 ...
- linux内存段页,linux内存管理-段式和页式管理
该博文参考国嵌视频和http://www.cnblogs.com/image-eye/archive/2011/07/13/2105765.html,在此感谢作者. 一.地址类型 物理地址:CPU通过 ...
最新文章
- Visual Studio 2017 UTF-8 无 BOM 一站式解决办法
- Python 深度学习,你的 Keras 准备好了吗?
- (117)FPGA面试题-使用三态缓冲器实现漏极开路缓冲
- 提高开发效率的 Eclipse 实用操作
- 字符串匹配-kmp算法
- 【web】自定义协议Protocol URL
- Centos7 kvm环境制作qcow2 格式镜像
- Homebrew替换阿里云镜像源
- ARM的商业模式和ARM各种版本号区分
- 2023阿里云服务器租用价格CPU/内存/带宽/系统盘收费标准
- gtbook安装使用教程
- python使用tkinter万年历
- 附件四:攻击方评分标准.docx
- 2020-2021考研南京大学软件学院学习经验分享(英语90,842自命题110+)
- waitpid() 做人要厚道
- Java小白自学8:循环结构练习题(一)
- backtracking及其应用2
- 知乎、百度会被ChatGPT取代吗?百度文心一言胜算如何
- OpenCV-判断OpenCV摄像头是否断开
- Eclipse使用Space键替换Tab键不生效的解决方案
热门文章
- Uncaught TypeError: Cannot read property ‘Kb‘ of undefined
- 你好,C++(26)如何与函数内部进行数据交换?5.1.3 函数参数的传递
- SARscape 处理ALOS数据
- GetForegroundWindow获取的是托管进程ApplicationFrameHost,而不是真正的进程,比如XD软件...
- .NET MF 4.2 RTM (QFE2)发布
- 老年用计算机手写板,什么样的手写板才适合老年朋友使用
- 声控楼道路灯电路的设计
- 使用MQTTClient.h库进行mqtt通讯【C语言】
- 【转】VB中应用DDE
- PYRE 人物剧情 和 游戏技巧小结