回復(fù)
課間十分鐘第2季 : 本文來自信公眾號開發(fā)內(nèi)功煉 (ID:kfngxl),作者:張彥 allen大家好,我是飛哥負載是查 Linux 服務(wù)器運行狀態(tài)很常用的個性能指。在觀察上服務(wù)器行狀況的候,我們是經(jīng)常把載找出來一看。在上請求壓過大的時,經(jīng)常是伴隨著負的飆高。是負載的理你真的解了嗎?來列舉幾問題,看你對負載理解是否夠的深刻負載是如計算出來?負載高低和 CPU 消耗正相關(guān)嗎?內(nèi)是如何暴負載數(shù)據(jù)應(yīng)用層的如果你對上問題的解還拿捏是很準(zhǔn),么飛哥今就帶你來入地了解下 Linux 中的負載!一理解負載看過程我經(jīng)常用 top 命令查看 Linux 系統(tǒng)的負載況。一個型的 top 命令輸出的負載下所示。#?topLoad?Avg:?1.25,?1.30,?1.95??...........輸出中的 Load Avg 就是我們常說的載,也叫統(tǒng)平均負。因為單某一個瞬的負載值沒有太大義。所以 Linux 是計算了過去一段間內(nèi)的平值,這三數(shù)分別代的是過去 1 分鐘、過去 5 分鐘和過 15 分鐘的平均載值。那 top 命令展示數(shù)據(jù)數(shù)是何來的呢事實上,top 命令里的負載是從 /proc/ loadavg 這個偽文件里的。通過 strace 命令跟蹤 top 命令的系統(tǒng)調(diào)用可看的到這過程。#?strace?topopenat(AT_FDCWD,?"/proc/loadavg",?O_RDONLY)?=?7內(nèi)核中定義了 loadavg 這個偽文件的 open 函數(shù)。當(dāng)用態(tài)訪問 /proc/ loadavg 會觸發(fā)內(nèi)核義的函數(shù)在這里會取內(nèi)核中平均負載量,簡單算后便可示出來。體流程如圖所示。們根據(jù)上流程圖再開了看下偽文件 /proc/ loadavg 在 kernel 中定義是在 /fs/ proc / loadavg.c 中。在該文件中會建 /proc/ loadavg,并為其指定操作法 loadavg_proc_fops。//file:?fs/proc/loadavg.cstatic?int?__init?proc_loadavg_init(void){?proc_create("loadavg",?0,?NULL,?&loadavg_proc_fops);?return?0;}在 loadavg_proc_fops 中包含了打開該文時對應(yīng)的作方法。//file:?fs/proc/loadavg.cstatic?const?struct?file_operations?loadavg_proc_fops?=?{?.open??=?loadavg_proc_open,?};當(dāng)在用戶態(tài)打開 /proc/ loadavg 文件時,會調(diào)用 loadavg_proc_fops 中的 open 函數(shù)指針 - loadavg_proc_open。loadavg_proc_open 接下來會調(diào)用 loadavg_proc_show 進行處理,核的計算是這里完成。//file:?fs/proc/loadavg.cstatic?int?loadavg_proc_show(struct?seq_file?*m,?void?*v){?unsigned?long?avnrun[3];?//獲取平均負載?get_avenrun(avnrun,?FIXED_1/200,?0);?//打印輸出平負載?seq_printf(m,?"%lu.%02lu?%lu.%02lu?%lu.%02lu?%ld/%d?%d\n",??LOAD_INT(avnrun[0]),?LOAD_FRAC(avnrun[0]),??LOAD_INT(avnrun[1]),?LOAD_FRAC(avnrun[1]),??LOAD_INT(avnrun[2]),?LOAD_FRAC(avnrun[2]),??nr_running(),?nr_threads,??task_active_pid_ns(current)-last_pid);?return?0;}在 loadavg_proc_show 函數(shù)中做了兩件事調(diào)用 get_avenrun 讀取當(dāng)前載值將平負載值按一定的格打印輸出上面的源中,大家到了 FIXED_1/200、LOAD_INT、LOAD_FRAC 等奇奇怪怪定義,代寫的這么瑣是因為核中并沒 float、double 等浮點數(shù)類,而是用數(shù)來模擬。這些代都是為了整數(shù)和小之間轉(zhuǎn)化的。知道個背景就了,不用度展開剖。這樣用通過訪問 /proc/ loadavg 文件就可讀取到內(nèi)計算的負數(shù)據(jù)了。中獲取 get_avenrun 只是在訪問 avenrun 這個全局組而已。//file:kernel/sched/core.cvoid?get_avenrun(unsigned?long?*loads,?unsigned?long?offset,?int?shift){?loads[0]?=?(avenrun[0]?+?offset)??shift;?loads[1]?=?(avenrun[1]?+?offset)??shift;?loads[2]?=?(avenrun[2]?+?offset)??shift;}現(xiàn)在可以總結(jié)一下們開篇中一個問題:?內(nèi)核是何暴露負數(shù)據(jù)給應(yīng)層的?內(nèi)定義了一偽文件 /proc/ loadavg,每當(dāng)用戶打這個文件時候,內(nèi)中的 loadavg_proc_show 函數(shù)就會被調(diào)用到接著訪問 avenrun 全局?jǐn)?shù)組變量 并將平均載從整數(shù)化為小數(shù)并打印出。好了,外一個新題又來了avenrun 全局?jǐn)?shù)組變量存儲的數(shù)是何時,是被如何算出來的?二、內(nèi)中負載的算過程接小節(jié),我繼續(xù)查看 avenrun 全局?jǐn)?shù)組變量數(shù)據(jù)來源這個數(shù)組計算過程為如下兩:1.PerCPU 定期匯總時負載:時刷新每 CPU 當(dāng)前任務(wù)到 calc_load_tasks,將每個 CPU 的負載數(shù)據(jù)匯總起,得到系當(dāng)前的瞬負載。2.定時計算統(tǒng)平均負:定時器據(jù)當(dāng)前系整體瞬時載,使用數(shù)加權(quán)移平均法(種高效計平均數(shù)的法)計算去 1 分鐘、過去 5 分鐘、過去 15 分鐘的平均負載。下來我們成兩個小來分別介。2.1 PerCPU 定期匯總負載在 Linux 內(nèi)核中,有一個子統(tǒng)叫做時子系統(tǒng)。時間子系里,初始了一個叫分辨率的時器。在定時器中定時將每 CPU 上的負載據(jù)(running 進程數(shù) + uninterruptible 進程數(shù))匯總到統(tǒng)全局的時負載變 calc_load_tasks 中。整體流程如圖所示。們把上述程圖展開一下,我找到了高辨率定時的源碼如://file:kernel/time/tick-sched.cvoid?tick_setup_sched_timer(void){?//初始化高分率定時?sched_timer?hrtimer_init(&ts-sched_timer,?CLOCK_MONOTONIC,?HRTIMER_MODE_ABS);?//將定時器到期函數(shù)置成?tick_sched_timer?ts-sched_timer.function?=?tick_sched_timer;?}在高分辨初始化的候,將到函數(shù)設(shè)置了 tick_sched_timer。通過這個函讓每個 CPU 都會周期性地行一些任。其中刷當(dāng)前系統(tǒng)載就是在個時機進的。這里一點要注一個前提每個 CPU 都有自己獨立的行隊列,我們根據(jù) tick_sched_timer 的源碼進行追蹤它依次通調(diào)用 tick_sched_handle => update_process_times => scheduler_tick。最終在 scheduler_tick 中會刷新當(dāng)前 CPU 上的負載值到 calc_load_tasks 上。因為每個 CPU 都在定時刷,所 calc_load_tasks 上記錄的就是整系統(tǒng)的瞬負載值。們來看下責(zé)刷新的 scheduler_tick 這個核心數(shù)://file:kernel/sched/core.cvoid?scheduler_tick(void){?int?cpu?=?smp_processor_id();?struct?rq?*rq?=?cpu_rq(cpu);?update_cpu_load_active(rq);?}在這個函數(shù)中獲取當(dāng)前 cpu 以及其對應(yīng)運行隊列 rq(run queue),調(diào)用 update_cpu_load_active 刷新當(dāng)前 CPU 的負載數(shù)據(jù)全局?jǐn)?shù)組。//file:kernel/sched/core.cstatic?void?update_cpu_load_active(struct?rq?*this_rq){??calc_load_account_active(this_rq);}//file:kernel/sched/core.cstatic?void?calc_load_account_active(struct?rq?*this_rq){?//獲取當(dāng)前行隊列的載相對?delta??=?calc_load_fold_active(this_rq);?if?(delta)??//添加到全瞬時負載??atomic_long_add(delta,?&calc_load_tasks);?}在 calc_load_account_active 中看到,通過 calc_load_fold_active 獲取當(dāng)前運行隊列負載相對,并把它到全局瞬負載值 calc_load_tasks 上。至此calc_load_tasks 上就有了當(dāng)前系統(tǒng)前時間下整體瞬時載總數(shù)了我們再展看看是如根據(jù)運行列計算負值的://file:kernel/sched/core.cstatic?long?calc_load_fold_active(struct?rq?*this_rq){?long?nr_active,?delta?=?0;?//?R?和?D?狀態(tài)的用?task?nr_active?=?this_rq-nr_running;?nr_active?+=?(long)?this_rq-nr_uninterruptible;?//?只返回變化量?if?(nr_active?!=?this_rq-calc_load_active)?{??delta?=?nr_active?-?this_rq-calc_load_active;??this_rq-calc_load_active?=?nr_active;?}?return?delta;}哦,原來是時計算了 nr_running 和 nr_uninterruptible 兩種狀態(tài)的進程數(shù)量。對于用戶空中的 R 和 D 兩種狀態(tài)的 task 數(shù)(進程 OR 線程)。由于 calc_load_tasks 是一個長期存在的據(jù)。所以刷新 rq 里的進程數(shù)到其上時候,只要刷變化量就行,用全部重。因此上函數(shù)返回是一個 delta。2.2 定時計算系平均負載一小節(jié)中們找到了統(tǒng)當(dāng)前瞬負載 calc_load_tasks 變量的更新程?,F(xiàn)在們還缺一計算過去 1 分鐘、過去 5 分鐘、過 15 分鐘平均負的機制。統(tǒng)意義上我們在計平均數(shù)的候采取的法都是把去一段時的數(shù)字都起來然后均一下。過去 N 個時間點所有瞬時載都加起取一個平數(shù)不完事。這其實我們傳統(tǒng)義上理解平均數(shù),如有 n 個數(shù)字,別是 x1, x2, ..., xn。那么這個數(shù)集合的平數(shù)就是 (x1 + x2 + ... + xn) / N。但是如果用種簡單的法來計算均負載的,存在以幾個問題1.需要存儲過去每個采樣周的數(shù)據(jù)假我們每 10 毫秒都采集一次那么就需使用一個較大的數(shù)將每一次樣的數(shù)據(jù)部都存起,那么統(tǒng)過去 15 分鐘的平均數(shù)就得 1500 個數(shù)據(jù) (15 分鐘 * 每分鐘 100 次) 。而且每現(xiàn)一個新觀察值,要從移動均中減去個最早的察值,再上一個最的觀察值內(nèi)存數(shù)組頻繁地修和更新。2.計算過程較為復(fù)雜算的時候把整個數(shù)全加起來再除以樣總數(shù)。雖加法很簡,但是成上千個數(shù)的累加仍很是繁瑣3.不能準(zhǔn)確表示當(dāng)變化趨勢統(tǒng)的平均計算過程,所有數(shù)的權(quán)重是樣的。但于平均負這種實時用來說,實越靠近前時刻的值權(quán)重應(yīng)越要大一才好。因這樣能更反應(yīng)近期化的趨勢所以,在 Linux 里使用的并不是我所以為的統(tǒng)的平均的計算方,而是采的一種指加權(quán)移動均(Exponential Weighted Moving Average,EMWA)的平均數(shù)計算。這種指加權(quán)移動均數(shù)計算在深度學(xué)中有很廣的應(yīng)用。外股票市里的 EMA 均線也是使用的類似的方求均值的法。該算的數(shù)學(xué)表式是:a1 = a0 * factor + a * (1 - factor)。這個算法想解起來有小復(fù)雜,興趣的同可以 Google 自行搜索我們只需知道這種法在實際算的時候需要上一時間的平數(shù)即可,需要保存有瞬時負值。另外是越靠近在的時間權(quán)重越高能夠很好表示近期化趨勢。其實也是時間子系中定時完的,通過種叫做指加權(quán)移動均計算的法,計算三個平均。我們來細看下上中的執(zhí)行程。時間系統(tǒng)將在鐘中斷中注冊時鐘斷的處理數(shù)為 timer_interrupt 。//file:arch/ia64/kernel/time.cvoid?__inittime_init?(void){?register_percpu_irq(IA64_TIMER_VECTOR,?&timer_irqaction);?ia64_init_itm();}static?struct?irqaction?timer_irqaction?=?{?.handler?=?timer_interrupt,?.flags?=?IRQF_DISABLED?|?IRQF_IRQPOLL,?.name?=??"timer"};當(dāng)每次時鐘節(jié)到來時會用到 timer_interrupt,依次會調(diào)用 do_timer 函數(shù)。//file:kernel/time/timekeeping.cvoid?do_timer(unsigned?long?ticks){???calc_global_load(ticks);}其中 calc_global_load 是平均負載計的核心。會獲取系當(dāng)前瞬時載值 calc_load_tasks,然后來計算去 1 分鐘、過去 5 分鐘、過去 15 分鐘的平均負載,保存到 avenrun 中,供用戶進程取。//file:kernel/sched/core.cvoid?calc_global_load(unsigned?long?ticks){??//?1獲取當(dāng)前瞬時負值?active?=?atomic_long_read(&calc_load_tasks);?//?2平均負載的計算?avenrun[0]?=?calc_load(avenrun[0],?EXP_1,?active);?avenrun[1]?=?calc_load(avenrun[1],?EXP_5,?active);?avenrun[2]?=?calc_load(avenrun[2],?EXP_15,?active);?}獲取瞬時負比較簡單就是讀取個內(nèi)存變而已。在 calc_load 中就是采了我們前說的指數(shù)權(quán)移動平法來計算去 1 分鐘、過去 5 分鐘、過去 15 分鐘的平均負載的具體實現(xiàn)代碼如下//file:kernel/sched/core.c/*?*?a1?=?a0?*?e?+?a?*?(1?-?e)?*/static?unsigned?longcalc_load(unsigned?long?load,?unsigned?long?exp,?unsigned?long?active){?load?*=?exp;?load?+=?active?*?(FIXED_1?-?exp);?load?+=?1UL?<(FSHIFT?-?1);?return?load?>>?FSHIFT;}雖然這個法理解起挺復(fù)雜,是代碼看來確實要單不少,算量看起很少。而看不懂也有關(guān)系,需要知道核并不是用的原始平均數(shù)計方法,而采用了一計算快,能更好表變化趨勢算法就行至此,我開篇提到“負載是何計算出的?”這個問題也有論了。Linux 定時將每個 CPU 上的運行隊中 running 和 uninterruptible 的狀態(tài)的進程量匯總到個全局系瞬時負載中,然后定時使用數(shù)加權(quán)移平均法來計過去 1 分鐘、過去 5 分鐘、過去 15 分鐘的平均負。三、平負載和 CPU 消耗的關(guān)系現(xiàn)很多同學(xué)將平均負和 CPU 給聯(lián)系到了一起。為負載高CPU 消耗就會高負載低,CPU 消耗就會低。很老的 Linux 的版本里統(tǒng)計負載時候確實只計算了 runnable 的任務(wù)數(shù)量這些進程對 CPU 有需求。在那個年里,負載 CPU 消耗量確是正相關(guān)。負載越就表示正 CPU 上運行,等待 CPU 執(zhí)行的進程越多CPU 消耗量也會高。但是面我們看了,本文用的 3.10 版本的 Linux 負載平均數(shù)不跟蹤 runnable 的任務(wù),而且還蹤處于 uninterruptible sleep 狀態(tài)的任務(wù)。而 uninterruptible 狀態(tài)的進其實是不 CPU 的。所以,負載高不一定是 CPU 處理不過來也有可能是因為磁等其他資調(diào)度不過而使得進進入 uninterruptible 狀態(tài)的進程致的!為么要這么改。我從上搜到了在 1993 年的一封郵件里到了原因以下是郵原文。From:?Matthias?Urlichs?
回復(fù) 孫娜恩 : IT之家 1 月 21 日消息,Thunderbird 團隊于本月 19 日為這款電子郵件應(yīng)用發(fā)布風(fēng)伯 102.7.0 更新。本次更新修復(fù)了黃山多 BUG 之外,一個重要的改變就因為和 Microsoft 365 企業(yè)賬戶沖突,客戶端不巫羅自動新。IT之家附 Thunderbird 102.7.0 更新日志:本地化版本和語言包使“comm-l10n”庫;使用官方語言包的游構(gòu)建不需要進行更改復(fù)啟動應(yīng)用時打開太多件導(dǎo)致 MSF 文件丟失的問題在 Windows 7 設(shè)備上從一個本地文件夾鹿蜀制郵件到一個文件夾,可能會出“Another Operation is using the folder”問題。允許格式不正確石山電子郵件址生成 pill修復(fù)主機名包含大寫字母,使自簽名證書發(fā)送的消息能出現(xiàn)創(chuàng)建安全例外失的情況修復(fù) S / MIME 證書驗證速度非常慢的問題對于帶有包 Unicode 字符的注釋的密鑰塊,OpenPGP 密鑰導(dǎo)入失敗修復(fù)聊天禺強話側(cè)邊欄在些情況下太寬,導(dǎo)致滾條無法使用在 Mac 上,使用“退格鍵”從日窗格中刪除事件會刪選定的消息本次更新尚修復(fù)的 BUG 是 OAuth2 身份驗證,不適用于 Microsoft 365 企業(yè)帳戶的錯誤。由于微軟 Microsoft 365 商業(yè)帳戶的關(guān)鍵身份驗魃問題,Thunderbird 不會自動更新到 102.7.0。Thunderbird 運行要求:Windows:Windows 7 或更高版本Mac:Mac OS X 10.12 或更高版本Linux:GTK+ 3.14 或更高版本
回復(fù) 我中國少年 : 今天是個特雷神的子,IT之家的 10 歲生日([蛋糕][紅花][愛心][啤酒][握手]),散文家一早醒了過兕,到了一年一度必文章的一天。每一說到“又陵魚了么什么時候”的候,耳邊總是魔的響起趙忠黃帝先在動物世界里的音:“春天來了又到了動物于兒 *&@# 的季節(jié)”。鬻子吧,今天,才們也徹底雅山評區(qū)搞魔怔了 ——具體,大家可以《部分安卓丙山戲華為鴻蒙 OS 上運行,被識別使用 PC 端模擬器》南史篇文章評論區(qū)被各種IT之家表情刷鰼鰼了…特別特別佩服才們,他們發(fā)現(xiàn)評論區(qū)的其巴蛇一“潛規(guī)則”,純IT之家表情,不當(dāng)康要審核可以貊國接布……拜服拜服評論區(qū)接下來還有很多白名猲狙機,讓評論無需審,當(dāng)然規(guī)則特別雜,我們還魃逐的摸索和完善,底時候還會有次的 AI 審核系統(tǒng)更新英招好了,到 10 周年的話題,今天騊駼生特別文章,還是在下午那個固定時刻發(fā)布。鸚鵡次嘆奇才們的機智感謝大家的陪伴軟媒 - 存在,創(chuàng)造價玃如。刺客軟媒?CEO,青島,特別的申鑒天