内存屏障        由于编译器的优化和缓存的使用,导致对内存的写入操作不能及时地反映出来,也就是说当完成对内存的写入操作之后,读取出来的有可能是旧的内容。我们把这种现象称为内存屏障(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中内存屏障相关推荐

  1. Linux内核中内存管理相关配置项的详细解析3

    接前一篇文章:Linux内核中内存管理相关配置项的详细解析2 5. 2:1 compression allocator (zbud) 对应配置变量为:CONFIG_ZBUD. 此项默认为选中(如果前一 ...

  2. Linux内核中内存分配函数

    1.原理说明 Linux内核 中采 用了一种同时适用于32位和64位系统的内 存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表,如图2-1所示.四级页表分别为 ...

  3. Linux操作系统中内存buffer和cache的区别

    我们一开始,先从Free命令说起. free 命令相对于top 提供了更简洁的查看系统内存使用情况: $ free                      total  used   free  s ...

  4. Linux操作系统中内存buffer和cache的区别--从free命令说起(转)

    原文链接:http://os.51cto.com/art/200709/56603.htm 我们一开始,先从Free命令说起. Free free 命令相对于top 提供了更简洁的查看系统内存使用情况 ...

  5. linux内存爆了会怎样,linux系统中内存爆满之后会如何?

    在使用python写程序的时候,发现一个可以无限迭代的迭代器,从而可以直接将系统中的内存占满,那么占满之后会发生什么呢? 1. 创建无限迭代,生成列表,如下: [root@python ~]# pyt ...

  6. linux内核中内存耗尽OOM killer

    当内存严重不足时,页分配器在多次尝试直接回收失败后,就会调用内存耗尽OOM killer,选择杀死进程,释放内存. 先看一段oom 输出的错误 [ 7981.765805] kthreadd invo ...

  7. 内存访问顺序 - part2: 屏障及Linux kernel中屏障的使用

    文章目录 屏障是什么 Linux Kernel 中的屏障 Linux 屏障 API 一般的屏障 强制性屏障 SMP 条件屏障 隐式屏障 其他屏障 屏障的开销 未来的文章 本文翻译自 Memory ac ...

  8. linux用8m内存读取1g日志,检查 Linux 中内存使用状况的 8 条命令

    Linux 并不像 Windows,你常常不会有图形界面可供使用,特别是在服务器环境中.html 做为一名 Linux 管理员,知道如何获取当前可用的和已经使用的资源状况,好比内存.CPU.磁盘等,是 ...

  9. linux+查内存数量,检查 Linux 中内存使用情况的 8 条命令 | Linux 中国

    作为一名 Linux 管理员,知道如何获取当前可用的和已经使用的资源情况,比如内存.CPU.磁盘等,是相当重要的. -- Magesh Maruthamuthu Linux 并不像 Windows,你 ...

  10. linux内存段页,linux内存管理-段式和页式管理

    该博文参考国嵌视频和http://www.cnblogs.com/image-eye/archive/2011/07/13/2105765.html,在此感谢作者. 一.地址类型 物理地址:CPU通过 ...

最新文章

  1. Visual Studio 2017 UTF-8 无 BOM 一站式解决办法
  2. Python 深度学习,你的 Keras 准备好了吗?
  3. (117)FPGA面试题-使用三态缓冲器实现漏极开路缓冲
  4. 提高开发效率的 Eclipse 实用操作
  5. 字符串匹配-kmp算法
  6. 【web】自定义协议Protocol URL
  7. Centos7 kvm环境制作qcow2 格式镜像
  8. Homebrew替换阿里云镜像源
  9. ARM的商业模式和ARM各种版本号区分
  10. 2023阿里云服务器租用价格CPU/内存/带宽/系统盘收费标准
  11. gtbook安装使用教程
  12. python使用tkinter万年历
  13. 附件四:攻击方评分标准.docx
  14. 2020-2021考研南京大学软件学院学习经验分享(英语90,842自命题110+)
  15. waitpid() 做人要厚道
  16. Java小白自学8:循环结构练习题(一)
  17. backtracking及其应用2
  18. 知乎、百度会被ChatGPT取代吗?百度文心一言胜算如何
  19. OpenCV-判断OpenCV摄像头是否断开
  20. Eclipse使用Space键替换Tab键不生效的解决方案

热门文章

  1. Uncaught TypeError: Cannot read property ‘Kb‘ of undefined
  2. 你好,C++(26)如何与函数内部进行数据交换?5.1.3 函数参数的传递
  3. SARscape 处理ALOS数据
  4. GetForegroundWindow获取的是托管进程ApplicationFrameHost,而不是真正的进程,比如XD软件...
  5. .NET MF 4.2 RTM (QFE2)发布
  6. 老年用计算机手写板,什么样的手写板才适合老年朋友使用
  7. 声控楼道路灯电路的设计
  8. 使用MQTTClient.h库进行mqtt通讯【C语言】
  9. 【转】VB中应用DDE
  10. PYRE 人物剧情 和 游戏技巧小结