首页 > 其他分享 >READ_ONCE/WRITE_ONCE/ACCESS_ONCE和smp_store_release/smp_load_acquire作用

READ_ONCE/WRITE_ONCE/ACCESS_ONCE和smp_store_release/smp_load_acquire作用

时间:2024-09-26 12:55:54浏览次数:9  
标签:load __ val smp volatile ONCE size

READ_ONCE,WRITE_ONCE和 ACCESS_ONCE 宏在linux内核中出现的频率极高。那么这三个宏到底起了什么样的作用呢?

smp_store_release/smp_load_acquire又如何呢?

1、宏定义

我们先看下READ_ONCE和WRITE_ONCE的宏定义:

#define READ_ONCE(x) \
	({ union { typeof(x) __val; char __c[1]; } __u; __read_once_size(&(x), __u.__, sizeof(x)); __u.__val; })


#define WRITE_ONCE(x, val) \
	({ typeof(x) __val = (val); __write_once_size(&(x), &__val, sizeof(__val)); __val; })

内部涉及到__read_once_size和__write_once_size两个函数,那我们再看下这两个函数实现:

/*p为待读取数据地址,res保存读取结果,size为读取数据长度*/
static __always_inline void __read_once_size(const volatile void *p, void *res, int size)
{
	switch (size) {
	case 1: *(__u8 *)res = *(volatile __u8 *)p; break;
	case 2: *(__u16 *)res = *(volatile __u16 *)p; break;
	case 4: *(__u32 *)res = *(volatile __u32 *)p; break;
#ifdef CONFIG_64BIT
	case 8: *(__u64 *)res = *(volatile __u64 *)p; break;
#endif
	default:/*超过系统支持长度*/
		barrier();
		__builtin_memcpy((void *)res, (const void *)p, size);
		data_access_exceeds_word_size(); /*产生警告*/
		barrier();
	}
}

/*p为待写入数据地址,res待写入数据值,size为写入数据长度*/
static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
	switch (size) {
	case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
	case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
	case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
#ifdef CONFIG_64BIT
	case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
#endif
	default: /*超过系统支持长度*/
		barrier();
		__builtin_memcpy((void *)p, (const void *)res, size);
		data_access_exceeds_word_size(); /*产生警告*/
		barrier();
	}
}
static __always_inline void data_access_exceeds_word_size(void)
#ifdef __compiletime_warning
__compiletime_warning("data access exceeds word size and won't be atomic")
#endif
;

__read_once_size和__write_once_size两个函数中对地址p都使用volatile进行了修饰,这是一个编译的优化,避免CPU从cache缓存中取值而是到其所在内存地址中去取值。

所以,READ_ONCE和WRITE_ONCE的含义如下:

/*获取变量x的值,内部实现通过volatile修饰x的地址,避免编译器的优化,从x所在地址重新获取其值*/
#define READ_ONCE(x) \
	({ union { typeof(x) __val; char __c[1]; } __u; __read_once_size(&(x)/*变量x的地址*/, __u.__c/*保存读取结果*/, sizeof(x)); __u.__val; })

/*将val赋值给x,内部将val值赋值给通过volatile修饰x的地址,避免编译器的优化,将val赋值给x的地址*/
#define WRITE_ONCE(x, val) \
	({ typeof(x) __val = (val); __write_once_size(&(x), &__val, sizeof(__val)); __val; })

我们在来看看ACCESS_ONCE的宏定义:

/*返回x的值,内部通过获取volatile修饰的x的地址获取x的值,避免编译器的优化*/
#define ACCESS_ONCE(x) (*__ACCESS_ONCE(x))

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

2、smp_store_release和smp_load_acquire的作用

smp_store_release和smp_load_acquire也是内核中涉及内存屏障出现概率较高的函数,其内部是如何实现的呢?

我们先看下它们定义:

/*将v赋值给p所指向的内存空间*/
#define smp_store_release(p, v)						\
do {									\
	compiletime_assert_atomic_type(*p);				\
	smp_mb();							\
	ACCESS_ONCE(*p) = (v);						\
} while (0)

