CPU C-state & cpuidle driver[通俗易懂]
1. 什么是C-states、C-mode?
為了在CPU空閑時節約能源,可以命令CPU進入低功耗模式。C-state是intel CPU處于空閑時的一種狀態,CPU有幾種電源模式,它們統稱為“c狀態”或“c模式”
低功耗模式最初是在486DX4處理器中引入的。到目前為止,已經引入了更多的功耗模式,并且對每種模式進行了增強,以使CPU在這些低功耗模式下消耗更少的功率。
CPU的每個狀態都使用不同的電量,并且對應用程序性能的影響也不同。
每當CPU內核處于空閑狀態時,內置的節能邏輯就會啟動,并嘗試將內核從當前的C狀態轉換為更高的C狀態,從而關閉各種處理器組件以節省功耗。
但是你還需要了解,每次應用程序嘗試在一個CPU來執行某些任務時,相應的CPU必須從其“更深的睡眠狀態”返回到“運行狀態”,這需要更多時間來喚醒計算機。 CPU并再次100%啟動并運行。這個過程還必須在原子環境中完成,以便在啟動CP U核心時沒有任何人嘗試使用cpu核心。
因此,CPU過渡到的各種C模式稱為C-state。
它們通常從C0開始,但C0比較特殊,它正常的CPU工作模式,即CPU已100%的開啟。
隨著C-state級別的增加,CPU睡眠模式會更深,即,更多的電路和信號將被關閉,并且CPU需要更多的時間才能返回到C0模式(即喚醒)。
每種模式也有一個名稱,其中每個c-state具有不同的省電策略,有些c級別還有不同的子模式。
下面鏈接有各級別cstate說明,intel手冊上也有詳細描述。
https://access.redhat.com/solutions/202743
mode Name What id does CPUs
C0 Operating State CPU fully turned on All CPUs
C1 Halt Stops CPU main internal clocks via software; bus interface unit and APIC are kept running at full speed 486DX4 and above
C1E Enhanced Halt Stops CPU main internal clocks via software and reduces CPU voltage; bus interface unit and APIC are kept running at full speed All socket 775 CPUs
C1E -- Stops all CPU internal clocks Turion 64, 65-nm Athlon X2 and Phenom CPUs
C2 Stop Grant Stops CPU main internal clocks via hardware; bus interface unit and APIC are kept running at full speed 486DX4 and above
C2 Stop Clock Stops CPU internal and external clocks via hardware Only 486DX4, Pentium, Pentium MMX, K5, K6, K6-2, K6-III
C2E Extended Stop Grant Stops CPU main internal clocks via hardware and reduces CPU voltage; bus interface unit and APIC are kept running at full speed Core 2 Duo and above (Intel only)
C3 Sleep Stops all CPU internal clocks Pentium II, Athlon and above, but not on Core 2 Duo E4000 and E6000
C3 Deep Sleep Stops all CPU internal and external clocks Pentium II and above, but not on Core 2 Duo E4000 and E6000; Turion 64
C3 AltVID Stops all CPU internal clocks and reduces CPU voltage AMD Turion 64
C4 Deeper Sleep Reduces CPU voltage Pentium M and above, but not on Core 2 Duo E4000 and E6000 series; AMD Turion 64
C4E/C5 Enhanced Deeper Sleep Reduces CPU voltage even more and turns off the memory cache Core Solo, Core Duo and 45-nm mobile Core 2 Duo only
C6 Deep Power Down Reduces the CPU internal voltage to any value, including 0 V 45-nm mobile Core 2 Duo only
C7 Deep Energy Saving The CPU tries to flush its L3 cache. If the L3 cache is able to be entirely cleared, the CPU cuts its power to save energy. The power from the system agent is removed too. —
C7s — When an MWAIT(C7) command is issued with a C7s sub-state hint, the entire L3 cache is flushed in one step as opposed to flushing the L3 cache in multiple steps. This also allows the system to send I/O devices to low power mode to reduce unnecessary power consumption when the system idles down. —
C8 — The L3 cache is flushed in a single step. The power to the PLL is cut. —
C9 — The VCCIN (VCC Input Voltage) gets lowered to a minimum. —
C10 — The single phase core management system, VR12.6, goes into a low-power state. The CPU is almost shut down. —
Jetbrains全家桶1年46,售后保障穩定
2. 如何禁用處理器睡眠狀態?
對延遲敏感的應用程序不希望處理器進入到更深的C狀態,因為從C狀態返回到C0會引起延遲。這些延遲的范圍可以從數百微秒到毫秒。
有幾種方法可以實現此目的。
方法1
通過使用內核命令行參數processor.max_cstate = 0、intel_idle.max_cstate=0進行引導,讓系統不進入深度的C狀態??梢栽趃rub2文件中添加這些變量。
**為什么要設置2個參數,分別代表什么? **
具體作用:
1)intel_idle.max_cstate=0
在intel平臺上,模式會使用intel cpuidle drviver,intel_idle.max_cstate=0 意味著禁用intel cpuidle driver,讓其退化使用acpi driver。
2)processor.max_cstate=0
processor.max_cstate=0用描述acpi driver中cpu cstate的最大級別,但是實際max_cstate=0并不能真的讓CPU保持在C0態,只能讓CPU保持在C1狀態。如下代碼:
Note that intel_idle.max_state = 0 disables intel_idle and lets acpi_idle (processor.max_state) take over.
vim /usr/src/debug/kernel-3.10.0-693.19.1.el7/linux-3.10.0-693.19.1.el7.x86_64/drivers/idle/intel_idle.c
(... set number ...)
889 /*
890 * intel_idle_probe()
891 */
892 static int __init intel_idle_probe(void)
893 {
894 unsigned int eax, ebx, ecx;
895 const struct x86_cpu_id *id;
896
897 if (max_cstate == 0) {
898 pr_debug(PREFIX "disabled\n");
899 return -EPERM;
900 }
(...)
looking at the acpi processor_idle code:
Raw
vim /usr/src/debug/kernel-3.10.0-693.19.1.el7/linux-3.10.0-693.19.1.el7.x86_64/drivers/acpi/processor_idle.c
(... set number ...)
915 if (max_cstate == 0)
916 max_cstate = 1;
(...)
the acpi_idle driver doesn’t allow locking to C0, i.e. the effect of the following two boot command lines is the same:
processor.max_cstate=0 intel_idle.max_cstate=0
processor.max_cstate=1 intel_idle.max_cstate=0 (與上面等效)
方法2
第二種方法是使用電源管理服務質量接口(PM QOS)。
文件**/dev/cpu_dma_latency**是一個字符設備,當打開該接口時,它會注冊一個服務質量請求以請求操作系統的延遲。
程序應打開**/dev/cpu_dma_latency**,向其寫入一個32位數字,該數字表示最大響應時間(以微秒為單位),寫入零表示您想要最快的響應時間。
通常cpu_dma_latency被系統tuned服務使用,通過配置文件來修改cpu _dma_latency的值,下面是是tuned配置文件中的一部分:
[cpu]
force_latency = 1
通常只要調整latency后,cpu_dma_latency文件處于激活狀態,該文件描述符將始終處于打開狀態。
[root@localhost ~]# lsof | grep cpu_dma
tuned 1709 root 11w CHR 10,61 0t0 2219 /dev/cpu_dma_latency
tuned的配置文件將force_latency寫為1,作用:以確保CPU C-state不會進入除C1之外的更深的C狀態。
3. 如何讀取及解釋/dev/cpu_dma_latency?
我們可以使用hexdump工具來讀此文件,如下示例:
[root@localhost ~]# cat /dev/cpu_dma_latency
?5w[root@localhost ~]#
[root@localhost ~]# hexdump /dev/cpu_dma_latency
0000000 9400 7735
0000004
[root@localhost ~]# echo $((0x77359400))
2000000000
[root@localhost ~]#
cpu_dma_latency反饋的數值,表示當前等待時間值為2000秒,這是CPU從較深的C狀態變為C0所需或需要的時間。
在RedHat 7上,默認設置為2000秒。我們使用force_latency = 1設置調整cpu_dma_latency時,可以用tuned-adm命令來調整。
例如,我們設置tuned模式為latency-performance:
[root@localhost ~]# tuned-adm active
Current active profile: balanced
[root@localhost ~]# tuned-adm list
Available profiles:
- balanced
- desktop
- latency-performance
- network-latency
- network-throughput
- powersave
- test
- throughput-performance
- virtual-guest
- virtual-host
Current active profile: balanced
[root@localhost ~]#
[root@localhost ~]# head /lib/tuned/latency-performance/tuned.conf | grep latency -a1
[cpu]
force_latency=1
governor=performance
[root@localhost ~]#
[root@localhost ~]# tuned-adm profile latency-performance
[root@localhost ~]#
[root@localhost ~]# hexdump /dev/cpu_dma_latency
0000000 0001 0000
0000004
[root@localhost ~]#
可以看到,等待時間值已更改為1微秒。
由于cpu_dma_latency(PM qos)會影響到cpuidle driver的governor的處理邏輯,所以會影響不同C-state的進入。
cpuidle有2種governor:ladder和menu,分別代表按順序計入C-state,和選擇進入。
cpu在進入idle時,會通過cpuidle governor的策略選擇合適的C-state進入。
static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
struct menu_device *data = &__get_cpu_var(menu_devices);
int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); //讀取cpu_dma_latency
int i;
int multiplier;
struct timespec t;
if (data->needs_update) {
menu_update(drv, dev);
data->needs_update = 0;
}
data->exit_us = 0;
/* Special case when user has set very strict latency requirement */
if (unlikely(latency_req == 0))
return 0;
/* determine the expected residency time, round up */
t = ktime_to_timespec(tick_nohz_get_sleep_length());
data->expected_us =
t.tv_sec * USEC_PER_SEC + t.tv_nsec / NSEC_PER_USEC;
data->bucket = which_bucket(data->expected_us);
multiplier = performance_multiplier();
/*
* if the correction factor is 0 (eg first time init or cpu hotplug
* etc), we actually want to start out with a unity factor.
*/
if (data->correction_factor[data->bucket] == 0)
data->correction_factor[data->bucket] = RESOLUTION * DECAY;
/* Make sure to round up for half microseconds */
data->predicted_us = div_round64(data->expected_us * data->correction_factor[data->bucket],
RESOLUTION * DECAY);
get_typical_interval(data);
if (CPUIDLE_DRIVER_STATE_START > 0) {
data->last_state_idx = CPUIDLE_DRIVER_STATE_START - 1;
/*
* We want to default to C1 (hlt), not to busy polling
* unless the timer is happening really really soon.
*/
if (data->expected_us > 5 &&
!drv->states[CPUIDLE_DRIVER_STATE_START].disabled &&
dev->states_usage[CPUIDLE_DRIVER_STATE_START].disable == 0)
data->last_state_idx = CPUIDLE_DRIVER_STATE_START;
} else {
data->last_state_idx = CPUIDLE_DRIVER_STATE_START;
}
/*
* Find the idle state with the lowest power while satisfying
* our constraints.
*/
for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
struct cpuidle_state *s = &drv->states[i];
struct cpuidle_state_usage *su = &dev->states_usage[i];
if (s->disabled || su->disable)
continue;
if (s->target_residency > data->predicted_us)
continue;
if (s->exit_latency > latency_req) //與cpu_dma_latency對比,小于才會選下一個C-state
continue;
if (s->exit_latency * multiplier > data->predicted_us)
continue;
data->last_state_idx = i;
data->exit_us = s->exit_latency;
}
return data->last_state_idx;
}
4. CPU允許的最大C-state是多少?
intel CPU一般會有多個CPU c-state,但實際也要根據cmdline的中提供的max_cstate設定值,具體來說不同型號的處理器所允許的最大c狀態會有所不同。
比如:在我的筆記本上查看,共有9個C-state級別,移動設備會更注重功耗。
root@ThinkPad-T450:/work/kernel # cat /sys/module/intel_idle/parameters/max_cstate
9
5. 如何檢查不同C-state的喚醒延遲值?
延遲時間值可能會根據各種C-state以及從更深的C-state到C0的過渡時間而變化。
sysfs中查看CPU每個C-state的exit_latency延遲值: (exit_latency由驅動指定)
root@ThinkPad-T450:/sys/devices/system/cpu/cpu2/cpuidle # for state in state{0..8};do echo c-$state `cat $state/name` `cat $state/latency`;done
c-state0 POLL 0
c-state1 C1 2
c-state2 C1E 10
c-state3 C3 40
c-state4 C6 133
c-state5 C7s 166
c-state6 C8 300
c-state7 C9 600
c-state8 C10 2600
root@ThinkPad-T450:/sys/devices/system/cpu/cpu2/cpuidle #
6. 如何檢查和監視Linux中每個CPU和內核的CPU c狀態使用情況?
intel平臺可以使用turbostat工具,該工具
可以查所有可用CPU核心的c-state使用量及占用百分比。
root@ThinkPad-T450:/work/kernel # turbostat -q
^CCore CPU Avg_MHz Busy% Bzy_MHz TSC_MHz IRQ SMI POLL C1 C1E C3 C6 C7s C8 C9 C10 POLL% C1% C1E% C3% C6% C7s% C8% C9% C10% CPU%c1 CPU%c3 CPU%c6 CPU%c7 CoreTmp PkgTmp Pkg%pc2 Pkg%pc8 Pkg%pc9 Pk%pc10 PkgWatt CorWatt GFXWatt
- - 267 18.28 1463 2194 10021 0 23 220 1256 3391 1215 3465 1724 875 25 0.00 0.17 1.24 10.16 6.61 25.43 20.23 17.59 0.32 15.69 13.53 8.55 43.94 46 47 0.00 0.00 0.00 0.00 4.20 1.58 0.33
0 0 249 18.09 1377 2195 2460 0 7 45 305 849 295 817 451 222 0 0.00 0.14 1.20 9.88 6.54 24.14 21.03 19.08 0.00 13.70 14.46 8.62 45.13 46 47 0.00 0.00 0.00 0.00 4.20 1.58 0.33
0 1 215 15.70 1374 2194 2573 0 2 57 372 933 317 895 437 230 5 0.00 0.21 1.31 11.59 6.57 25.82 19.51 18.87 0.53 16.08
1 2 291 18.92 1543 2194 2678 0 8 67 337 866 318 940 408 185 11 0.00 0.27 1.58 10.29 6.77 27.23 20.14 14.34 0.45 17.24 12.60 8.47 42.76 46
1 3 312 20.43 1533 2195 2310 0 6 51 242 743 285 813 428 238 9 0.00 0.06 0.88 8.90 6.57 24.54 20.22 18.06 0.31 15.75
root@ThinkPad-T450:/work/kernel #
在設置max_cstate=0的情況下,CPU最深的C-state為C1。
[root@localhost ~]# cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-3.10.0-693.11.1.el7.es.10.x86_64 root=/dev/mapper/os-root ro console=ttyS0,9600 console=tty0 rootdelay=90 nomodeset
crashkernel=auto rd.lvm.lv=os/root rd.lvm.lv=os/swap biosdevname=1 net.ifnames=1 rhgb quiet LANG=en_US.UTF-8
processor.max_cstate=0 intel_idle.max_cstate=0
[root@localhost ~]#
[root@localhost ~]# turbostat
Core CPU Avg_MHz Busy% Bzy_MHz TSC_MHz IRQ SMI CPU%c1 CPU%c3 CPU%c6 CoreTmp Pkg%pc3 Pkg%pc6
- - 8 0.52 1600 2394 2219 176 99.48 0.00 0.00 34 0.00 0.00
0 1 1 0.08 1600 2394 300 11 99.92 0.00 0.00 32 0.00 0.00
0 9 0 0.03 1600 2394 24 11 99.97
1 3 1 0.06 1600 2394 73 11 99.94 0.00 0.00 29
1 11 117 7.33 1600 2394 411 11 92.67
9 5 1 0.07 1600 2394 89 11 99.93 0.00 0.00 26
9 13 1 0.04 1600 2394 47 11 99.96
10 7 1 0.05 1600 2394 61 11 99.95 0.00 0.00 28
10 15 1 0.05 1600 2394 39 11 99.95
0 0 1 0.09 1600 2394 264 11 99.91 0.00 0.00 29 0.00 0.00
0 8 0 0.03 1600 2394 46 11 99.97
1 2 1 0.08 1600 2394 177 11 99.92 0.00 0.00 32
1 10 1 0.08 1600 2394 59 11 99.92
9 4 2 0.12 1600 2394 237 11 99.88 0.00 0.00 33
9 12 1 0.04 1600 2394 27 11 99.96
10 6 2 0.10 1600 2394 287 11 99.90 0.00 0.00 34
10 14 2 0.13 1600 2394 78 11 99.87
^C
[root@localhost ~]#
7. 什么叫POLL idle狀態?
前面看到的結果,都不能讓CPU完全不進入C-state,因為cpu idle enter代碼進入了C1狀態,cmdlind添加idle=poll參數可以讓CPU完全處于C0狀態,當CPU空閑時其實是執行busy-loop,但是TOP并看不出來。
POLL idle狀態不是真正的空閑狀態,它不節省任何功率。取而代之的是,執行busy-waiting。如果足夠了解你的應用程序,對延遲很敏感,讓內核知道必須盡快處理工作,因為進入任何實際的硬件空閑狀態可能會導致輕微的性能損失,則可以使用此狀態。
X86體系結構平臺上存在兩種不同的cpuidle驅動程序:
“ acpi_idle” cpuidle驅動程序
acpi_idle cpuidle驅動程序從ACPI BIOS表(從最新平臺上的_CST ACPI函數或從較舊平臺上的FADT BIOS表)檢索可用的睡眠狀態(C狀態)。不會從ACPI表中檢索C1狀態。如果進入C1狀態,內核將調用hlt指令(或Intel上的mwait)。
“ intel_idle” cpuidle驅動程序
在內核2.6.36中引入了intel_idle驅動程序。它僅服務于最近的Intel CPU(Nehalem,Westmere,Sandybridge,Atoms或更高版本)。在較舊的Intel CPU上,仍使用acpi_idle驅動程序(如果BIOS提供C狀態ACPI表)。intel_idle驅動程序知道處理器的睡眠狀態功能,并忽略ACPI BIOS導出的處理器睡眠狀態表。
8. 為什么操作系統可能會忽略BIOS設置?
https://access.redhat.com/solutions/202743
紅帽鏈接中表示,操作系統可能會基于正在使用的cpidle驅動程序忽略BIOS設置,這刷新了我之前對BIOS C-state設定的認知。
針對不同的硬件廠商,BIOS中的實現也不一樣,有些服務器BIOS中沒有關閉C-state選項,我曾經在一臺dell服務器上進入BIOS設置,并沒有發現有選項可以關閉C-state,或C-state的相關配置。
如果使用intel_idle(intel計算機上的默認設置),則OS可以忽略ACPI和BIOS設置,即驅動程序可以重新啟用C狀態。
如果禁用intel_idle并使用較舊的acpi_idle驅動程序,則操作系統應遵循BIOS設置。
可以通過以下方式禁用intel_idle驅動程序:
將intel_idle.max_cstate = 0傳遞到內核命令行或傳遞idle = xxx (其中*可以例如是poll,即idle = poll)
目前來看,關于C-state,使用OS來控制C-state的進出是最穩妥的做法。
比如:添加“idle=poll”內核參數,可以讓CPU完全不進入C-state。
9. 如何查看當前加載的驅動程序?
intel_idle驅動程序是支持現代Intel處理器的CPU idle驅動程序。
intel_idle驅動程序為內核提供目標駐留時間和每個受支持的英特爾處理器的退出延遲時間。
cpu_idle memu governor用此數據來預測CPU空閑多長時間
root@ThinkPad-T450:/work/kernel # cat /sys/devices/system/cpu/cpuidle/current_driver
intel_idle
root@ThinkPad-T450:/work/kernel # cat /sys/devices/system/cpu/cpuidle/current_governor_ro
menu
root@ThinkPad-T450:/work/kernel #
總結
以上是生活随笔為你收集整理的CPU C-state & cpuidle driver[通俗易懂]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TypeScript里的类型为any和泛
- 下一篇: SAP OData请求是如何通过ODat