內核在微觀上,把CPU的運行時間分成許多分,然后安排給各個進程輪流運行,造成宏觀上所有的進程仿佛同時在執行。雙核CPU,實際上最多只能有兩個進程在同時運行,大家在top、vmstat命令里看到的正在運行的進程,並不是真的在占有着CPU哈。
所以,一些設計良好的高性能進程,比如nginx,都是實際上有幾顆CPU,就配幾個工作進程,道理就在這。比如你的服務器有8顆CPU,那么nginx worker應當只有8個,當你多於8個時,內核可能會放超過多個nginx worker進程到1個runqueue里,會發生什么呢?就是在這顆CPU上,會比較均勻的把時間分配給這幾個nginx worker,每個worker進程運行完一個時間片后,內核需要做進程切換,把正在運行的進程上下文保存下來。假設內核分配的時間片是100ms,做進程切換的時間是5ms,那么進程性能下降還是很明顯的,跟你配置的worker有關,中老年女裝越多下降得越厲害。
當然,這是跟nginx的設計有關的。nginx是事件驅動的全異步進程,本身設計上就幾乎不存在阻塞和中斷,nginx的設計者就希望每一個nginx worker可以獨占CPU的幾乎全部時間片,這點就是nginx worker數量配置的依據所在。
當然,實際的運行進程里,大部分並不是nginx這種希望獨占CPU全部時間片的進程,許多進程,比如vi,它在很多時間是在等待用戶輸入,這時vi在等待IO中斷,是不占用時間片的,內核面對多樣化的進程,就需要技巧性的分配CPU時間片了。
內核分配時間片是有策略和傾向性的。換句話說,內核是偏心的,它喜歡的是IO消耗型進程,因為這類進程如果不能及時響應,用戶就會很不爽,所以它總會下意識的多分配CPU運行時間給這類進程。而CPU消耗進程內核就不太關心了。這有道理嗎?太有了,CPU消耗型慢一點用戶感知不出來,電信號和生物信號運轉速度差距巨大。雖然內核盡量多的分配時間片給IO消耗型進程,但IO消耗進程常常在睡覺,給它的時間片根本用不掉。很合理吧?
那么內核具體是怎么實現這種偏心呢?通過動態調整進程的優先級,以及分配不同長短的CPU時間處來實現。先說內核如何決定時間片的長度。
對每一個進程,有一個整型static_prio表示用戶設置的靜態優先級,內核里它與nice值是對應的。看看進程描述結構里的static_prio成員。
nice值是什么?其實就是優先級針對用戶進程的另一種表示法,nice的取值范圍是-20到+19,-20優先級最高,+19最低。上篇曾經說過,內核優先級共有140,而用戶能夠設置的NICE優先級如何與這140個優先級對應起來呢?看代碼:
可以看到,MAX_PRIO就是140,也就是內核定義的最大優先級了。
而MAX_USER_PRIO就是40,意指,普通進程指定的優先級別最多40,就像前面我們講的那樣-20到+19。
nice值是-20表示最高,對應着static_prio是多少呢?NICE_TO_PRIO(0)就是120,NICE_TO_PRIO(-20)就是100。
當該進程剛被其父進程fork出來時,是平分其父進程的剩余時間片的。這個時間片執行完后,就會根據它的初始優先級來重新分配時間片,優先級為+19時最低,只分配最小時間片5ms,優先級為0時是100ms,優先級是-20時是最大時間片800ms。我們看看內核是如何計算時間片長度的,大家先看下task_timeslice時間片計算函數: