ACCESS_ONCE的作用

如果你看过 Linux 内核中的 RCU 的实现,你应该注意到了这个叫做 ACCESS_ONCE() 宏。

ACCESS_ONCE的定义如下:

#define __ACCESS_ONCE(x) ({ \
     __maybe_unused typeof(x) __var = (__force typeof(x)) 0;     (volatile typeof(x) *)&(x); })
#define ACCESS_ONCE(x) (*__ACCESS_ONCE(x))

仅从语法上讲,这似乎毫无意义,先取其地址,在通过指针取其值。而实际上不然,多了一个关键词 volatile,所以它的含义就是强制编译器每次使用 x 都从内存中获取。

原因:

可以通过几个例子看一下。

1. 循环中有每次都要读取的全局变量:

…
static int should_continue;
static void do_something(void);
…
while (should_continue)
do_something();

假设 do_something() 函数中并没有对变量 should_continue 做任何修改,那么,编译器完全有可能把它优化成:

…
if (should_continue)
for (;;)
do_something();

这很好理解,不是吗?对于单线程的程序,这么做完全没问题,可是对于多线程,问题就出来了:如果这个线程在执行do_something() 的期间,另外一个线程改变了 should_continue 的值,那么上面的优化就是完全错误的了!更严重的问题是,编译器根本就没有办法知道这段代码是不是并发的,也就无从决定进行的优化是不是正确的!

这里有两种解决办法:1) 给 should_continue 加锁,毕竟多个进程访问和修改全局变量需要锁是很自然的;2) 禁止编译器做此优化。加锁的方法有些过了,毕竟 should_continue 只是一个布尔,而且退一步讲,就算每次读到的值不是最新的 should_continue 的值也可能是无所谓的,大不了多循环几次,所以禁止编译器做优化是一个更简单也更容易的解决办法。我们使用 ACCESS_ONCE() 来访问 should_continue:

…
while (ACCESS_ONCE(should_continue))
do_something();

2. 指针读取一次,但要dereference多次:

…
p = global_ptr;
if (p && p->s && p->s->func)
p->s->func();

那么编译器也有可能把它编译成:

…
if (global_ptr && global_ptr->s && global_ptr->s->func)
global_ptr->s->func();

你可以谴责编译器有些笨了,但事实上这是C标准允许的。这种情况下,另外的进程做了 global_ptr = NULL; 就会导致后一段代码 segfault,而前一段代码没问题。同上,所以这时候也要用 ACCESS_ONCE():

…
p = ACCESS_ONCE(global_ptr);
if (p && p->s && p->s->func)
p->s->func();

3. watchdog 中的变量:

for (;;) {
still_working = 1;
do_something();
}

假设 do_something() 定义是可见的,而且没有修改 still_working 的值,那么,编译器可能会把它优化成:

still_working = 1;
for (;;) {
do_something();
}

如果其它进程同时执行了:

for (;;) {
still_working = 0;
sleep(10);
if (!still_working)
panic();
}

通过 still_working 变量来检测 wathcdog 是否停止了,并且等待10秒后,它确实停止了,panic()!经过编译器优化后,就算它没有停止也会 panic!!所以也应该加上 ACCESS_ONCE():

for (;;) {
ACCESS_ONCE(still_working) = 1;
do_something();
}

原文地址:https://www.cnblogs.com/chaozhu/p/10084274.html

时间: 12-07

ACCESS_ONCE的作用的相关文章

@jsonignore的作用

作用是json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响. 如下: package com.hzboy.orm; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.pe

配置虚拟域名,hosts文件起作用

快速打开hosts方法 开始->运行->system32->回车 当前路径文件夹drivers\etc\hosts hosts文件起作用,目前楼主知道有两个可能原因 1.刷新dns cmd里面运行 ipconfig /flushdns 2.没配置前访问了一次域名,配置完之后浏览器没重启.所以不起作用 重新启动浏览器或者换一个浏览器

Linux中的环境变量配置文件及其作用

