程概念:從馮諾依曼體系到Linux進(jìn)程管理)
1. 馮諾依曼體系結(jié)構(gòu)現(xiàn)代計(jì)算機(jī)的硬件基礎(chǔ)馮諾依曼體系結(jié)構(gòu)是現(xiàn)代計(jì)算機(jī)的硬件體系結(jié)構(gòu)它將計(jì)算機(jī)硬件劃分為五大類輸入設(shè)備如鍵盤、鼠標(biāo)、掃描儀等輸出設(shè)備如顯示器、打印機(jī)、音響等存儲(chǔ)器內(nèi)存主存和外部存儲(chǔ)器運(yùn)算器執(zhí)行算術(shù)和邏輯運(yùn)算控制器協(xié)調(diào)和控制計(jì)算機(jī)各部件工作在計(jì)算機(jī)中數(shù)據(jù)流是圍繞內(nèi)存工作的。這意味著CPU要處理任意數(shù)據(jù)都需要先把數(shù)據(jù)放到內(nèi)存中。因此要運(yùn)行一個(gè)程序先把這個(gè)程序的指令數(shù)據(jù)加載到內(nèi)存中。2. 操作系統(tǒng)的功能層次劃分2.1 操作系統(tǒng)是什么操作系統(tǒng)是一個(gè)軟件負(fù)責(zé)對(duì)計(jì)算機(jī)上的軟硬件資源進(jìn)行管理。2.2 系統(tǒng)調(diào)用接口操作系統(tǒng)內(nèi)核提供給上層用于進(jìn)行計(jì)算機(jī)操作的接口。2.3 庫(kù)函數(shù)庫(kù)函數(shù)是對(duì)系統(tǒng)調(diào)用接口進(jìn)行的二次封裝接口如write、printf等。庫(kù)函數(shù)與系統(tǒng)調(diào)用接口的關(guān)系庫(kù)函數(shù)內(nèi)部調(diào)用了系統(tǒng)調(diào)用接口。3. 進(jìn)程概念3.1 不同角度的進(jìn)程定義站在用戶的角度進(jìn)程就是運(yùn)行中的程序站在系統(tǒng)的角度進(jìn)程就是對(duì)于程序的動(dòng)態(tài)運(yùn)行中描述這個(gè)描述在Linux下是一個(gè)結(jié)構(gòu)體struct task_struct{ }3.2 進(jìn)程描述了什么信息進(jìn)程控制塊PCB中包含了豐富的信息標(biāo)識(shí)符進(jìn)程IDpid進(jìn)程調(diào)度信息描述了如何調(diào)度一個(gè)程序的運(yùn)行進(jìn)程狀態(tài)對(duì)進(jìn)程不同運(yùn)行狀態(tài)進(jìn)行描述讓進(jìn)程在合適的時(shí)候做合適的事情進(jìn)程的內(nèi)存管理進(jìn)程內(nèi)部所有數(shù)據(jù)都會(huì)占據(jù)內(nèi)存了解進(jìn)程內(nèi)部?jī)?nèi)存管理方式內(nèi)存內(nèi)打開的文件信息通過(guò)了解這塊信息更加深入地了解系統(tǒng)調(diào)用I/O進(jìn)程間通信資源管理學(xué)習(xí)進(jìn)程間通信進(jìn)程信號(hào)信息描述進(jìn)程的軟中斷相關(guān)內(nèi)容4. 進(jìn)程標(biāo)識(shí)符與調(diào)度4.1 標(biāo)識(shí)符進(jìn)程ID進(jìn)程ID其實(shí)就是一個(gè)數(shù)字。在Linux下可以通過(guò)ps指令查看進(jìn)程信息ps -ef4.2 進(jìn)程的調(diào)度并行多個(gè)進(jìn)程可以同時(shí)運(yùn)行串行有多個(gè)進(jìn)程但是同一時(shí)間只能運(yùn)行一個(gè)并行與串行在計(jì)算機(jī)上取決于CPU核心數(shù)量每個(gè)CPU核心上都有一套獨(dú)立的寄存器保存程序運(yùn)行的上下文信息來(lái)執(zhí)行指令。計(jì)算機(jī)上運(yùn)行的程序非常多但是CPU核心很少如何讓它們同時(shí)運(yùn)行CPU分時(shí)機(jī)制一個(gè)程序運(yùn)行的時(shí)候只能在CPU上運(yùn)行很短的一段時(shí)間時(shí)間片時(shí)間片運(yùn)行完畢則切換下一個(gè)進(jìn)程運(yùn)行分時(shí)機(jī)制實(shí)現(xiàn)了多個(gè)程序在宏觀上的并行但我們要理解微觀上它們實(shí)際上是串行的一旦程序是切換運(yùn)行的等切換回來(lái)的時(shí)候CPU又怎么知道該從哪里開始執(zhí)行為了解決這個(gè)問(wèn)題在struct task_structPCB-進(jìn)程控制塊中就保存了一系列程序的運(yùn)行上下文pc寄存器內(nèi)容即將要執(zhí)行的指令地址一系列上下文數(shù)據(jù)正在處理的數(shù)據(jù)即將要處理的數(shù)據(jù)......因此一旦CPU切換輪轉(zhuǎn)回來(lái)了就可以通過(guò)pid找到task_struct從其中取出這些上下文數(shù)據(jù)重新放到CPU寄存器中CPU就可以從原來(lái)的位置繼續(xù)向下執(zhí)行處理原來(lái)沒(méi)有處理完的數(shù)據(jù)。5. 進(jìn)程狀態(tài)5.1 三態(tài)模型就緒、運(yùn)行、阻塞5.2 Linux下的進(jìn)程狀態(tài)運(yùn)行態(tài)就緒運(yùn)行拿到時(shí)間片就能運(yùn)行以及正在運(yùn)行的都屬于運(yùn)行態(tài)??芍袛嘈菝邞B(tài)可以被打斷的阻塞狀態(tài)阻塞態(tài)都有一個(gè)喚醒條件只有喚醒條件達(dá)成的時(shí)候才會(huì)轉(zhuǎn)為就緒態(tài)。不可中斷休眠態(tài)只能被條件喚醒不能被中斷打斷的阻塞狀態(tài)。停止態(tài)讓進(jìn)程停止運(yùn)行只能通過(guò)特定方式喚醒。追蹤態(tài)調(diào)試的時(shí)候涉及到的狀態(tài)。死亡態(tài)進(jìn)程退出后資源全部釋放完畢處于的一個(gè)短暫的狀態(tài)。僵尸態(tài)一個(gè)進(jìn)程退出后但是資源沒(méi)有完全被釋放的狀態(tài)。5.3 僵尸態(tài)詳解產(chǎn)生原因由于子進(jìn)程退出但是父進(jìn)程沒(méi)有關(guān)注子進(jìn)程的退出狀態(tài)沒(méi)有等待也沒(méi)有忽略SIGCHLD信號(hào)所導(dǎo)致的。SIGCHLD信號(hào)就是子進(jìn)程退出時(shí)操作系統(tǒng)給父進(jìn)程發(fā)送的信號(hào)。忽略這個(gè)信號(hào)就是告訴操作系統(tǒng)子進(jìn)程退出直接釋放資源我不管他的返回值。危害會(huì)造成系統(tǒng)資源泄露。解決kill -9強(qiáng)制殺死。避免進(jìn)程等待wait/waitpid等待子進(jìn)程退出獲取他的返回值?;蛘吆雎許IGCHLD信號(hào)。6. 進(jìn)程的內(nèi)存管理程序地址空間6.1 程序地址空間概念程序地址空間進(jìn)程的虛擬地址空間。進(jìn)程的地址空間是虛擬的地址空間并不是給每個(gè)進(jìn)程都分配了4G內(nèi)存。程序地址空間實(shí)際上是系統(tǒng)給我們進(jìn)程所描述的一個(gè)虛擬的地址空間并不是分配了這么多內(nèi)存而是虛擬的地址分配。這個(gè)描述在pcb中是mm_struct結(jié)構(gòu)體。6.2 虛擬地址空間作用實(shí)現(xiàn)數(shù)據(jù)在物理內(nèi)存中的離散式存儲(chǔ)提高內(nèi)存利用率并進(jìn)行內(nèi)存訪問(wèn)控制提高進(jìn)程獨(dú)立性。6.3 虛擬地址的實(shí)現(xiàn)虛擬地址的實(shí)現(xiàn)依賴了一個(gè)關(guān)鍵性技術(shù)頁(yè)表內(nèi)存的分頁(yè)式管理。頁(yè)表信息虛擬地址和物理地址的映射關(guān)系訪問(wèn)權(quán)限缺頁(yè)中斷標(biāo)志......虛擬地址組成頁(yè)號(hào)針對(duì)頁(yè)面的編號(hào) 頁(yè)內(nèi)偏移32位系統(tǒng)下地址大小4字節(jié)32個(gè)比特位頁(yè)號(hào)占據(jù)地址中的高20位頁(yè)內(nèi)偏移占據(jù)地址中的低12位6.4 進(jìn)程的獨(dú)立性每個(gè)進(jìn)程操作系統(tǒng)都會(huì)為該進(jìn)程創(chuàng)建task_struct(pcb)其中就包含虛擬地址空間描述和頁(yè)表信息。因此每個(gè)進(jìn)程內(nèi)部數(shù)據(jù)存儲(chǔ)所分配的地址都是虛擬地址。這也是為什么進(jìn)程間通信需要操作系統(tǒng)提供進(jìn)程間通信資源的原因——讓操作系統(tǒng)給多個(gè)不同的進(jìn)程分一塊大家都能訪問(wèn)的空間。6.5 內(nèi)存置換當(dāng)內(nèi)存不夠用了操作系統(tǒng)如何處理內(nèi)存置換操作系統(tǒng)認(rèn)為內(nèi)存中的很多數(shù)據(jù)并不是一直在訪問(wèn)熱數(shù)據(jù)。因此當(dāng)內(nèi)存不夠用的時(shí)候操作系統(tǒng)就會(huì)根據(jù)一定的算法將指定內(nèi)存中的數(shù)據(jù)置換出去存放到硬盤中。存放的這塊硬盤區(qū)域有個(gè)專業(yè)名稱交換分區(qū)。常見(jiàn)置換算法LRU最久未使用LFU最少未使用FIFO先進(jìn)先出6.6 缺頁(yè)中斷當(dāng)訪問(wèn)內(nèi)存數(shù)據(jù)的時(shí)候通過(guò)頁(yè)表將虛擬地址轉(zhuǎn)換為物理地址但是轉(zhuǎn)換的時(shí)候發(fā)現(xiàn)頁(yè)表中置位了缺頁(yè)中斷位當(dāng)前這個(gè)虛擬地址對(duì)應(yīng)的那塊內(nèi)存數(shù)據(jù)沒(méi)有在內(nèi)存中。缺頁(yè)中斷的處理從交換分區(qū)將數(shù)據(jù)重新置換到內(nèi)存中更新頁(yè)表信息。6.7 分段式與段頁(yè)式內(nèi)存管理分段式內(nèi)存管理將代碼分為代碼段、數(shù)據(jù)段等對(duì)于程序的內(nèi)存管理比較友好。段頁(yè)式內(nèi)存管理對(duì)內(nèi)存分段在每個(gè)段內(nèi)進(jìn)行分頁(yè)管理。7. 進(jìn)程控制創(chuàng)建、退出、等待、程序替換7.1 創(chuàng)建pid_t fork()功能通過(guò)復(fù)制父進(jìn)程的方式創(chuàng)建一個(gè)子進(jìn)程。復(fù)制了什么緩沖區(qū)調(diào)度切換相關(guān)的上下文信息虛擬地址空間頁(yè)表信息文件描述符信息......pid_t pid fork(); if (pid 0) { // 錯(cuò)誤處理 } else if (pid 0) { // 子進(jìn)程運(yùn)行的代碼 } else { // 父進(jìn)程運(yùn)行的代碼 }1. 復(fù)制了虛擬地址空間復(fù)制處理的子進(jìn)程要執(zhí)行的代碼、要處理的數(shù)據(jù)跟父進(jìn)程是一樣的。創(chuàng)建子進(jìn)程這里使用了寫時(shí)拷貝技術(shù)創(chuàng)建子進(jìn)程后子進(jìn)程與父進(jìn)程指向同一塊內(nèi)存區(qū)但是當(dāng)任意一方對(duì)一塊內(nèi)存區(qū)域數(shù)據(jù)進(jìn)行修改則給當(dāng)前進(jìn)程重新開辟一塊空間拷貝新數(shù)據(jù)進(jìn)去。這大大提高了創(chuàng)建子進(jìn)程的效率。2. 復(fù)制了程序調(diào)度上下文信息pc寄存器保存的是即將要執(zhí)行的指令地址程序運(yùn)行到了哪里以及其他切換信息正在執(zhí)行的指令正在處理的數(shù)據(jù)...7.2 退出終止程序的運(yùn)行正常退出在main中return在任意位置調(diào)用exit()函數(shù)——exit()函數(shù)是一個(gè)庫(kù)函數(shù)在任意位置調(diào)用_exit()函數(shù)——_exit()函數(shù)是一個(gè)系統(tǒng)調(diào)用函數(shù)庫(kù)函數(shù)是對(duì)系統(tǒng)調(diào)用函數(shù)在特定場(chǎng)景下的一些功能補(bǔ)充庫(kù)函數(shù)內(nèi)部調(diào)用了系統(tǒng)調(diào)用函數(shù)。庫(kù)函數(shù)在exit退出的時(shí)候會(huì)進(jìn)行用戶態(tài)空間的一些資源清理操作例如緩沖區(qū)的數(shù)據(jù)刷新。異常退出程序在運(yùn)行中的時(shí)候遇到了某種異常內(nèi)存訪問(wèn)錯(cuò)誤導(dǎo)致程序崩潰。異常退出的情況下進(jìn)程的返回值是沒(méi)有參考意義的。7.3 等待父進(jìn)程創(chuàng)建子進(jìn)程之后等待子進(jìn)程退出為什么要等待捕獲子進(jìn)程的退出狀態(tài)獲取他的返回值釋放子進(jìn)程所有資源避免產(chǎn)生僵尸進(jìn)程。pid_t wait(int *wstatus); pid_t waitpid(pid_t pid, int *wstatus, int options);wstatus內(nèi)部包含兩個(gè)信息1. 進(jìn)程的退出原因2. 進(jìn)程的退出碼處理邏輯先取低7位判斷是否為0為0則表示正常退出status 0x7f再取低16位中的高8位當(dāng)作退出碼進(jìn)行處理(status 8) 0xff。7.4 程序替換默認(rèn)創(chuàng)建子進(jìn)程之后子進(jìn)程與父進(jìn)程的代碼段是一樣的可以通過(guò)fork返回值進(jìn)行分支控制但是這樣會(huì)導(dǎo)致兩個(gè)不同的功能放在一個(gè)程序中實(shí)現(xiàn)代碼較為臃腫。如何讓子進(jìn)程重新運(yùn)行另一個(gè)程序呢使用程序替換將新的程序指令 數(shù)據(jù)加載到內(nèi)存中。將子進(jìn)程的頁(yè)表映射信息初始化并更新映射到新的內(nèi)存區(qū)域加載到內(nèi)存的新程序。初始化調(diào)度上下文信息讓當(dāng)前進(jìn)程pc寄存器指向新程序指令的起始位置。Exec函數(shù)族e(cuò)xecl, execlp, execle, execv, execvp, execvel和v的差別設(shè)置程序運(yùn)行參數(shù)的差別l是通過(guò)不定參逐個(gè)給予v是組織成數(shù)組一次性給予有沒(méi)有p的差別是否會(huì)到系統(tǒng)path環(huán)境變量指定的路徑下去找這個(gè)程序execl(/bin/ls) vs execlp(ls)有沒(méi)有e的差別子進(jìn)程中是否自定義環(huán)境變量8. 內(nèi)存內(nèi)打開的文件信息8.1 進(jìn)程的I/O文件的輸入輸出系統(tǒng)調(diào)用open, read, write, lseek, close文件描述符的本質(zhì)是一個(gè)數(shù)組的下標(biāo)一個(gè)進(jìn)程內(nèi)部能夠打開的文件數(shù)量是有上限的重定向就是修改描述符所對(duì)應(yīng)的描述信息而實(shí)現(xiàn)的。重定向修改數(shù)據(jù)的輸出位置每個(gè)進(jìn)程運(yùn)行起來(lái)后默認(rèn)會(huì)打開三個(gè)文件標(biāo)準(zhǔn)輸入-鍵盤描述符0標(biāo)準(zhǔn)輸出-顯示器描述符1錯(cuò)誤輸出-顯示器描述符29. 進(jìn)程間通信資源進(jìn)程間通信就是讓操作系統(tǒng)為進(jìn)程之間提供一個(gè)公共內(nèi)存訪問(wèn)區(qū)。原因進(jìn)程之間具有獨(dú)立性。根據(jù)不同的應(yīng)用場(chǎng)景操作系統(tǒng)提供了多種不同的方式管道數(shù)據(jù)傳輸、共享內(nèi)存數(shù)據(jù)共享、消息隊(duì)列數(shù)據(jù)傳輸、信號(hào)量同步控制、套接字。9.1 管道1. 管道的本質(zhì)內(nèi)核中的一塊緩沖區(qū)匿名管道沒(méi)有名字的管道沒(méi)有標(biāo)識(shí)符。其他進(jìn)程怎么找到這個(gè)管道跟我通信呢特性只能用于具有親緣進(jìn)程關(guān)系的進(jìn)程間通信創(chuàng)建子進(jìn)程子進(jìn)程會(huì)復(fù)制父進(jìn)程的信息。操作在創(chuàng)建子進(jìn)程之前創(chuàng)建管道然后創(chuàng)建子進(jìn)程子進(jìn)程就復(fù)制進(jìn)程內(nèi)部打開的文件信息。int pipe(int fd[2]); // 接收兩個(gè)描述符 // fd[0]用于讀fd[1]用于寫命名管道有名字的管道可以被所有進(jìn)程找到可以用于任意進(jìn)程間通信。名字一個(gè)管道文件這個(gè)文件只是一個(gè)標(biāo)識(shí)名字本質(zhì)上管道就是內(nèi)核的緩沖區(qū)。操作mkfifo(filename, flag);9.2 公共特性管道的特性是半雙工通信可以選擇方向的單向通信管道的數(shù)據(jù)寫滿了write就會(huì)阻塞管道中沒(méi)有數(shù)據(jù)read就阻塞管道所有讀端關(guān)閉則write就會(huì)觸發(fā)異常-SIGPIPE所有寫端關(guān)閉取完所有數(shù)據(jù)后繼續(xù)read不再阻塞而是返回0數(shù)據(jù)傳輸是字節(jié)流傳輸以字節(jié)為單位進(jìn)行數(shù)據(jù)傳輸