<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
proc - process information pseudo-filesystem (儲存程序資訊的偽檔案系統)
The proc filesystem is a pseudo-filesystem which provides an interface to kernel data structures.
It is commonly mounted at /proc. Most of it is read-only, but some files allow kernel variables to
be changedpooc檔案系統是一個偽裝的檔案系統,它提供介面給核心來儲存資料,通常掛載在裝置的/proc目錄,
大部分檔案是唯讀的,但是有些檔案可以被內和變數給改變.
具體代表的含義可以通過man proc
去檢視. 以上資訊就是通過man
獲取.翻譯不一定精確.
cat /proc/loadavg
/proc/loadavg
The first three fields in this file are load average figures giving the number of
jobs in the run queue (state R) or waiting for disk I/O (state D) averaged over 1, 5,
and 15 minutes.這個檔案的前三個數位是平均負載的數值,計算平均1分鐘,5分鐘,15分鐘內的執行佇列中(R狀態)或等待磁碟I/O(D狀態)的任務數.
The first of these is the number of cur‐rently runnable kernel scheduling entities
(processes, threads). The value after the slash is the number of kernel scheduling
entities that currently exist on the system.第四個引數/前面是可執行的核心排程實體的數量(排程實體指 程序,執行緒), /後的值是系統中存在的核心排程實體的數量.
The fifth field is the PID of the process that was most recently created on the system.
第五個引數是系統最新建立程序的PID
在從事的大屏領域遇到一個問題,就是loadavg
中的數值其高無比,對比8
核手機的3+
,4+
,目前的手頭的裝置loadavg
竟然高達70+
,這個問題一直困擾了我很久,最近騰出一個整塊的時間來研究一下這個數值的計算規則.
在kernel
中的loadvg.c
檔案中有這樣的一個函數.我們看到它就是最終的輸出函數.
static 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 %dn", LOAD_INT(avnrun[0]), LOAD_FRAC(avnrun[0]), // 1分鐘平均值 LOAD_INT(avnrun[1]), LOAD_FRAC(avnrun[1]), // 5分鐘平均值 LOAD_INT(avnrun[2]), LOAD_FRAC(avnrun[2]), // 15分鐘平均值 // 可執行實體使用 nr_running()獲取, nr_threads 是存在的所有實體 nr_running() , nr_threads, // 獲取最新建立的程序PID task_active_pid_ns(current)->last_pid); return 0; }
看過上面的程式碼獲取具體平均負載的函數是get_avenrun()
,我們接著找一下它的具體實現.
unsigned long avenrun[3]; EXPORT_SYMBOL(avenrun); /* should be removed */ /** * get_avenrun - get the load average array * @loads: pointer to dest load array * @offset: offset to add * @shift: shift count to shift the result left * * These values are estimates at best, so no need for locking. */ void get_avenrun(unsigned long *loads, unsigned long offset, int shift) { //資料來源主要是avenrun陣列 loads[0] = (avenrun[0] + offset) << shift; loads[1] = (avenrun[1] + offset) << shift; loads[2] = (avenrun[2] + offset) << shift; }
接著我們接著尋找avenrun[]
在哪裡賦值,我們先看資料的來源問題.
kernel
版本4.9
程式碼路徑kernel/sched/core.c
,kernel/sched/loadavg.c
./* * This function gets called by the timer code, with HZ frequency. * We call it with interrupts disabled. * 這裡註釋就比較清楚了,由計時器排程,排程的頻率為HZ */ void scheduler_tick(void) { int cpu = smp_processor_id(); struct rq *rq = cpu_rq(cpu); struct task_struct *curr = rq->curr; sched_clock_tick(); raw_spin_lock(&rq->lock); walt_set_window_start(rq); walt_update_task_ravg(rq->curr, rq, TASK_UPDATE, walt_ktime_clock(), 0); update_rq_clock(rq); curr->sched_class->task_tick(rq, curr, 0); cpu_load_update_active(rq); calc_global_load_tick(rq); // 這裡排程 raw_spin_unlock(&rq->lock); perf_event_task_tick(); #ifdef CONFIG_SMP rq->idle_balance = idle_cpu(cpu); trigger_load_balance(rq); #endif rq_last_tick_reset(rq); if (curr->sched_class == &fair_sched_class) check_for_migration(rq, curr); }
/* * Called from scheduler_tick() to periodically update this CPU's * active count. */ void calc_global_load_tick(struct rq *this_rq) { long delta; //過濾系統負載重複更新,這裡是同過jiffies進行過濾,jiffies也在下面統一介紹 if (time_before(jiffies, this_rq->calc_load_update)) return; // 更新資料 delta = calc_load_fold_active(this_rq, 0); if (delta) // 將資料同步到calc_load_tasks, atomic_long_add 是kernel中的一個原子操作函數 atomic_long_add(delta, &calc_load_tasks); // 下一次系統更新系統負載的時間 LOAD_FREQ定義在include/linux/sched.h // #define LOAD_FREQ (5*HZ+1) /* 5 sec intervals */ this_rq->calc_load_update += LOAD_FREQ; }
long calc_load_fold_active(struct rq *this_rq, long adjust) { long nr_active, delta = 0; nr_active = this_rq->nr_running - adjust; //統計排程器中nr_running的task數量 adjust傳入為0,不做討論. nr_active += (long)this_rq->nr_uninterruptible; //統計排程器中nr_uninterruptible的task的數量. // calc_load_active代表了nr_running和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後,將資料更新到 calc_load_tasks. return delta; }
看完資料來源的邏輯,我們接著梳理資料計算的邏輯
這裡前半部分的邏輯設計的底層驅動的高解析度定時器模組,我並不是十分了解.簡單的介紹一下,感興趣的可以自己去研究一下.(類名:tick-sched.c,因為planuml
不支援類名存在-
)
/* * High resolution timer specific code */ //這裡要看下核心是否開啟了高解析度定時器+ CONFIG_HIGH_RES_TIMERS = y #ifdef CONFIG_HIGH_RES_TIMERS /* * We rearm the timer until we get disabled by the idle code. * Called with interrupts disabled. */ // tick_sched_timer函數是高解析度定時器的到期函數,也就是定時的每個週期結束都會執行 static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer) { struct tick_sched *ts = container_of(timer, struct tick_sched, sched_timer); struct pt_regs *regs = get_irq_regs(); ktime_t now = ktime_get(); tick_sched_do_timer(now); ... return HRTIMER_RESTART; }
中間的定時器模組的函數就跳過了,已經超出本文的範圍,我也並不是完全瞭解其中的邏輯.
/* * calc_load - update the avenrun load estimates 10 ticks after the * CPUs have updated calc_load_tasks. * * Called from the global timer code. */ void calc_global_load(unsigned long ticks) { long active, delta; // 在前文出現過的時間,這裡有加上了10個tick,總間隔就是5s + 10 tick if (time_before(jiffies, calc_load_update + 10)) return; /* * Fold the 'old' idle-delta to include all NO_HZ cpus. */ // 統計NO_HZ模式下,cpu陷入空閒時間段錯過統計的task資料 delta = calc_load_fold_idle(); if (delta) atomic_long_add(delta, &calc_load_tasks); // 更新資料 active = atomic_long_read(&calc_load_tasks); // 原子的方式讀取前面存入的全域性變數 active = active > 0 ? active * FIXED_1 : 0; // 乘FIXED_1 avenrun[0] = calc_load(avenrun[0], EXP_1, active); // 1分鐘負載 avenrun[1] = calc_load(avenrun[1], EXP_5, active); // 5分鐘負載 avenrun[2] = calc_load(avenrun[2], EXP_15, active); // 15分鐘負載 calc_load_update += LOAD_FREQ; //更新時間 /* * In case we idled for multiple LOAD_FREQ intervals, catch up in bulk. */ //統計了NO_HZ模式下的task資料,也要將NO_HZ模式下的tick數重新計算,要不然資料會不準. calc_global_nohz(); }
這裡出現了一個NO_HZ
模式,這個是CPU的一個概念,後文專門介紹一下.下面就是負載的計算規則了
/* * a1 = a0 * e + a * (1 - e) */ static unsigned long calc_load(unsigned long load, unsigned long exp, unsigned long active) { unsigned long newload; newload = load * exp + active * (FIXED_1 - exp); if (active >= load) newload += FIXED_1-1; return newload / FIXED_1; }
具體的計算規則註釋也是非常清晰了,並不複雜,整體下來就和使用man proc
獲取到的資訊一樣,系統負載統計的是nr_running
和nr_uninterruptible
的數量.這兩個資料的來源就是core.c
的struct rq
,rq
是CPU執行佇列中重要的儲存結構之一.
回到最初的問題,我司的裝置系統負載達到70+
還沒有卡爆炸的原因,通過上面的程式碼邏輯還是沒有直接給出答案.不過已經有了邏輯,其他就很簡單了.
nr_running
和nr_uninterruptible
的task數量發現,nr_running
的資料是正常的,出問題的在與nr_uninterruptible
的數量.nr_uninterruptible
task數量,那麼我司的裝置真的有那麼多工在等待I/O麼,真的有怎麼多工在等待I/O,裝置依然會十分卡頓,我抓取了systrace
檢視後,一切是正常的.nr_uninterruptible
的關鍵字,我查到了一些蛛絲馬跡.首先在UNIX
系統上是沒有統計nr_uninterruptible
的,Linux
在引入後,有人提出不統計I/O
等待的任務數量,無法體現真正體現系統的負載狀況.
後面在很多Linux
大佬的文章中看到一個資訊,NFS系統出現問題的的時候,會將所有存取這個檔案系統的執行緒都標識為nr_uninterruptible
,這部分的知識太貼近核心了.(ps:如果有大佬有相關的核心書籍推薦的話,請務必推薦一下).
nr_uninterruptible
的資料異常,導致系統負載資料並沒有體現出目前裝置的真實狀況.HZ
,應該是軟中斷,軟中斷和核心設定中的CONFIG_HZ_250
,CONFIG_HZ_1000
是關聯的,例如CONFIG_HZ_1000=y,CONFIG_HZ=1000
,就是每秒核心會發出1000的軟中斷訊號. 對應的時間就是 1s/1000
. (通常CONFIG_HZ=250
)jiffies
它就是時鐘中斷次數, jiffies = 1s / HZ
rq
結構體太長了,就不全部貼出來了,結構體定義在kernel/sched/sched.h
中,有興趣的自行檢視.struct rq *rq = cpu_rq(cpu); /* * This is the main, per-CPU runqueue data structure. * * Locking rule: those places that want to lock multiple runqueues * (such as the load balancing or the thread migration code), lock * acquire operations must be ordered by ascending &runqueue. */ struct rq { /* runqueue lock: */ raw_spinlock_t lock; /* * nr_running and cpu_load should be in the same cacheline because * remote CPUs use both these fields when doing load calculation. */ unsigned int nr_running; // 這裡 #ifdef CONFIG_NUMA_BALANCING unsigned int nr_numa_running; unsigned int nr_preferred_running; #endif #define CPU_LOAD_IDX_MAX 5 unsigned long cpu_load[CPU_LOAD_IDX_MAX]; unsigned int misfit_task; #ifdef CONFIG_NO_HZ_COMMON #ifdef CONFIG_SMP unsigned long last_load_update_tick; #endif /* CONFIG_SMP */ unsigned long nohz_flags; #endif /* CONFIG_NO_HZ_COMMON */ #ifdef CONFIG_NO_HZ_FULL unsigned long last_sched_tick; #endif #ifdef CONFIG_CPU_QUIET /* time-based average load */ u64 nr_last_stamp; u64 nr_running_integral; seqcount_t ave_seqcnt; #endif /* capture load from *all* tasks on this cpu: */ struct load_weight load; unsigned long nr_load_updates; u64 nr_switches; struct cfs_rq cfs; struct rt_rq rt; struct dl_rq dl; #ifdef CONFIG_FAIR_GROUP_SCHED /* list of leaf cfs_rq on this cpu: */ struct list_head leaf_cfs_rq_list; struct list_head *tmp_alone_branch; #endif /* CONFIG_FAIR_GROUP_SCHED */ /* * This is part of a global counter where only the total sum * over all CPUs matters. A task can increase this counter on * one CPU and if it got migrated afterwards it may decrease * it on another CPU. Always updated under the runqueue lock: */ unsigned long nr_uninterruptible; // 這裡 struct task_struct *curr, *idle, *stop; unsigned long next_balance; struct mm_struct *prev_mm; unsigned int clock_skip_update; u64 clock; u64 clock_task; atomic_t nr_iowait; #ifdef CONFIG_SMP struct root_domain *rd; struct sched_domain *sd; unsigned long cpu_capacity; unsigned long cpu_capacity_orig; struct callback_head *balance_callback; unsigned char idle_balance; /* For active balancing */ int active_balance; int push_cpu; struct task_struct *push_task; struct cpu_stop_work active_balance_work; /* cpu of this runqueue: */ int cpu; int online; ... };
CONFIG_HIGH_RES_TIMERS=y
NO_HZ
就是在CPU進入休眠狀態時,不再持續的傳送軟中斷訊號,來減少裝置功耗與耗電.核心設定CONFIG_NO_HZ=y
&CONFIG_NO_HZ_IDLE=y
,那麼相反,如果裝置對功耗並不敏感,需要外部輸入電源,可以關閉這個模式,來提高效能.Android
提取核心設定:adb pull /proc/config.gz .
以上就是loadavg資料異常引發問題起源分析的詳細內容,更多關於loadavg 資料異常的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45