進(jìn)程)
最近學(xué)了Linux軟件編程中進(jìn)程的部分相關(guān)內(nèi)容進(jìn)程和線(xiàn)程這一部分定義比較多有的比較難以理解建議搞懂、多背定義。一、程序和進(jìn)程很多人會(huì)把程序和進(jìn)程弄混其實(shí)兩者很好區(qū)分。程序是存放在磁盤(pán)上的代碼文件屬于靜態(tài)的放在那里不會(huì)自己運(yùn)行。進(jìn)程是程序跑起來(lái)之后的動(dòng)態(tài)過(guò)程包含創(chuàng)建、調(diào)度、消亡完整生命周期。程序運(yùn)行就生成進(jìn)程。每個(gè)進(jìn)程都有自己獨(dú)一無(wú)二的標(biāo)識(shí) PIDPPID代表它父進(jìn)程的ID用來(lái)描述進(jìn)程之間的父子關(guān)系。二、進(jìn)程常用命令寫(xiě)代碼之前先要學(xué)會(huì)在終端觀察進(jìn)程。top實(shí)時(shí)查看CPU占用看進(jìn)程運(yùn)行狀態(tài)ps -ef查看全部進(jìn)程能看到PID、PPIDpstree以樹(shù)狀打印直觀看到父子進(jìn)程關(guān)系ps -aux查看進(jìn)程詳細(xì)運(yùn)行狀態(tài)殺死進(jìn)程kill -9 進(jìn)程PIDkillall -9 進(jìn)程名后臺(tái)運(yùn)行程序在命令末尾加上./a.out jobs查看當(dāng)前終端下后臺(tái)任務(wù)fg 編號(hào)把后臺(tái)任務(wù)切回前臺(tái)運(yùn)行修改進(jìn)程優(yōu)先級(jí)nice -n 優(yōu)先級(jí) 程序renice -n 優(yōu)先級(jí) 進(jìn)程PID三、進(jìn)程虛擬內(nèi)存空間32位系統(tǒng)每個(gè)進(jìn)程擁有0?4G虛擬內(nèi)存空間分成幾個(gè)段文本段存放我們寫(xiě)的指令代碼數(shù)據(jù)段字符串常量、全局變量、靜態(tài)變量。分為初始化和未初始化兩部分系統(tǒng)數(shù)據(jù)段堆、棧。堆用于動(dòng)態(tài)內(nèi)存malloc棧保存局部變量、函數(shù)調(diào)用。關(guān)鍵點(diǎn)每個(gè)進(jìn)程擁有獨(dú)立虛擬地址空間。但是全部進(jìn)程共享0?4G虛擬內(nèi)存由操作系統(tǒng)做分時(shí)復(fù)用。兩個(gè)進(jìn)程看著地址一樣實(shí)際對(duì)應(yīng)的物理內(nèi)存不是同一塊。四、進(jìn)程調(diào)度與進(jìn)程狀態(tài)系統(tǒng)同時(shí)跑很多進(jìn)程依靠調(diào)度算法分配CPU時(shí)間。常見(jiàn)調(diào)度算法先來(lái)先執(zhí)行、高優(yōu)先級(jí)調(diào)度、時(shí)間片輪轉(zhuǎn)、多級(jí)隊(duì)列反饋、負(fù)載均衡。時(shí)間片CPU分配給單個(gè)進(jìn)程一小段執(zhí)行時(shí)間。宏觀上多個(gè)程序同時(shí)運(yùn)行微觀上CPU交替執(zhí)行同一時(shí)刻CPU只跑一個(gè)任務(wù)。進(jìn)程的7種狀態(tài)就緒態(tài)R任務(wù)已經(jīng)準(zhǔn)備好了等待分配CPU運(yùn)行態(tài)R正在CPU上面執(zhí)行可喚醒等待態(tài)S缺資源主動(dòng)掛起收到信號(hào)可以被喚醒不可喚醒等待態(tài)D等待過(guò)程不能被信號(hào)喚醒停止態(tài)T進(jìn)程被暫停僵尸態(tài)Z代碼已經(jīng)執(zhí)行完畢但是資源沒(méi)有被父進(jìn)程回收結(jié)束態(tài)X資源已經(jīng)回收進(jìn)程徹底消失五、進(jìn)程編程核心函數(shù)fork()pid_t fork(void);功能創(chuàng)建子進(jìn)程。返回值規(guī)則一定要記牢父進(jìn)程返回新創(chuàng)建子進(jìn)程的PID大于0子進(jìn)程返回0創(chuàng)建失敗返回?1調(diào)用fork之后會(huì)復(fù)制一份父進(jìn)程地址空間父子兩個(gè)進(jìn)程從fork之后的代碼分別往下執(zhí)行。注意子進(jìn)程不會(huì)從頭重新執(zhí)行main從fork返回處繼續(xù)運(yùn)行。如果子進(jìn)程不寫(xiě)return/exit退出子進(jìn)程還會(huì)繼續(xù)執(zhí)行后面的代碼甚至繼續(xù)fork生成大量多余子進(jìn)程。getpid() / getppid()getpid() 獲取當(dāng)前自己進(jìn)程PIDgetppid() 獲取父進(jìn)程PIDexit()void exit(int status);結(jié)束當(dāng)前進(jìn)程。main函數(shù)里面exit()和return效果基本一致普通函數(shù)內(nèi)部return只是函數(shù)返回不會(huì)結(jié)束進(jìn)程想要結(jié)束進(jìn)程必須調(diào)用exit。ps、僵尸進(jìn)程怎么產(chǎn)生如何避免產(chǎn)生原因子進(jìn)程代碼執(zhí)行結(jié)束但是父進(jìn)程沒(méi)有回收子進(jìn)程資源。子進(jìn)程就變成僵尸進(jìn)程ZPID還占著內(nèi)存沒(méi)有釋放。解決辦法父進(jìn)程先退出子進(jìn)程變成孤兒進(jìn)程會(huì)被系統(tǒng)init進(jìn)程收養(yǎng)由init幫忙回收。父進(jìn)程調(diào)用wait()主動(dòng)回收子進(jìn)程這是我們寫(xiě)代碼最常用的方式。七、wait函數(shù)pid_t wait(int *wstatus);功能回收子進(jìn)程資源具備阻塞特性。如果子進(jìn)程還沒(méi)結(jié)束父進(jìn)程會(huì)卡在wait這里原地等待。返回值成功返回被回收子進(jìn)程PID失敗返回?1。參數(shù)wstatus用來(lái)保存子進(jìn)程退出狀態(tài)配合一組宏使用WIFEXITED(wstatus) 判斷子進(jìn)程是否正常退出WEXITSTATUS(wstatus) 獲取子進(jìn)程退出返回值WIFSIGNALED(wstatus) 判斷進(jìn)程是不是被信號(hào)殺死WTERMSIG(wstatus) 獲取殺死進(jìn)程的信號(hào)編號(hào)八、waitpid函數(shù)我們知道子進(jìn)程退出之后如果父進(jìn)程不去回收它子進(jìn)程就會(huì)變成僵尸進(jìn)程仍然占用系統(tǒng)資源。waitpid就是用來(lái)回收指定子進(jìn)程的系統(tǒng)調(diào)用。pid_t waitpid(pid_t pid, int *wstatus, int options);參數(shù)說(shuō)明pid想要回收的子進(jìn)程PIDwstatus指針用來(lái)保存子進(jìn)程退出狀態(tài)信息傳NULL代表不關(guān)心退出狀態(tài)options選項(xiàng)0阻塞回收。子進(jìn)程沒(méi)有退出父進(jìn)程就卡在這一行一直等子進(jìn)程結(jié)束。WNOHANG非阻塞回收。如果子進(jìn)程還沒(méi)結(jié)束函數(shù)直接返回0父進(jìn)程不會(huì)卡住可以繼續(xù)干別的事。返回值成功返回被回收的子進(jìn)程pid失敗返回-1設(shè)置WNOHANG時(shí)如果子進(jìn)程還沒(méi)有結(jié)束返回0wait只能隨便等待任意一個(gè)子進(jìn)程waitpid可以精準(zhǔn)指定回收哪一個(gè)子進(jìn)程還能選擇阻塞或者非阻塞處理僵尸進(jìn)程更加靈活。九、exec函數(shù)族進(jìn)程內(nèi)部執(zhí)行另一套程序fork創(chuàng)建子進(jìn)程之后子進(jìn)程復(fù)制父進(jìn)程的代碼。如果我們想要子進(jìn)程去跑一個(gè)完全不一樣的程序就需要exec函數(shù)族。exec不會(huì)創(chuàng)建新進(jìn)程它復(fù)用當(dāng)前進(jìn)程的PID直接把原來(lái)的代碼替換成新程序的代碼。執(zhí)行成功之后后面的代碼全部不會(huì)再運(yùn)行。一共有6個(gè)exec系列函數(shù)名字后綴有含義llist參數(shù)以列表形式逐個(gè)傳最后必須以NULL結(jié)尾vvector參數(shù)放到指針數(shù)組里面?zhèn)鬟M(jìn)去p會(huì)去系統(tǒng)環(huán)境變量PATH路徑里面查找程序文件不用寫(xiě)完整路徑e可以自己傳入自定義環(huán)境變量int execl(const char *path, const chararg, …/(char *)NULL */);int execlp(const char *file, const chararg, …/(char *)NULL */);int execle(const charpath, const chararg, …/,(char)NULL, char *const envp[] */);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);int execvpe(const char *file, char *const argv[], char *const envp[]);返回值特點(diǎn)exec執(zhí)行成功沒(méi)有返回值原程序代碼直接被覆蓋只有執(zhí)行失敗的時(shí)候才會(huì)返回?1繼續(xù)運(yùn)行后面代碼。區(qū)分帶p不用寫(xiě)完整路徑依靠PATH找程序不帶p必須寫(xiě)程序完整路徑。帶l一個(gè)個(gè)羅列參數(shù)帶v把參數(shù)放到字符串?dāng)?shù)組。帶e可以自定義環(huán)境變量。十、環(huán)境變量環(huán)境變量就是進(jìn)程運(yùn)行時(shí)臨時(shí)存放配置信息的變量。比如我們經(jīng)常見(jiàn)到的PATH就是環(huán)境變量系統(tǒng)靠它尋找可執(zhí)行程序。環(huán)境變量是繼承關(guān)系父進(jìn)程的環(huán)境變量默認(rèn)會(huì)復(fù)制給子進(jìn)程。exec執(zhí)行新程序的時(shí)候默認(rèn)沿用原有環(huán)境變量如果使用帶e后綴的exec函數(shù)可以傳入一套全新環(huán)境變量。setenv 修改環(huán)境變量int setenv(const char *name, const char *value, int overwrite);參數(shù)name環(huán)境變量名字value要設(shè)置的值overwrite是否覆蓋舊值非0如果這個(gè)變量已經(jīng)存在覆蓋原來(lái)的值0已經(jīng)存在則不做修改保留舊值返回值成功返回0失敗返回?1。這部分是進(jìn)程編程的重點(diǎn)寫(xiě)fork子進(jìn)程調(diào)用exec執(zhí)行外部程序搭配waitpid回收是非常經(jīng)典的代碼模型。