/*返回p所指向内存空间的值*/
#define smp_load_acquire(p)						\
({									\
	typeof(*p) ___p1 = ACCESS_ONCE(*p);				\
	compiletime_assert_atomic_type(*p);				\
	smp_mb();							\
	___p1;								\
})

其中ACCESS_ONCE 上面已经讲述过,就是避免编译器优化,获取到正确的值。

大家仔细看,在两个函数中smp_mb的位置相对于ACCESS_ONCE是不同的,为什么?


"smp_store_release" 中的smp_mb用来防止barrier之前的store/release跑到barrier之后的store后面。
"smp_load_acquire"  中的smp_mb用来防止barrier之后的store/release跑到barrier之前的load前面。
它们可以算是 "smp_mb" 针对只需“单向”order保证的裁剪版本,被称作 one-way barrier。

标签:load,__,val,smp,volatile,ONCE,size
From: https://blog.csdn.net/qq_30896803/article/details/142552445

相关文章

  • WPF InkCanvas selection mode, save all/selected strokes, load strokes file, sav
    //xaml<Windowx:Class="WpfApp416.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.mi......
  • nload实时监网络流量信息
    nload用于实时监控linux下网络流量信息,是命令行工具,用来监控网络的吞吐量。它使用两个图表数据来对进出站流量进行可视化。一、安装nload[root@localhost~]#yuminstallepel-release.noarch-y[root@localhost~]#yuminstallnload.x86_64-y二、命令nload详解1、命令......
  • COMPSCI 315 Web Server Workload Characterization
    WebServerWorkloadCharacterizationAssignment3and4,COMPSCI315Due:RefertothedeadlineonCanvas;SubmissionviaCanvas1IntroductionInternettrafficmeasurementinvolvescollectingnetworkdatathatcanbeanalyzedforseveralpurposessuchas......
  • 如何使用 pygame.image.load() 加载图像?
    我只是想知道语法。如何使用pygame.image.load()加载图像?举个例子,我想加载一个名为cat.png的图像-并输入这个pygame.image.load('cat.png')那么,图像cat.png应该保存在哪里?当然可以,让我们来分解一下如何使用Pygame加载图像。1.导入和初始化首先,你......
  • SpringBoot 整合 apache fileupload 轻松实现文件上传与下载(通用版)
    我们以Thymeleaf页面模板引擎为例,简单介绍利用apachefileupload工具实现文件上传的功能。2.1、添加相关依赖包首先创建一个基础的SpringBoot项目,并引入相关的依赖包。2.2、添加相关配置参数2.3、文件上传示例对应文件上传的Controller类,示例如下:importorg.apache.commons.fi......
  • SpringBoot 整合 apache fileupload 轻松实现文件上传与下载(通用版)
    我们以Thymeleaf页面模板引擎为例,简单介绍利用apachefileupload工具实现文件上传的功能。2.1、添加相关依赖包首先创建一个基础的SpringBoot项目,并引入相关的依赖包。2.2、添加相关配置参数2.3、文件上传示例对应文件上传的Controller类,示例如下:importorg.apache.commons.fi......
  • OverloadValueOf
    packagecom.shrimpking.t6;/***CreatedbyIntelliJIDEA.**@Author:Shrimpking*@create2024/9/1612:08*/publicclassOverloadValueOf{publicstaticvoidmain(String[]args){bytenumByte=12;shortnumShort=34;......
  • ShowPrintlnOverload
    packagecom.shrimpking.t6;/***CreatedbyIntelliJIDEA.**@Author:Shrimpking*@create2024/9/1612:14*/publicclassShowPrintlnOverload{publicstaticvoidmain(String[]args){System.out.println(123);//整型System......
  • dmloader.dll文件丢失导致程序无法运行问题
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个dmloader.dll文件(挑选合适的版本文件)把它......
  • C++ std::call_once 实现单例模式
    #if1#include<iostream>#include<memory>#include<mutex>usingnamespacestd;classSingleton{public:staticSingleton&getInstance(){std::call_once(m_OnceFlag,&Singleton::init);return*m_Insta......