注明:转载自 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");
}
能够看到:
详细的解释如下:
我们知道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指令似乎没有实现…