
1、馮諾依曼體系結(jié)構(gòu)我們常用的計算機服務(wù)器等大部分都遵守馮諾依曼體系。關(guān)于馮諾依曼必須強調(diào)幾點這里的存儲器指的是內(nèi)存不考慮緩存情況這里的CPU能且只能對內(nèi)存進行讀寫不能訪問外設(shè)(輸入或輸出設(shè)備)(數(shù)據(jù)層面)外設(shè)(輸入或輸出設(shè)備)要輸入或者輸出數(shù)據(jù)也只能寫入內(nèi)存或者從內(nèi)存中讀取。?句話所有設(shè)備都只能直接和內(nèi)存打交道。2、操作系統(tǒng)任何計算機系統(tǒng)都包含?個基本的程序集合稱為操作系統(tǒng)(OS)?;\統(tǒng)的理解操作系統(tǒng)包括內(nèi)核進程管理內(nèi)存管理文件管理驅(qū)動管理其他程序例如函數(shù)庫shell程序等等設(shè)計OS的目的對下與硬件交互管理所有的軟硬件資源對上為用戶程序應(yīng)用程序提供?個良好的執(zhí)行環(huán)境3、進程進程 內(nèi)核數(shù)據(jù)結(jié)構(gòu)(task_struct) 自己的程序代碼和數(shù)據(jù)3.1 描述進程-PCB基本概念進程信息被放在?個叫做進程控制塊的數(shù)據(jù)結(jié)構(gòu)中可以理解為進程屬性的集合。課本上稱之為PCBprocess control block Linux 操作系統(tǒng)下的 PCB 是: task_structtask_struct是PCB的?種在 Linux 中描述進程的結(jié)構(gòu)體叫做 task_struct 。task_struct 是 Linux 內(nèi)核的?種數(shù)據(jù)結(jié)構(gòu)類型它會被裝載到RAM(內(nèi)存)里并且包含著進程的信息。3.2 task_ struct內(nèi)容分類標示符: 描述本進程的唯?標示符用來區(qū)別其他進程。狀態(tài): 任務(wù)狀態(tài)退出代碼退出信號等。優(yōu)先級: 相對于其他進程的優(yōu)先級。程序計數(shù)器: 程序中即將被執(zhí)行的下?條指令的地址。內(nèi)存指針: 包括程序代碼和進程相關(guān)數(shù)據(jù)的指針還有和其他進程共享的內(nèi)存塊的指針上下文數(shù)據(jù): 進程執(zhí)行時處理器的寄存器中的數(shù)據(jù)I ∕ O狀態(tài)信息: 包括顯示的I/O請求,分配給進程的I∕O設(shè)備和被進程使用的文件列表。記賬信息: 可能包括處理器時間總和使用的時鐘數(shù)總和時間限制記賬號等。其他信息所有運行在系統(tǒng)里的進程都以 task_struct 雙鏈表的形式存在內(nèi)核里。3.3 查看進程進程的信息可以通過 /proc 系統(tǒng)文件夾查看大多數(shù)進程信息同樣可以使用top和ps這些用戶級?具來獲取3.4 通過系統(tǒng)調(diào)用創(chuàng)建進程-fork初識fork有兩個返回值 父子進程代碼共享數(shù)據(jù)各自開辟空間私有一份采用寫時拷貝1. fork 為什么會有兩個返回值不是一個函數(shù)返回兩次而是調(diào)用 fork 之后操作系統(tǒng)創(chuàng)建了一個新進程變成兩個進程各自執(zhí)行 fork 之后的代碼各自拿到一份返回值。調(diào)用fork()之前只有父進程這 1 個進程在運行。調(diào)用fork()的一瞬間OS 復(fù)制父進程創(chuàng)建出子進程子進程幾乎完整拷貝父進程的地址空間、代碼、數(shù)據(jù)。fork系統(tǒng)調(diào)用在父進程里返回一次同時在新建出來的子進程里再返回一次。所以代碼上看起來同一個fork調(diào)用出現(xiàn)兩個返回值。本質(zhì)是兩個獨立進程各自拿到返回值。2. 兩個返回值分別給誰父進程fork返回 0 的正數(shù)這個數(shù)字就是子進程的 PID進程號。子進程fork返回0。出錯情況fork 失敗進程數(shù)量上限等返回-1此時不會創(chuàng)建子進程。3.同一個變量 ret為什么 if 和 else if 可以同時成立很多初學(xué)者會疑惑ret 一個變量怎么if(ret0)和else if(ret0)都執(zhí)行真相不是同一個變量fork創(chuàng)建子進程時會拷貝父進程的內(nèi)存子進程擁有屬于自己獨立的地址空間。父進程內(nèi)存里的ret存的是子進程 PID正數(shù)子進程內(nèi)存里的ret存的是 0兩個進程兩份獨立的 ret 變量各自走自己的分支。父進程走else if(ret0)子進程走if(ret0)兩個分支分別在兩個進程里面運行互不干擾。并不是單進程里面一個變量同時等于 0 又大于 0。3.5 進程狀態(tài)?個進程可以有幾個狀態(tài)在Linux內(nèi)核里進程有時候也叫做任務(wù)R運行狀態(tài)running: 并不意味著進程?定在運行中它表明進程要么是在運行中要么在運行隊列里。S睡眠狀態(tài)sleeping): 意味著進程在等待事件完成這里的睡眠有時候也叫做可中斷睡眠interruptible sleep。D磁盤休眠狀態(tài)Disk sleep有時候也叫不可中斷睡眠狀態(tài)uninterruptible sleep在這個狀態(tài)的進程通常會等待IO的結(jié)束。T停止狀態(tài)stopped 可以通過發(fā)送 SIGSTOP 信號給進程來停止T進程。這個被暫停的進程可以通過發(fā)送 SIGCONT 信號讓進程繼續(xù)運行。X死亡狀態(tài)dead這個狀態(tài)只是?個返回狀態(tài)你不會在任務(wù)列表里看到這個狀態(tài)。進程狀態(tài)查看ps aux / ps axj 命令a顯示?個終端所有的進程包括其他用戶的進程。x顯示沒有控制終端的進程例如后臺運行的守護進程。j顯示進程歸屬的進程組ID、會話ID、父進程ID以及與作業(yè)控制相關(guān)的信息u以用戶為中心的格式顯示進程信息提供進程的詳細信息如有用戶、CPU和內(nèi)存使用情況等3.51 僵尸進程僵死狀態(tài)Zombies是一個比較特殊的狀態(tài)。當(dāng)進程退出并且父進程沒有讀取到子進程退出的返回代碼時就會產(chǎn)生僵死進程僵死進程會以終止狀態(tài)保持在進程表中并且會?直在等待父進程讀取退出狀態(tài)代碼因為需要告訴父進程任務(wù)進行得怎么樣。所以只要子進程退出父進程還在運行但父進程沒有讀取子進程狀態(tài)子進程進入Z狀態(tài)僵尸進程的危害僵尸進程已釋放用戶態(tài)內(nèi)存但內(nèi)核中 task_struct 等 PCB 資源未釋放會造成內(nèi)核內(nèi)存資源泄漏。它持續(xù)占用 PID 與內(nèi)核進程表項大量堆積會耗盡 PID導(dǎo)致系統(tǒng)無法創(chuàng)建新進程。大量僵尸進程還會干擾進程排查掩蓋系統(tǒng)其他異常問題。3.52 孤兒進程問題父進程如果提前退出那么子進程后退出進入Z之后那該如何處理呢父進程先退出子進程就稱之為“孤兒進程”孤兒進程被1號init / systemd進程領(lǐng)養(yǎng)當(dāng)然要有init / systemd進程回收模擬孤兒進程3.6 進程優(yōu)先級cpu資源分配的先后順序就是指進程的優(yōu)先權(quán)priority。優(yōu)先權(quán)高的進程有優(yōu)先執(zhí)行權(quán)利。配置進程優(yōu)先權(quán)對多任務(wù)環(huán)境的linux很有用可以改善系統(tǒng)性能。還可以把進程運行到指定的CPU上這樣?來把不重要的進程安排到某個CPU可以改善系統(tǒng)整體性能。3.61 查看系統(tǒng)進程在linux或者unix系統(tǒng)中用ps ?l命令則會類似輸出以下幾個內(nèi)容我們很容易注意到其中的幾個重要信息有下UID : 代表執(zhí)行者的身份PID : 代表這個進程的代號PPID 代表這個進程是由哪個進程發(fā)展衍生而來的亦即父進程的代號PRI 代表這個進程可被執(zhí)行的優(yōu)先級其值越小越早被執(zhí)行NI 代表這個進程的nice值3.62 PRI and NIPRI也還是比較好理解的即進程的優(yōu)先級或者通俗點說就是程序被CPU執(zhí)行的先后順序此值越小進程的優(yōu)先級別越高那NI呢?就是我們所要說的nice值了其表示進程可被執(zhí)行的優(yōu)先級的修正數(shù)值PRI值越小越快被執(zhí)行那么加入nice值后將會使得PRI變?yōu)镻RI(new)PRI(old)nice這樣當(dāng)nice值為負值的時候那么該程序?qū)?yōu)先級值將變小即其優(yōu)先級會變高則其越快被執(zhí)行所以調(diào)整進程優(yōu)先級在Linux下就是調(diào)整進程nice值nice其取值范圍是-20至19?共40個級別。3.63 PRI vs NI需要強調(diào)?點的是進程的nice值不是進程的優(yōu)先級他們不是?個概念但是進程nice值會影響到進程的優(yōu)先級變化??梢岳斫鈔ice值是進程優(yōu)先級的修正數(shù)據(jù)3.64 查看進程優(yōu)先級的命令?top命令更改已存在進程的nicetop進入top后按“r”?輸入進程PID?輸入nice值3.7 進程切換CPU上下文切換其實際含義是任務(wù)切換, 或者CPU寄存器切換。當(dāng)多任務(wù)內(nèi)核決定運行另外的任務(wù)時, 它保存正在運行任務(wù)的當(dāng)前狀態(tài), 也就是CPU寄存器中的全部內(nèi)容。這些內(nèi)容被保存在任務(wù)自己的堆棧中, 入棧工作作完成后就把下?個將要運行的任務(wù)的當(dāng)前狀況從該任務(wù)的棧中重新裝入CPU寄存器,并開始下?個任務(wù)的運行, 這?過程就是context switch。每個進程都會分配對應(yīng)的時間片如果沒在時間片指定的時間內(nèi)完成任務(wù)該進程會被暫時切下來需要進程自己保存自己得上下文數(shù)據(jù)目的是下次回來能夠恢復(fù)按照之前得邏輯向后運行。3.8 Linux內(nèi)核進程O(1)調(diào)度隊列時間片還沒有結(jié)束的所有進程都按照優(yōu)先級放在該隊列nr_active: 總共有多少個運行狀態(tài)的進程queue[140]: ?個元素就是?個進程隊列相同優(yōu)先級的進程按照FIFO規(guī)則進行排隊調(diào)度,所以數(shù)組下標就是優(yōu)先級從該結(jié)構(gòu)中選擇?個最合適的進程過程是怎么的呢從0下表開始遍歷queue[140]找到第?個非空隊列該隊列必定為優(yōu)先級最高的隊列拿到選中隊列的第?個進程開始運行調(diào)度完成遍歷queue[140]時間復(fù)雜度是常數(shù)bitmap[5]:?共140個優(yōu)先級?共140個進程隊列為了提高查找非空隊列的效率就可以用5*32個比特位表示隊列是否為空3.81 過期隊列過期隊列和活動隊列結(jié)構(gòu)?模?樣過期隊列上放置的進程都是時間片耗盡的進程當(dāng)活動隊列上的進程都被處理完畢之后對過期隊列的進程進行時間片重新計算3.82 active指針和expired指針active指針永遠指向活動隊列expired指針永遠指向過期隊列可是活動隊列上的進程會越來越少過期隊列上的進程會越來越多因為進程時間片到期時?直都存在的。新加進來的進程也會放在過期隊列里面相當(dāng)于進程狀態(tài)中的就緒狀態(tài)只要能夠交換active指針和expired指針的內(nèi)容就相當(dāng)于有具有了?批新的活動進程4、調(diào)度算法為o(1)原因從 active 選最高優(yōu)先級進程運行進程時間片用完把它挪到 expired 隊列active 全部空了的時候直接交換 active 和 expired 指針不需要移動任何進程只是交換兩個指針變量常數(shù)操作。