首页 > 其他分享 >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浏览次数:14  
标签: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

相关文章

  • nload实时监网络流量信息
    nload用于实时监控linux下网络流量信息,是命令行工具,用来监控网络的吞吐量。它使用两个图表数据来对进出站流量进行可视化。一、安装nload[root@localhost~]#yuminstallepel-release.noarch-y[root@localhost~]#yuminstallnload.x86_64-y二、命令nload详解1、命令......
  • 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文件(挑选合适的版本文件)把它......