天天看點

cpu_relax()函數的意義

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

繼續閱讀