CPU 就和皮鞋廠的工人一樣, 無可奈何之時也得躺平。歷代CPU的架構師都有一顆仁慈的心——給自己的產品留下了躺平的功能,而且是一代更比一代強。相關的指令有HLT/PAUSE/MWAIT/UMWAT/TPAUSE,其中我最喜愛的是UMWAIT,它的妙處我們后面再說。
當然經理并不會輕易讓CPU工人去摸魚, 必須滿足一定的條件。Linux 內核中那么嚴格說來是由Scheduler 來判斷目前的工作量是否飽滿。如果工作量實在不夠,也只好讓CPU劃劃水, 睜只眼閉只眼, 好歹省點電費不是。雖然在CPU層面摸魚的手段花樣百出, 但是在OS層面只有一個抽象的概念就是IDLE。在這個時候經理表面上放任CPU工人劃水, 其實打的是省電的小九九,這也算是Win-Win的帕累托改進。那么問題就來了, Scheduler 到底是如何判斷當前工作量不飽滿呢?
調度器中idle 觸發條件
Linux Scheduler 為每一個CPU工人都維護了一個RunQueue 可以認為是一個任務列表, 當且僅當這個列表里所有的任務都不是runnable的狀態時, Scheduler 才會切換到idle process。也就是說這個時候CPU工人完全無所事事, 必須休息節省體力以及節省電費! 同時還要注意可以通過nohz/nohz_full啟動參數減少tick 中斷對正在休息的CPU工人的干擾!
身世顯赫
那么首先我們要考慮得是idle進程從何而來,在描述細節之前我先劇透一下, idle進程雖然名字聽起來不怎么樣, 但是出身顯赫。首個idle進程實際上轉化自0號進程!內核成為了生活的真相帝, 躺平并不是人人都能擁有的選項!
具體的過程則是說來話長. 在開辟鴻蒙之初, kernel 所有的進程之始祖是一個靜態的結構體:
struct task_struct init_task
init_task 并非由任何kernel API所創建, 是所有進程的祖先, 名副其實的the one。
它肩負了非常重要的職責, 例如創建首個內核線程kernel_init。從而達到 一生二,二生三,三生萬物 的效果。不過今天這些不是本文的重點, 重點在于大功告成之際, init_task 并未事了拂衣去而是默默的轉化成了idle 進程。它繼續無言的守護著整個系統, 俯首甘當孺子牛啊。
這里參見代碼 init/main.c line 451,這里是函數rest_init 的最后階段大家可以發現調用了 cpu_startup_entry 這個函數。
我們可以跳轉到 kernel/sched/idle.c 來一窺細節. 這里第一個arch_cpu_idle_entry 是可選接口,x86并沒有實現,核心中的核心還是函數 do_idle()-- just 躺平.
如果沒有在啟動時強制idle 模式為poll, 則我們將在這里進入真正的躺平函數 cpuidle_idle_call.
在smp系統中 core0 以外的其它core 也會通過 start_secondary 函數最終產生0號進程且調用cpu_startup_entry 來進入idle loop之中。
CPU各種“躺平”的姿勢
各個廠子出產的CPU工人的idle 姿勢也是慢慢演化的, 從簡入繁, 花樣百出。下面我們一起來浮光掠影的看一下這些"超能力"。
X86
HLT
這是初代的idle 指令, 于486DX時代引入. 首先只有在ring0的特權級別才能執行HLT指令, 同時執 行的效果是 CPU 進入了C1/C1E state(參考ACPI標準)。嚴格說起來只能算是摸魚0.1v。APIC/BUS /CACHE 都是照常運轉, 只要中斷發生, CPU工人立即就要回到產線繼續搬磚。C1E 稍微又優待了CPU點, 停止內部時鐘又降了壓, 比較體貼。
PAUSE
這個也是非常早期的指令(Pentium 4)許可CPU 工人打個盹,大概從幾個到幾十個cycles吧(各代CPU有差異)。為什么要打盹呢?其實主要是要降低CPU工人在特定情況下(spin-lock)給內存控制器帶來的壓力,與其讓CPU工人阻塞了內存控器, 不如讓他打個盹吧。在最近的幾代Xeon之上還附帶了降低功耗的buff。
MWAIT/MONITOR
新一代CPU架構師回顧了前輩的設計, 覺得CPU工人的權力完全沒有得到充分的照顧, 應該給予更進一步的休息機會乃至真正的躺平!而且喚醒的條件又多了一個, 除了中斷這種強喚醒模式以外, 又加了內存的CacheLine Invalidate喚醒。你的鄰居CPU 除了敲門以外還多了拿橡皮筋彈窗戶玻璃的渠道。首先這兩條指令也只能在ring0 級別執行, 首先是調用MONITOR 地址范圍, 其次是MWAIT 進入休眠,一旦該地址的內存被任何其它的主體修改, 則喚醒CPU工人起來繼續搬磚。同時這次最大的改進是可以通過MWAIT 進入各種不同的Cstate。其中C6 是我心目中真正的躺平 CPU 電壓可以歸0同時cache 也停, 實至名歸啊。
最常見的C State 狀態詳細描述,引自[2]
Cstate | Name | Description |
---|---|---|
C0 | Operating State | CPU fully turned on |
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 |
C3 | Deep Sleep | Stops all CPU internal and external clocks |
C6 | Deep Power Down | Reduces the CPU internal voltage to any value, including 0 Volts |
UMWAIT/UMONITOR
MWAIT雖好, 但是奈何必須在ring0特權級下執行, 如果是一些特定的用戶級應用例如DPDK, Linux的 idle driver 是很難得到執行的機會,所以CPU架構師又生憐憫之心, 允許CPU在用戶級也能進入躺平的模式, 不過作為妥協連C1 state都不行,只能進入 C0.1/C0.2 等神秘模式。效果還有待觀察,不過話說回來SPR這代Xeon才開始支持....距離上市少說還得1年之久。
TPAUSE
UMWAIT 指令的升級加強版, 附帶了一個timer。TPAUSE 可以讓CPU工人根據規定好的時間進行休息, 時間一到, 立刻繼續搬磚。當然這也是一個簇新簇新的指令,大家還要等待SPR。
ARM的Idle-state 級別情況比較復雜一些, 更多的是和具體的芯片實現相關. 但是總體上也是把握幾個大的類別:
只是停止CPU內部時鐘
CPU降頻
停止給Cache供電
停止給CPU供電
和X86 相比 Arm的喚醒機制沒有和MESI協議連接有些遺憾(也就是沒有實現通過MEM 地址監控的方式達成喚醒).
YEILD
這條頗為類似 PAUSE基本功能接近,使用場景也接近(spin lock).
WFE/WFI
這兩條指令顧名思義 wait for event/ wait for interrupt,中斷這條大家都可以理解類似HLT,那么event這條就值得看看了。ARM架構可以從一個CPU向所有其它CPU 發送event(sev 指令),我的理解類似IPI廣播,收到了此event的CPU如果處于idle狀態, 則需要立即喚醒。(注:和宋老師討論以后發現 event 和IPI的一個區別是不需要ISR來響應,同時event并不能喚醒由于WFI指令進入的idle,這個有點囧,反過來中斷可以喚醒由于WFE進入的idle。這兩個躺平姿勢水很深啊)
軟件實現
除了硬件的各種花式躺平技術之外還有兩類“偽躺平”技術。
idle polling
通過啟動參數, 我們可以指定cpu的idle 進程并不調用硬件提供的idle功能而僅僅是polling, 這種情況主要用于需要極低的CPU從idle狀態返回時延的場景。那么如果壓根沒有進入實際的idle狀態,當然時延是極低的,同時也能融入到idle整體的框架,不至于破壞規矩開特例。
halt-polling
在打開虛擬化的場景下, 事情就變得更加有趣了。大多數情況下, qemu 會缺省的只對guest 提供HLT指令作為idle的唯一機制,但是 HLT 指令毫無懸念的會觸發VMEXIT。雖然說大多數情況下kvm看到exit reason 是HLT 也只是執行poll而已, 但是VMEXIT/VM_RESUME 還是如此的痛,畢竟幾千個cycles已經無謂流逝, 追求極致的我們怎么能放任資源浪費。于是Redhat在Guest端引入了halt poll 機制, 也就是說如果matrix中的CPU工人首先開始假摸魚(poll), 如果假摸魚時間超過了閾值才真的去觸發HLT指令。如果很快就被從假模魚狀態拉回去搬磚, 則省去了出入matrix的費用(經理得意的笑了)。
最后軟件硬件各路躺平姿勢花樣繁多, 內核無奈又祭出了抽象大法把idle的時長與返回時延的選擇與具體執行idle的機制分離開來。
idle governor 就負責做時長與時延的選擇,也可以稱為 idle -select。
idle driver 則是負責通過我們上面描述的各種軟硬件機制來實現governor指定的目標。同時向governor menu 經理提供各種不同機制的性能參數,以供menu經理選擇,就是所謂的idle-enter。
idle governor 缺省的算法只有一個就是menu, 還有3個候選的ladder/TEO/haltpoll 算法但是一般需要重新編譯內核來激活。
ladder 算法故名意思, 是首先從能耗較高/返回時延較小的狀態開始,當系統idle超過了閾值再進入更深的節能狀態,從而逐步升級節能狀態。俗稱添油戰術也可以美其名曰“快速迭代”。
menu 算法單從名字看則有點讓人摸不到頭腦,其內部機制也確實頗為復雜,menu算法主要是要在節能狀態的停留時間與系統能容忍的返回時延之間做權衡以達到最佳效果。
請原諒我非常不精確地描述一下menu。menu仿佛一個非常敬業的經理凡事都要精算做出最優選擇,CPU工人一旦休息再想打起精神干活這個轉換是有一個代價的, 往往需要口頭鼓勵(畫餅)+物質鼓勵(肉夾饃)。那么經理就要考慮如果工人休息時間太短,休息的好處遠低于讓CPU工人重新振作的代價,那么這個休息就是不合理的(無情啊)。而且休息也有好些種類, 從假休息到完全躺平, 到底哪一種休息狀態才是收益比最佳的? menu會無情的選擇那個休息帶來好處大于重新振作代價的方案。同時menu經理還會受到來自客戶的壓力, 時延也是要滿足的??蛻舻哪托拇蟮稚隙际遣缓玫? menu經理會瘋狂試探客戶的底線。它選擇的方案是滿足客戶耐心上限的情況下CPU工人消耗能耗最少的方案。同時做到以上兩點 menu經理大約才能有希望完成OKR/KPI。
結語
今天我們一起浮光掠影的討論了一下Linux的各種躺平姿勢,從中能領略到一代代CPU架構師對CPU打工人的關愛。最后我衷心的祝愿CPU打工人在層出不窮的各類躺平技術加持下,最終能同各位經理一起實現碳中和的OKR/KPI。
原文標題:漫話Linux之“躺平”: IDLE 子系統
文章出處:【微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。
責任編輯:haq
-
cpu
+關注
關注
68文章
10905瀏覽量
213031 -
Linux
+關注
關注
87文章
11345瀏覽量
210408
原文標題:漫話Linux之“躺平”: IDLE 子系統
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
“碰一下”支付背后的4G技術
支付寶發布新一代AI視覺搜索“探一下”
Linux文件壓縮打包的各種姿勢
歡創播報 支付寶“碰一下”正式發布
![歡創播報 支付寶“碰<b class='flag-5'>一下</b>”正式發布](https://file1.elecfans.com//web2/M00/FC/02/wKgaomaPUkeACiRpAAASJK4J4sA16.webp)
哪個STM8的開發工具最好用,最便捷?
請問Systick配置為多長時間中斷一次合適?
stm32外部中斷不能喚醒stop休眠模式怎么解決?
功率器件 Spice 模型建立
STM32可以使用手機APP,藍牙或者WIFI軟件升級MCU軟件的方法嗎?
深入討論一下模擬式UDP無線中繼技術的缺陷
![深入<b class='flag-5'>討論一下</b>模擬式UDP無線中繼技術的缺陷](https://file1.elecfans.com/web2/M00/C1/A1/wKgaomXYCRqAEchQAAAUDppob2Y556.png)
評論