大數(shù)據(jù)時(shí)代戈轿,除了基礎(chǔ)配置和安裝,需要性能調(diào)優(yōu)的時(shí)候阵子。不可能永遠(yuǎn)想著換硬件這么low的思想思杯!
以下九點(diǎn),為你闡述基礎(chǔ)的調(diào)優(yōu)概念挠进,希望對(duì)你對(duì)你有益斤儿。
進(jìn)程是什么拄养?
進(jìn)程就是執(zhí)行程序運(yùn)行在處理器上的一個(gè)實(shí)例。進(jìn)程可以使用Linux內(nèi)核所能控制的任何資源來完成它的任務(wù)。
所有運(yùn)行在Linux操作系統(tǒng)上的進(jìn)程都使用一個(gè)名叫task_struct的結(jié)構(gòu)來管理掉缺,這個(gè)結(jié)構(gòu)亦被稱作進(jìn)程描述符【Process Descriptor】。
進(jìn)程描述符包括進(jìn)程運(yùn)行的所有信息如進(jìn)程ID贡蓖、進(jìn)程屬性和構(gòu)建這個(gè)進(jìn)程所需要的資源纺酸。如果你清楚進(jìn)程的結(jié)構(gòu)嘲碱,就能了解到什么對(duì)于進(jìn)程執(zhí)行和效能來說是重要的。圖1-2展現(xiàn)了進(jìn)程結(jié)構(gòu)的概要局蚀。
進(jìn)程生命周期
每個(gè)進(jìn)程都有自己的生命周期如創(chuàng)建悍汛、執(zhí)行、結(jié)束和消除至会。這些階段在系統(tǒng)啟動(dòng)運(yùn)行中會(huì)被重復(fù)無數(shù)次离咐。
因此從性能角度來看進(jìn)程生命周期是極其重要的。
當(dāng)進(jìn)程創(chuàng)建一個(gè)新的進(jìn)程奉件,創(chuàng)建進(jìn)程(父進(jìn)程)發(fā)出fork()系統(tǒng)調(diào)用宵蛀。當(dāng)一個(gè)fork()系統(tǒng)調(diào)用被發(fā)出,它將得到一個(gè)關(guān)于新進(jìn)程(子進(jìn)程)的進(jìn)程描述符并設(shè)置一個(gè)新的進(jìn)程ID县貌。它會(huì)將父進(jìn)程的進(jìn)程描述符中所有數(shù)據(jù)復(fù)制到子進(jìn)程术陶。此時(shí)父進(jìn)程的整個(gè)地址空間并沒有被復(fù)制的,所以父子進(jìn)程會(huì)共享相同的地址空間煤痕。
exec()系統(tǒng)調(diào)用會(huì)復(fù)制一個(gè)新的程序到子進(jìn)程的地址空間梧宫。因?yàn)楦缸舆M(jìn)程共享相同的地址空間,所以當(dāng)新程序?qū)懭霐?shù)據(jù)時(shí)會(huì)導(dǎo)致分頁錯(cuò)誤【page fault】的例外發(fā)生摆碉。這時(shí)候內(nèi)核會(huì)分配給子進(jìn)程一個(gè)新的物理分頁塘匣。
這個(gè)推遲的操作被叫做寫時(shí)復(fù)制【Copy On Write】。子進(jìn)程通常是執(zhí)行自己的程序巷帝,與其父進(jìn)程所執(zhí)行的有所不同忌卤。這樣的操作可以避免沒有必要的系統(tǒng)開銷,因?yàn)閺?fù)制整個(gè)地址空間是一個(gè)非常慢而且效率低的操作楞泼,它會(huì)消耗很多處理器時(shí)間和資源驰徊。
當(dāng)程序執(zhí)行完成時(shí),子進(jìn)程調(diào)用exit()系統(tǒng)調(diào)用結(jié)束堕阔。系統(tǒng)調(diào)用exit()會(huì)釋放進(jìn)程的大部分?jǐn)?shù)據(jù)結(jié)構(gòu)并發(fā)送信號(hào)通知父進(jìn)程棍厂。此時(shí)子進(jìn)程被稱作僵尸進(jìn)程【zombie process】。
在子進(jìn)程使用wait()系統(tǒng)調(diào)用讓父進(jìn)程知道其已經(jīng)結(jié)束之前超陆,子進(jìn)程是不會(huì)被清除的牺弹。當(dāng)父進(jìn)程得到子進(jìn)程結(jié)束的通知后,會(huì)立即清除子進(jìn)程的所有數(shù)據(jù)結(jié)構(gòu)并釋放進(jìn)程描述符侥猬。
線程
線程是由一個(gè)單獨(dú)進(jìn)程產(chǎn)生的執(zhí)行單元例驹。它與同一進(jìn)程中的其他線程并行運(yùn)行。它們能共享同一資源如內(nèi)存退唠、地址空間鹃锈、打開的文件等。它們能訪問同樣一組應(yīng)用數(shù)據(jù)瞧预。線程也被稱作輕量級(jí)進(jìn)程(Light Weight Process LWP)屎债。
因?yàn)樗鼈児蚕碣Y源仅政,線程不可以在同一時(shí)間修改它們共享的資源∨杈裕互斥的實(shí)現(xiàn)圆丹、鎖、序列化等是用戶應(yīng)用的職責(zé)躯喇。
從性能角度來說辫封,創(chuàng)建線程要比創(chuàng)建進(jìn)程的開銷小,因?yàn)榫€程在創(chuàng)建時(shí)不需要復(fù)制資源廉丽。另一方面倦微,進(jìn)程和線程在調(diào)度算法方面上有很多相似的特性。內(nèi)核在處理它們時(shí)都使用類似的方法正压。
在目前Linux的實(shí)現(xiàn)中欣福,線程支持可移植操作系統(tǒng)接口(POSIX)。在Linux操作系統(tǒng)中有幾種線程的實(shí)現(xiàn)焦履。下面列舉幾種最常使用的線程實(shí)現(xiàn)拓劝。
- LinuxThreads
- LinuxThreads自從Linux內(nèi)核2.0就被作為默認(rèn)的線程實(shí)現(xiàn)。但LinuxThread有許多實(shí)現(xiàn)與POSIX標(biāo)準(zhǔn)不兼容嘉裤。Native POSIX Thread Library(NPTL)正在取代LinuxThreads郑临。在未來的Linux企業(yè)發(fā)行版中將不在支持LinuxThreads。
- Native POSIX Thread Libray(NPTL)本地POSIX線程庫(kù)
- NPTL最初是由Red Hat開發(fā)价脾。NPTL與POSIX標(biāo)準(zhǔn)更加兼容牧抵。利用2.6內(nèi)核增強(qiáng)特性如新的系統(tǒng)調(diào)用clone()、信號(hào)處理實(shí)現(xiàn)等侨把,它可以提供較LinuxThreads更好的性能和伸縮性。
- NPTL與LinuxThreads有很多的不兼容之處妹孙。一個(gè)應(yīng)用如果依賴于LinuxThreads秋柄,可能在NPTL實(shí)現(xiàn)中無法工作。
- Next Generation POSIX Thread(NGPT)下一代POSIX線程
- NGPT是IBM開發(fā)的POSIX線程庫(kù)的版本蠢正。目前處于維護(hù)階段骇笔,未來也沒有開發(fā)計(jì)劃。
- 使用LD_ASSUME_KERNEL環(huán)境變量嚣崭,你可以設(shè)定應(yīng)用使用哪個(gè)線程庫(kù)笨触。
進(jìn)程優(yōu)先級(jí)和Nice值
程優(yōu)先級(jí)【Process priority】是一個(gè)數(shù)值,用來讓CPU根據(jù)動(dòng)態(tài)優(yōu)先級(jí)和靜態(tài)優(yōu)先級(jí)來決定進(jìn)程執(zhí)行的順序雹舀。一個(gè)高優(yōu)先級(jí)的進(jìn)程可以獲得更多在處理器上運(yùn)行的機(jī)會(huì)芦劣。
內(nèi)核會(huì)根據(jù)進(jìn)程的行為和特性使用試探算法【Heuristic Algorithm】來動(dòng)態(tài)調(diào)高和調(diào)低動(dòng)態(tài)優(yōu)先級(jí)。
用戶進(jìn)程可以通過進(jìn)程Nice的值間接改變靜態(tài)優(yōu)先級(jí)说榆。靜態(tài)優(yōu)先級(jí)高的進(jìn)程可以獲得較長(zhǎng)的時(shí)間片【Time Slice】(進(jìn)程能運(yùn)行在處理器有多長(zhǎng)時(shí)間)虚吟。
Linux中Nice值范圍為19(最低優(yōu)先級(jí))到-20(最高優(yōu)先級(jí))寸认,默認(rèn)值為0。要將Nice值更改為負(fù)數(shù)串慰,必須通過登錄或使用su命令由root執(zhí)行偏塞。
上下文交換【Context switching】
在進(jìn)程執(zhí)行過程中,進(jìn)程信息存儲(chǔ)在處理器的寄存器和緩存中邦鲫。這組為執(zhí)行中進(jìn)程而載入寄存器的數(shù)據(jù)被稱作上下文【Context】灸叼。
為切換進(jìn)程,當(dāng)前執(zhí)行中進(jìn)程的上下文會(huì)被暫存庆捺,下一個(gè)執(zhí)行進(jìn)程的上下文會(huì)被還原到寄存器怜姿,進(jìn)程描述符和內(nèi)核模式堆棧【Kernel mode stack】的區(qū)塊會(huì)被用來存儲(chǔ)上下文疼燥,這個(gè)交換過程被叫做上下文交換【Context Switching】沧卢。
發(fā)生過多的上下文交換是不好的,因?yàn)樘幚砥髅看味家⑿录拇嫫骱途彺鏋樾逻M(jìn)程讓出資源醉者,這會(huì)導(dǎo)致性能上的問題但狭。
中斷處理
中斷處理是優(yōu)先級(jí)最高的任務(wù)之一。中斷通常是由I/O設(shè)備產(chǎn)生的如網(wǎng)卡撬即、鍵盤立磁、硬盤控制器、串行適配器等剥槐。
中斷控制會(huì)向內(nèi)核發(fā)送一個(gè)事件通知(如鍵盤輸入唱歧、以太幀到達(dá)等),它指示內(nèi)核中斷執(zhí)行中的進(jìn)程并盡快處理中斷粒竖,因?yàn)榇蠖鄶?shù)設(shè)備需要快速的回應(yīng)颅崩,這對(duì)系統(tǒng)性能是很關(guān)鍵的。當(dāng)一個(gè)中斷信號(hào)到達(dá)內(nèi)核時(shí)蕊苗,內(nèi)核必須切換當(dāng)前執(zhí)行的進(jìn)程到處理中斷的新進(jìn)程沿后,這意味著中斷會(huì)觸發(fā)上下文交換,因此大量的中斷可以導(dǎo)致系統(tǒng)性能的下降朽砰。
在Linux中尖滚,有兩種類型的中斷。一種為硬中斷【Hard Interrupt】瞧柔,是由需要回應(yīng)的設(shè)備產(chǎn)生的(硬盤I/O中斷漆弄、網(wǎng)絡(luò)適配器中斷,鍵盤中斷造锅,鼠標(biāo)中斷)撼唾。另一種為軟中斷【Soft Interrupt】,用于可以延后執(zhí)行的任務(wù)(TCP/IP操作备绽,SCSI協(xié)議操作等)券坞。你可以在/proc/interrupts中查看到有關(guān)硬中斷的信息鬓催。
在多處理器環(huán)境下,每個(gè)處理器都可以用來處理中斷恨锚。將中斷綁定到某一個(gè)物理處理可以提升系統(tǒng)的性能宇驾。
進(jìn)程狀態(tài)
每個(gè)進(jìn)程都有它自己的狀態(tài),來顯示進(jìn)程當(dāng)前的情況猴伶。在進(jìn)程執(zhí)行過程中其狀態(tài)會(huì)發(fā)生變化课舍。可能狀態(tài)有如下幾種:
- TASK_RUNNING【運(yùn)行中】
- 此狀態(tài)表示進(jìn)程正運(yùn)行在CPU上或在隊(duì)列中等待運(yùn)行(運(yùn)行隊(duì)列【Run Queue】)他挎。
- ? TASK_STOPPED【停止】
- 當(dāng)進(jìn)程接收到某些信號(hào)(例如SIGINT筝尾、SIGSTOP)后被暫停就處于此種狀態(tài),該等待的進(jìn)程在收到恢復(fù)信號(hào)如SIGCONT后會(huì)重新投入運(yùn)行办桨。
- TASK_INTERRUPTIBLE【可中斷】
- 在這種狀態(tài)下筹淫,進(jìn)程被暫停運(yùn)行,等待某些狀態(tài)的達(dá)成呢撞。如果一個(gè)處于可中斷狀態(tài)的進(jìn)程收到停止的信號(hào)损姜,將變更進(jìn)程的狀態(tài)并中斷操作∈庀迹可中斷狀態(tài)進(jìn)程的一個(gè)典型例子就是等待鍵盤的輸入摧阅。
- ? TASK_UNINTERRUPTIBLE【不可中斷】
- 此狀態(tài)基本上與可中斷狀態(tài)十分相似。但可中斷狀態(tài)進(jìn)程可以被中斷绷蹲,而向一個(gè)不可中斷進(jìn)程發(fā)送信號(hào)卻不會(huì)有任何反應(yīng)棒卷。不可中斷狀態(tài)進(jìn)程的一個(gè)典型例子就是等待硬盤I/O操作。
- TASK_ZOMBIE【僵尸】
在進(jìn)程使用系統(tǒng)調(diào)用exit()退出后祝钢,其父進(jìn)程就會(huì)知道比规。僵尸狀態(tài)的進(jìn)程會(huì)等待父進(jìn)程通知其釋放所有的數(shù)據(jù)結(jié)構(gòu)。
進(jìn)程內(nèi)存段
一個(gè)進(jìn)程需要使用自己的內(nèi)存區(qū)域來執(zhí)行工作太颤。工作的變化隨情況和進(jìn)程用法而定苞俘。
一個(gè)進(jìn)程可以有不同的工作負(fù)載特性和不同的數(shù)據(jù)大小的需求,進(jìn)程需要處理數(shù)據(jù)的大小多種多樣龄章。為了滿足這樣的需求,Linux內(nèi)核使用動(dòng)態(tài)內(nèi)存分配機(jī)制乞封。
- 文字段
- 用來存儲(chǔ)執(zhí)行代碼做裙。
- ? 數(shù)據(jù)段
- 數(shù)據(jù)段由三塊區(qū)域組成。
- ―數(shù)據(jù)【Data】:存儲(chǔ)已初始化數(shù)據(jù)如靜態(tài)變量肃晚。
- ― BSS :存儲(chǔ)零初始化的數(shù)據(jù)锚贱,數(shù)據(jù)被初始化為零。
- ― 堆【Heap】:這塊區(qū)域由malloc()用來按需要分配動(dòng)態(tài)內(nèi)存关串。堆向高地址擴(kuò)張拧廊。
- 堆棧段
- 用來存儲(chǔ)本地變量监徘、函數(shù)參數(shù)、函數(shù)返回地址吧碾。堆棧段向低地址擴(kuò)張凰盔。
Linux CPU調(diào)度器
計(jì)算機(jī)的基本功能非常簡(jiǎn)單就是計(jì)算。為了計(jì)算倦春,這就意味要管理計(jì)算資源或處理器和計(jì)算任務(wù)(被稱作線程或進(jìn)程)户敬。
Linux內(nèi)核使用與過去CPU調(diào)度器使用的算法O(n)截然不同的O(1)算法,這要感謝Lngo Molnar的巨大貢獻(xiàn)。O(1)指的是一種靜態(tài)算法睁本,意思就是不管進(jìn)程的數(shù)量有多少尿庐,進(jìn)程的執(zhí)行時(shí)間都是不變的。
這種新的調(diào)度器的擴(kuò)展性非常好呢堰,不管進(jìn)程的數(shù)量或處理器的數(shù)量有多少抄瑟,系統(tǒng)的開銷都是非常小的。此算法中使用到兩個(gè)進(jìn)程優(yōu)先級(jí)數(shù)組:
- 活動(dòng)的【Active】
- 過期的【Expired】
調(diào)度器根據(jù)進(jìn)程的優(yōu)先級(jí)和優(yōu)先攔截率【Prior Blocking Rate】分配時(shí)間片枉疼,然后它們被以優(yōu)先級(jí)順序置于活動(dòng)數(shù)組【Active Array】中皮假。
當(dāng)時(shí)間片耗盡,它們會(huì)被分配一個(gè)新的時(shí)間片并置于過期數(shù)組中往衷。當(dāng)活動(dòng)數(shù)組中所有進(jìn)程的時(shí)間片都全部耗盡钞翔,兩個(gè)數(shù)組會(huì)被互換并重新執(zhí)行。
對(duì)于交互進(jìn)程(相對(duì)于實(shí)時(shí)進(jìn)程)席舍,擁有長(zhǎng)時(shí)間片的高優(yōu)先級(jí)進(jìn)程可以得到比低優(yōu)先級(jí)進(jìn)程更多的時(shí)間布轿,但這并不意味著低優(yōu)先級(jí)的進(jìn)程會(huì)被置之不理。
在企業(yè)環(huán)境中来颤,擁有很多的處理器并經(jīng)常出現(xiàn)大量的線程和進(jìn)程汰扭,這樣做可以大大提升Linux內(nèi)核的伸縮性。
新的O(1)CPU調(diào)度器被設(shè)計(jì)用于2.6內(nèi)核福铅,但已被移植到2.4內(nèi)核家族萝毛。
新調(diào)度器另一個(gè)大的改進(jìn)就是支持非一致性內(nèi)存架構(gòu)(NUMA)和對(duì)稱多線程處理器,如Intel超線程技術(shù)滑黔。
改良后的NUMA支持確保只有當(dāng)某個(gè)節(jié)點(diǎn)過載時(shí)笆包,負(fù)載平衡才會(huì)跨越NUMA節(jié)點(diǎn)。
盡管在每個(gè)調(diào)度節(jié)拍【tick】時(shí)負(fù)載平衡會(huì)遍歷調(diào)度域群組【Scheduler Domain Group】中的處理器略荡,但只有在節(jié)點(diǎn)過載并請(qǐng)求負(fù)載平衡時(shí)庵佣,負(fù)載才會(huì)跨越調(diào)度域【Scheduler Domain】轉(zhuǎn)移。
希望這篇文章汛兜,對(duì)你有幫助巴粪。