内核版本:6.10
源码
#define READ_ONCE(x) \
({ \
compiletime_assert_rwonce_type(x); \
__READ_ONCE(x); \
})
compiletime_assert_rwonce_type
compiletime_assert_rwonce_type的定义
#define compiletime_assert_rwonce_type(t) \
compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long), \
"Unsupported access size for {READ,WRITE}_ONCE().")
对t进行检查,有问题就停止编译
__native_word
#define __native_word(t) \
(sizeof(t) == sizeof(char) || sizeof(t) == sizeof(short) || \
sizeof(t) == sizeof(int) || sizeof(t) == sizeof(long))
就是对t的类型进行检查
compiletime_assert
源码
#define compiletime_assert(condition, msg) \
_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
就是对_compiletime_assert的封装,__COUNTER__是编译器宏,调用一次 加一
demo
#include<stdio.h>
int main(){
printf("%d %d\n", __COUNTER__,__COUNTER__);
return 0;
}
执行结果
pipishuo@pipishuo-OMEN-by-HP-Laptop-16-b0xxx:~/Desktop/knowledge/technology/linuxCode/READ_ONCE$ make
0 1
_compiletime_assert
定义
#define _compiletime_assert(condition, msg, prefix, suffix) \
__compiletime_assert(condition, msg, prefix, suffix)
调用的__compiletime_assert
__compiletime_assert 定义
# define __compiletime_assert(condition, msg, prefix, suffix) \
do { \
/* \
* __noreturn is needed to give the compiler enough \
* information to avoid certain possibly-uninitialized \
* warnings (regardless of the build failing). \
*/ \
__noreturn extern void prefix ## suffix(void) \
__compiletime_error(msg); \
if (!(condition)) \
prefix ## suffix(); \
} while (0)
其中__compiletime_error
# define __compiletime_error(msg) __attribute__((__error__(msg)))
用个demo解释吧
#include<stdio.h>
# define __compiletime_error(msg) __attribute__((__error__(msg)))
#define __noreturn __attribute__((__noreturn__))
# define __compiletime_assert(condition, msg, prefix, suffix) \
do { \
/* \
* __noreturn is needed to give the compiler enough \
* information to avoid certain possibly-uninitialized \
* warnings (regardless of the build failing). \
*/ \
__noreturn extern void prefix ## suffix(void) \
__compiletime_error(msg); \
if (!(condition)) \
prefix ## suffix(); \
} while (0)
#define _compiletime_assert(condition, msg, prefix, suffix) \
__compiletime_assert(condition, msg, prefix, suffix)
#define compiletime_assert(condition, msg) \
_compiletime_assert(condition, msg, dss, __COUNTER__)
int main(){
compiletime_assert(0,"here!!!!!! look");
return 0;
}
把这些乱七八糟的宏展开后代码长这样(用 gcc -E main.c),就可以把宏展开
demo
int main(){
do { __attribute__((__noreturn__)) extern void __compiletime_assert_0(void) __attribute__((__error__("here!!!!!! look")));
if (!(0)) __compiletime_assert_0();
} while (0);
return 0;
}
__attribute__是个gcc的特性,可对函数进行修饰,调用时会有而外的反应,像这个例子,就是先声明了一个函数
extern void __compiletime_assert_0(void)
然后加了两个__attribute__:
1 attribute((noreturn))
2 attribute((error(“here!!! look”)))
就变成上面那形状了。
error__我理解就是输出某种格式的错误信息,并标记这个函数,如果用调用的地方 就停止编译,因此__attribute((error(“here!!! look”)))的作用就是告诉gcc检测到有的地方要调用__compiletime_assert_0函数,就停止编译输出错误信息。
运行结果如下
pipishuo@pipishuo-OMEN-by-HP-Laptop-16-b0xxx:~/Desktop/knowledge/technology/linuxCode/READ_ONCE$ make
gcc -std=c11 main.c
main.c: In function ‘main’:
main.c:19:45: error: call to ‘__compiletime_assert_0’ declared with attribute error: here!!!!!! look
19 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
看 编译时就输出咱们代码写的信息,并停止了编译
__READ_ONCE(x);
#define __READ_ONCE(x) (*(const volatile __unqual_scalar_typeof(x) *)&(x))
__unqual_scalar_typeof
代码如下
#define __scalar_type_to_expr_cases(type) \
unsigned type: (unsigned type)0, \
signed type: (signed type)0
#define __unqual_scalar_typeof(x) typeof( \
_Generic((x), \
char: (char)0, \
__scalar_type_to_expr_cases(char), \
__scalar_type_to_expr_cases(short), \
__scalar_type_to_expr_cases(int), \
__scalar_type_to_expr_cases(long), \
__scalar_type_to_expr_cases(long long), \
default: (x)))
关于_Generic 这篇博客讲的很好:https://blog.csdn.net/qq_31243065/article/details/80904613
简单来说就是根据x的类型输出不同的代码
比如 x的类型是signed int,_Generic()就变成(signed int)0,再根据typeof 就变成了signed int,感觉多此一举呢
所以__READ_ONCE(x)
的作用就是把x变量换个类型,然后取他的值,比如 int x
__READ_ONCE(x)
翻译过来就是(*(const volatile int *)&(x))
关于volatile,参考链接:https://www.cnblogs.com/Goforyouqp/p/17606953.html
总结下来就是每次读这个变量,都去内存去读,而不是从缓存中读。
总结
先检查类型,然后转了一下类型然后取他的值,如果是看kernel代码,就基本没用,如果是写,还是很有用的,保证数据不混乱