登录相关的配置文件: /etc/profile 范围:对所有用户生效 作用: a.定义USER变量 b.定义LOGNAME变量 c.定义MAIL变量 d.定义PATH变量 e.定义HOSTNAME变量 f.定义HISTSIZE变量 g.定义umask值 i.调用/etc/profile.d/*.sh文件 /etc/profile.d/*.sh 范围:对所有用户生效 说明:这是一族sh文件,没个都会被调用 例如: /etc/profile.d/lang.sh 作用:调用/etc/sysconfg/

表单的作用

①input的作用分为:单选按钮.文本框.密码框.复选按钮.隐藏表单域.文本选择框.图片按钮.普通按钮.提交按钮.重置按钮 ②select的作用:可创建单选或多选菜单,含有<option>标签 在HTML5中的新属性:1.autofocus属性            值autofocus                     规定在页面加载后文本区域自动获得焦点: 2.disabled                        disabled                      

shell中各种括号的作用

小括号里是命令大括号是变量值   一.小括号,圆括号() 1.单小括号 () ①命令组.括号中的命令将会新开一个子shell顺序执行,所以括号中的变量不能够被脚本余下的部分使用.括号中多个命令之间用分号隔开,最后一个命令可以没有分号,各命令和括号之间不必有空格. ②命令替换.等同于`cmd`,shell扫描一遍命令行,发现了$(cmd)结构,便将$(cmd)中的cmd执行一次,得到其标准输出,再将此输出放到原来命令.有些shell不支持,如tcsh. ③用于初始化数组.如:array=(a b

VMware Tools的安装及其作用(redhat5.5为例)

VMware Tools是VMware虚拟机中自带的一种增强工具,相当于VirtualBox中的增强功能(Sun VirtualBox Guest Additions),是VMware提供的增强虚拟显卡和硬盘性能.以及同步虚拟机与主机时钟的驱动程序. 只有在VMware虚拟机中安装好了VMware Tools,才能实现主机与虚拟机之间的文件共享,同时可支持自由拖拽的功能,鼠标也可在虚拟机与主机之前自由移动(不用再按ctrl+alt),且虚拟机屏幕也可实现全屏化. 在vm上安装完redhat系统后

磁盘阵列RAID的功能作用介绍

RAID是一个我们经常能见到的名词.但却因为很少能在实际环境中体验,所以很难对其原理能有很清楚的认识和掌握.RAID是英文Redundant Array of Independent Disks的缩写,中文简称为独立磁盘冗余阵列.RAID就是一种由多块硬盘构成的冗余阵列.虽然RAID包含多块硬盘,但是在操作系统下是作为一个独立的大型存储设备出现.上海天盾数据恢复中心的专家告诉我们,利用RAID技术于存储系统的好处主要有以下三种: 1.通过把多个磁盘组织在一起作为一个逻辑卷提供磁盘跨越功能; 2.

Atitit.获取主板与bios序列号获取硬件设备信息&#160;&#160;Wmi&#160;wmic&#160;的作用

Atitit.获取主板与bios序列号获取硬件设备信息  Wmi wmic 的作用 1 获取硬件核心基础核心基础Wmi1 2 其他资料2 3 Wmic WMI 命令行接口2 4 Atitit.获取主板与bios序列号2 5 参考3 1 获取硬件核心基础核心基础Wmi WMI,是Windows 2K/XP管理系统的核心:对于其他的Win32操作系统,WMI是一个有用的插件.WMI以CIMOM为基础,CIMOM即公共信息模型对象管理器(Common Information Model Object 

PLM在工业4.0中的作用

1 PLM支撑个性化定制 支撑个性化定制是工业4.0的特征之一.在工业4.0时代,个性化定制不像现在买汽车,只有颜色.型号等非常有限的选项,而是涉及到关键零件,如发动机. 选零件本质上已经是一种设计了.用户不是设计师,而设计是个专业性很强的活.要解决这个矛盾,就要用软件来支撑.这个软件就是PLM.PLM提供合适的部件,能保证用户所选的部件能与其他部件一起正常运转,且保证产品质量.从这种意义上说,PLM就像傻瓜相机:自动对焦.自动调光圈,让普通人也能照出近乎专业的照片. 2 PLM支撑智能生产 生

JSP 的内置对象及方法,动作和作用,常用指令

JSP 的内置对象及方法:JSP 共有以下9 种基本内置组件:request:用户端请求,此请求会包含来自GET/POST 请求的参数:response:网页传回用户端的回应:pageContext:网页的属性是在这里管理:session:与请求有关的会话期,可以存贮用户的状态信息:application:servlet 正在执行的内容:out:用来传送回应的输出:config:servlet 的构架部件,用于存取servlet 实例的初始化参数:page:JSP 网页本身:exception: