当前位置: 首页 > 工具软件 > relax > 使用案例 >

cpu_relax()函数的意义

林炫明
2023-12-01

注明:转载自 http://www.wowotech.net/forum/viewtopic.php?id=21
里面很多大牛的干货!!!

最近在看wake_up_process流程的时候,发现在try_to_wake_up函数里面有这么一条语句:

	while (p->on_cpu)
		cpu_relax();

一直想弄明白cpu_relax()函数的真实含义.
我们查看这个函数的定义如下:
ARM32:

#if __LINUX_ARM_ARCH__ == 6 || defined(CONFIG_ARM_ERRATA_754327)
#define cpu_relax()			smp_mb()
#else
#define cpu_relax()			barrier()
#endif

ARM64:

static inline void cpu_relax(void)
{
	asm volatile("yield" ::: "memory");
}

能够看到:

  • ARM32中,在调用cpu_relax()函数的时候,只有内存屏障
  • ARM64中,在调用cpu_relax()函数的时候,不仅仅有内存屏障还存一个yield指令,是让cpu松弛下来,降低功耗,把资源配置给其他thread等.

详细的解释如下:
我们知道cpu_relax()是用于busy loop的场景.比如下面的代码:

	while (p->on_cpu)
		cpu_relax();

p->on_cpu的数值是期待其他进程修改的,从而解除本cpu的忙等待状态.
cpu_relax必须具备两个功能:

</tr>
<tr>
	<td bgcolor=orange>2、通知底层CPU,ARM32的代码没有在做什么实际有意义的事情,如果可以的话,别让cpu做太多事情,系统的资源尽量让给其他的cpu。ARM64目前支持</td>

</tr>
1、确保对p->on_cpu的访问每次都从memory中加载,也就是barrier()函数的作用;

当然,由于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等,即yield指令用来告知硬件系统,本cpu上执行的指令是polling操作,没有那么急迫,如果有任何的资源冲突,本cpu可以让出控制权。

但是看ARM V8 spec,yield指令似乎没有实现…

 类似资料: