注明:轉載自 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指令似乎沒有實作…