arm体系下的cpu_relax()函数疑惑
本文是原「蜗窝讨论区」的历史存档(2016-05-18),来自版块「Linux kernel技术问答」,共 4 帖。讨论区已停止服务,此处仅供查阅。
在看代码过程中经常有遇到cpu_relax()函数,记得是cpu 忙等的语句,不过今天查看了下,发现kernel/arch/arm/include/asm下面的process.h文件对其的定义如下:
if LINUX_ARM_ARCH == 6 || defined(CONFIG_ARM_ERRATA_754327)
define cpu_relax( smp_mb(
else
define cpu_relax( barrier(
endif
定义除了arm 6和ERRATA_754327外 cpu_relax(定义为barrier(函数,而barrier(应该是避免gcc 对code的优化,保证每次去内存读取对应的值,这和忙等待有啥关系呢?比较疑惑,不知我的理解是否正确呢?
的确,cpu_relax是用于busy loop的场景,代码示例如下: flag = 0; while (flag == 0) { cpu_relax(; } 程序逻辑很奇怪,当然它实际上是期待其他的thread会修改flag这个全局变量,从而解除本cpu的忙等待状态。 cpu_relax必须具备两个功能: 1、确保对flag的访问每次都从memory中加载,也就是barrier(函数的作用 2、通知底层CPU,目前代码没有在做什么实际有意义的事情,如果可以的话,别让cpu做太多事情,系统的资源尽量让给其他的cpu。 当然,由于ARMv8之前的CPU不支持上面的第二个功能,因此你看到的cpu_relax(就是barrier(。如果有兴趣看看ARM64的代码,你会有新的发现: static inline void cpu_relax(void) { asm volatile("yield" ::: "memory"); }
在这里, 嵌入式汇编中的clobber list没有描述汇编代码对寄存器的修改情况,只是有一个memory的标记。我们知道,clober list是gcc和gas的接口,用于gas通知gcc它对寄存器和memory的修改情况。因此,这里的memory就是告知gcc,在汇编代码中,我修改了memory中的内容,cpu_relax之前的c代码块和cpu_relax之后的c代码块看到的memory是不一样的,对memory的访问不能依赖于嵌入式汇编之前的c代码块中寄存器的内容,需要重新加载,这也就是Optimization barrier的功能。 而 嵌入式汇编中的yield指令则完成了cpu_relax的第二个功能,即让CPU 松弛下来,降低功耗,把资源配置给其他thread等。
非常感谢linuxer的精辟答复哈!
linuxer的解释很nice
感谢。
