Linux進(jìn)程等待和進(jìn)程替換詳解
進(jìn)程等待
前面我們了解了如果父進(jìn)程沒(méi)有回收子進(jìn)程, 那么當(dāng)子進(jìn)程接收后, 就會(huì)一直處于僵尸狀態(tài), 導(dǎo)致內(nèi)存泄漏, 那么我們?nèi)绾巫尭高M(jìn)程來(lái)回收子進(jìn)程的資源.
waitpid
我們可以通過(guò) Linux 提供的系統(tǒng)調(diào)用函數(shù) wait 系列函數(shù)來(lái)等待子進(jìn)程死亡, 并回收資源.
wait
函數(shù)用于等待任何一個(gè)子進(jìn)程結(jié)束, 并回收其資源.
status
:指向整數(shù)的指針, 用于存儲(chǔ)子進(jìn)程的退出狀態(tài). 如果不需要這個(gè)信息, 可以傳遞NULL.
- 成功時(shí)返回被等待的子進(jìn)程的PID, 失敗時(shí)返回 -1, 并設(shè)置 errno.
waitpid函數(shù)允許父進(jìn)程等待特定的子進(jìn)程結(jié)束
- pid:子進(jìn)程的PID. 如果為 -1, 則等待任何一個(gè)子進(jìn)程。
- status:同wait函數(shù).
- options:等待選項(xiàng), 常用的有 WNOHANG (非阻塞等待).
- 成功時(shí)返回被等待的子進(jìn)程的PID. 失敗時(shí)返回-1,并設(shè)置errno。
一般來(lái)說(shuō), 用 waitpid 多一點(diǎn), 因?yàn)?waitpid 提供的更為細(xì)致的操作.
int main() { pid_t id = fork(); if(id<0) { perror("fork"); exit(1); } if(id==0)//子進(jìn)程代碼 { int count = 5; while(count) { printf("[%d]我是子進(jìn)程,我的pid是: %d\n",count,getpid()); sleep(1); count--; } exit(0);//子進(jìn)程執(zhí)行完代碼后退出, exit 會(huì)直接終止本進(jìn)程 } //父進(jìn)程代碼 waitpid(id,NULL,0); printf("等待子進(jìn)程成功!\n"); return 0; }
可以觀(guān)察到, 在等待子進(jìn)程結(jié)束之前, 父進(jìn)程卡在了 waitpid(), 直到子進(jìn)程都被等待成功, 父進(jìn)程才會(huì)繼續(xù)向后執(zhí)行.
status 參數(shù)
在 wait 和 waitpid 函數(shù)中都存在一個(gè) status 的參數(shù).
在 status 中存儲(chǔ)著子進(jìn)程的退出碼和退出信號(hào).
如果父進(jìn)程想要了解子進(jìn)程的退出信息, 可以通過(guò) status 來(lái)了解.
status 是一個(gè) int 類(lèi)型的變量, 一共有 32 個(gè)bit, 我們主要看它的低 16 位bit.
那么如何獲取退出狀態(tài) (退出碼) 和 信號(hào)
退出狀態(tài) (退出碼): (status >> 8) & 0xFF 退出信號(hào): status & 0x7F
int main() { pid_t id = fork(); if(id<0) { perror("fork"); exit(1); } if(id==0)//子進(jìn)程代碼 { int count = 5; while(count) { printf("[%d]我是子進(jìn)程,我的pid是: %d\n",count,getpid()); sleep(1); count--; } exit(55);//子進(jìn)程執(zhí)行完代碼后退出 } //父進(jìn)程代碼 int status = 0; waitpid(id,&status,0); printf("等待子進(jìn)程成功!\n"); printf("進(jìn)程退出碼: %d,進(jìn)程退出信號(hào): %d\n",(status >> 8) & 0xFF,status & 0x7F); return 0; }
當(dāng)子進(jìn)程在運(yùn)行時(shí), 使用 kill -9 617714命令, 來(lái)終止子進(jìn)程, 那么也就能觀(guān)察到, 退出信號(hào)為 9.
option
在前面的參數(shù)解釋中說(shuō)到, 這是一個(gè)等待選項(xiàng).
父進(jìn)程可以選擇一直阻塞下去, 直到等到子進(jìn)程的死亡,
或者當(dāng)子進(jìn)程還沒(méi)死亡時(shí), 父進(jìn)程去執(zhí)行其他的代碼. 等一會(huì)再來(lái)查看子進(jìn)程是否死亡.
waitpid(pid,&status,WNOHANG); // 非阻塞等待 waitpid(pid,&status,0); // 阻塞等待
int main() { pid_t id = fork(); if(id<0) { perror("fork"); exit(1); } if(id==0)//子進(jìn)程代碼 { int count = 5; while(count) { printf("[%d]我是子進(jìn)程,我的pid是: %d\n",count,getpid()); sleep(1); count--; } exit(55);//子進(jìn)程執(zhí)行完代碼后退出 } //父進(jìn)程代碼 while(1)//循環(huán)訪(fǎng)問(wèn)子進(jìn)程退出情況 { int wait = waitpid(id,NULL,WNOHANG); if(wait>0)//子進(jìn)程退出成功 { printf("子進(jìn)程退出成功,子進(jìn)程pid: %d\n",wait); break; } else if(wait==0)//子進(jìn)程還沒(méi)退出,父進(jìn)程干自己的事情 { //此處簡(jiǎn)單模擬父進(jìn)程干的事情 printf("我是父進(jìn)程\n"); } else //等待子進(jìn)程退出失敗 { perror("waitpid"); exit(1); } sleep(1); } return 0; }
執(zhí)行上面的代碼就能觀(guān)察到, 在子進(jìn)程結(jié)束前, 父進(jìn)程還在向頻幕上打印文字.
進(jìn)程替換
創(chuàng)建子進(jìn)程是為了完成一些工作, 但是子進(jìn)程的代碼和父進(jìn)程是一樣的,
有可能子進(jìn)程并不需要執(zhí)行父進(jìn)程的代碼, 而是執(zhí)行一些其他代碼.
這種場(chǎng)景下, 就可以使用進(jìn)程替換.
我們?yōu)槭裁床恢苯訉⒆舆M(jìn)程要執(zhí)行的代碼寫(xiě)在父進(jìn)程中呢, 還要去使用進(jìn)程替換?
1. 如果所有的代碼都放在父進(jìn)程中, 那么父進(jìn)程的代碼會(huì)有多么的龐大,
這會(huì)提高代碼編寫(xiě)和維護(hù)的成本.
2. 進(jìn)程所執(zhí)行的一定是我們的C/C++程序嗎? 進(jìn)程也可以執(zhí)行其他的非 C/C++ 程序,
那對(duì)于那些非 C/C++ 程序 (java程序), 我們無(wú)法將他們和我們所寫(xiě)的 C/C++ 代碼整合在一起, java 和 C/C++ 的運(yùn)行環(huán)境都不同.
exec 系列函數(shù)
如果想要?jiǎng)?chuàng)建出來(lái)的子進(jìn)程執(zhí)行全新的程序, 可以使用 exec 系列函數(shù)進(jìn)行程序替換.
一共有6個(gè)函數(shù), 其中主要分為兩類(lèi)
1. execl 系列
2. execv 系列
execl
int main() { printf("進(jìn)行程序替換了\n"); int n = execl("/usr/bin/ls","ls","-a","-l",NULL); if(n==-1) { perror("execl"); } printf("程序替換完畢!\n"); return 0; }
execl參數(shù): 第一個(gè)是要執(zhí)行程序的路徑 (/usr/bin/ls),
第二個(gè)參數(shù)是要執(zhí)行的程序的名稱(chēng) (ls),后面的參數(shù)到 NULL 之前, 都是要替換的程序參數(shù) (-a, -l, 都是 ls 程序的參數(shù)).
execl 中 l, 表示如何將參數(shù)傳遞要替換的程序. l 表示通過(guò)一個(gè)列表的方式,
即向上面的 "-l", "-a"..., 一個(gè)列表的形式.
execlp 和 execle 兩個(gè)函數(shù)則分別多了 p 和 e.
p 則代表要執(zhí)行的程序可以從環(huán)境變量 PATH 中找到, 所以不用寫(xiě)執(zhí)行程序的路徑.
e 則表示, 可以傳入用戶(hù)自己定義的環(huán)境變量 (_env[]) 給程序使用.
int main() { printf("我要進(jìn)行程序替換了...\n"); int n = execlp("ls","-l",NULL); if(n==-1) { perror("execl"); } printf("程序替換完畢!\n"); return 0; } int main() { const char* _env[]={"MY_ENV=666",NULL}; printf("我要進(jìn)行程序替換了...\n"); int n = execle("/usr/bin/ls","ls","-l",NULL,_env);//自己定義一個(gè)環(huán)境變量MY_ENV=666傳遞給要去執(zhí)行的程序 if(n==-1) { perror("execl"); } printf("程序替換完畢!\n"); return 0; }
execv
上面的 execl 中的 l, 代表傳參使用列表的形式.
那么 v 很容易就想到了是vector.
所以 execv 函數(shù)在給替換的程序傳參時(shí), 是通過(guò)一個(gè) vector 來(lái)傳參的.
int main() { char* const set[]={"ls","-a","-l",NULL}; printf("我要進(jìn)行程序替換了...\n"); int n = execv("/usr/bin/ls",set); if(n==-1) { perror("execl"); } printf("程序替換完畢!\n"); return 0; }
那么剩下的 execvp 和 execvpe 和之前的 execl 系列中的一樣.
p 代表在環(huán)境變量 PATH 中查找, e 可以傳入自己定義的環(huán)境變量.
- l (list): 傳參的方式為使用列表來(lái)傳遞
- v (vector): 使用數(shù)組來(lái)傳遞參數(shù)
- p (path): 會(huì)在環(huán)境變量 PATH 中查找程序
- e (env): 可以傳遞自己定義的環(huán)境變量
以上就是Linux進(jìn)程等待和進(jìn)程替換詳解的詳細(xì)內(nèi)容,更多關(guān)于Linux進(jìn)程等待和替換的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Apache Pulsar 微信大流量實(shí)時(shí)推薦場(chǎng)景下實(shí)踐詳解
這篇文章主要為大家介紹了Apache Pulsar 微信大流量實(shí)時(shí)推薦場(chǎng)景下實(shí)踐詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Linux上虛擬機(jī)顯示網(wǎng)絡(luò)不可用的解決方法
這篇文章主要介紹了Linux上虛擬機(jī)顯示網(wǎng)絡(luò)不可用的解決方法,文中通過(guò)圖文結(jié)合的方式講解的非常詳細(xì),對(duì)大家解決問(wèn)題有一定的幫助,需要的朋友可以參考下2024-12-12Ubuntu18.04一次性升級(jí)Python所有庫(kù)的方法步驟
這篇文章主要介紹了Ubuntu18.04一次性升級(jí)Python所有庫(kù)的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01CentOS 安裝 Mongodb詳解(在線(xiàn)和離線(xiàn))
這篇文章主要介紹了CentOS 安裝 Mogodb詳解(在線(xiàn)和離線(xiàn)) ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01