作者簡介
朱輝,做過幾年模擬器,做過幾年GDB,在小米電視做過幾年Linux內核優化,主要圍繞MM。
現在在HyperHQ當軟件工程師。
之前在我熱愛的公眾號Linuxer看到The precise meaning of I/O wait time in Linux 這篇文章,感覺寫的不錯,就是沒有落實到源碼上感覺稍微有點晦澀,于是自己讀了一下代碼。
當task發生iowait的時候,內核對他們的處理方法是將task切換出去,讓可運行的task先運行,而在切換出去前,會將其in_iowait設置為1,再次被喚醒的時候in_iowait被設置為原值。相關函數io_schedule,io_schedule_timeout,mutex_lock_io,mutex_lock_io_nested。
例如:
由此可見in_iowait表明了這個task是否在iowait。
另外要注意的是,這幾個切換函數除了mutex_lock_io,mutex_lock_io_nested會設置task運行狀態為TASK_UNINTERRUPTIBLE外,內核在調用io_schedule,io_schedule_timeout前都會設置task運行狀態TASK_UNINTERRUPTIBLE。
在進程切換函數__schedule在切換task的時候,如果被切換出的task的in_iowait為真,則會對這個CPU的運行隊列rq結構中的nr_iowait加1。
因為前面對task已經被設置為TASK_UNINTERRUPTIBLE,則task需要被喚醒,對nr_iowait的減少操作也是在task喚醒函數來做的。
由此可見nr_iowait可以表明某CPU上是否有task在iowait,以及數量。
因為處于iowait的task是TASK_UNINTERRUPTIBLE狀態,其并不在就緒隊列中,所以其也沒有被CPU負載均衡到其他CPU的可能,所以nr_iowait也不需要處理負載均衡問題。
當累加系統idle時間的時候,如果CPU的nr_iowait為真,也就是當前這個cpu有task在等待iowait,則記錄為iowait時間。
在打開NO_HZ的內核中,相關代碼在update_ts_time_stats。
而沒打開的則在 account_idle_time。
當相關/proc/stat接口被訪問時,get_iowait_time就會訪問這個時間并返回。
綜上所述,iowait時間就是CPU idle時間,但是這時候CPU上不是完全沒TASK需要運行,而是休眠的task中有一個或者若干個是iowait的task。
當然idle和iowait的時候CPU上還有idle task。
最后推薦一篇阿里內核組的文章作為擴展閱讀Kernel Documents/new iowait calculation
比較有意思是這里:
+ wait_event_interruptible_hrtimeout(ctx->wait,
+ aio_read_events(ctx, min_nr, nr, event, &ret), until);
無論超時值until是什么值,都會調用wait_event_interruptible_hrtimeout,雖然是hrtimer實時性已經很高,但是在用來實際處理wait的宏__wait_event_hrtimeout可以看到hrtimer初始化使用的是:
hrtimer_start_range_ns(&__t.timer, timeout,
current->timer_slack_ns,
HRTIMER_MODE_REL);
其中第三個參數current->timer_slack_ns是傳遞給hrtimer的觸發范圍,因為hrtimer實時性高,但是頻繁觸發系統顯然受不了,所以每次hrtimer觸發都會將時間范圍內的timer都處理掉(見__hrtimer_run_queues)。所以timeout+current->timer_slack_ns才是設置的hrtimer的最后觸發時間,current->timer_slack_ns的默認值是50000,也就是代表50000納秒。也就是這個時鐘最久會在50000納秒后觸發,當然也可能被之前的hrtimer觸發。
所以在wait_event_interruptible_hrtimeout中,一旦ctx->wait未能就緒,即使設置超時時間為0,也很可能要調用一次schedule,這導致iowait時間相差很大,也還很大幅度傷害了性能。
而這個問題也被5f785de588735306ec4d7c875caf9d28481c8b21進行了修復,這段代碼改成了:
- wait_event_interruptible_hrtimeout(ctx->wait,
- aio_read_events(ctx, min_nr, nr, event, &ret), until);
+ if (until.tv64 == 0)
+ aio_read_events(ctx, min_nr, nr, event, &ret);
+ else
+ wait_event_interruptible_hrtimeout(ctx->wait,
+ aio_read_events(ctx, min_nr, nr, event, &ret),
+ until);
從而在until為0的時候,直接調用aio_read_events。應該就不會再有那么明顯的iowait問題了,另外也因此這個修復讓io_getevents的調用得到了超過百倍的性能提升。
當然這個iowait不夠精確的原因還是存在,一旦因為需要發生task切換,還是會有不夠精確的問題。
最后要吐槽一下aio的設計,都aio了還需要wait嗎?
-
內核
+關注
關注
3文章
1382瀏覽量
40373 -
Linux
+關注
關注
87文章
11342瀏覽量
210146 -
代碼
+關注
關注
30文章
4823瀏覽量
68897
原文標題:朱輝(茶水): Linux Kernel iowait 時間的代碼原理
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論