Linux——進程控制(二)進程等待

目錄

前言

一、進程等待

二、如何進行進程等待

1.wait

2.waitpid

2.1第二個參數?

2.2第三個參數?

3.?等待多個進程

三、為什么不用全局變量獲取子進程的退出信息


前言

前面我們花了大量的時間去學習進程的退出,退出并不難,但更深入的學習能為本章進程等待打好基礎,因此沒看過的小伙伴可以先學習進程退出。

一、進程等待

之前講過,子進程退出,父進程一直在運行,不對子進程進行回收,就可能造成‘僵尸進程’的問題,進而造成內存泄漏

另外,進程一旦變成僵尸狀態,那就刀槍不入,“殺人不眨眼”的kill -9 也無能為力,因為誰也沒有辦法 殺死一個已經死去的進程。

父進程派給子進程的任務完成的如何,我們需要知道。如子進程運行完成,結果對還是不對, 或者是否正常退出。

父進程通過進程等待的方式,可以獲取子進程退出的信息。(雖然不是一定要獲取,但是得有這個功能)

二、如何進行進程等待

1.wait

我們看看2號手冊中的wait函數,他可以等待任意一個子進程的退出,參數是int類型的指針,等待成功返回子進程的pid,失敗返回-1。

我們使用如下代碼進行進程等待,這里wait的參數先給NULL,代表不關心子進程退出的狀態(后續會再提到)。子進程運行5秒后變成僵尸狀態,父進程先休眠10秒再去調用wait函數。

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void Work()
{int cnt = 5;while(cnt){printf("我是子進程, pid: %d, ppid: %d, cnt: %d\n",getpid(),getppid(),cnt--);sleep(1);}
}int main()
{pid_t id = fork();if(id == 0){//childWork();exit(0);}else{sleep(10);//fatherpid_t rid = wait(NULL);if(rid == id){printf("等待成功,pid: %d\n",getpid());}}return 0;
}

我們寫了一個腳本來監控進程的運行情況,代碼如下(注意myprocess是我設置的進程名)

 while :; do ps ajx | head -1 && ps ajx | grep myprocess | grep -v "grep"; sleep 1; echo "###################"; done

結果發現0-5秒中,父子進程正常運行,5-10秒中,子進程變成了僵尸狀態,父進程此時在sleep,并沒有回收子進程,10秒后,父進程sleep結束,wait函數等到了子進程,于是將子進程回收了,同時父進程也運行完畢。?

由此我們可以得知:

進程等待能回收子進程僵尸狀態。

還有一個結論,在父進程進行等待的時候,如果子進程還沒有處理完,那么父進程必須在wait上進行阻塞等待,直到子進程僵尸,wait自動回收,再繼續執行后續代碼。這可以通過打印的方式查看。就類似于scanf需要等待用戶輸入一樣,用戶不輸入就一直在這里阻塞著,直到輸入后才繼續往后執行。

一般而言,父子進程誰先運行我們不知道,但能知道一般都是父進程最后退出,多進程由父進程發起,也由父進程統一回收

2.waitpid

wait是等待任意一個子進程,而waitpid可以等待指定的那一個,第一個參數傳等待子進程的pid代表等待這個進程,傳-1代表等待任意進程。?第二個參數和wait的參數一樣,第三個參數也先不管,設置為0代表默認阻塞等待。

將上面的代碼從wait修改為waitpid,因為我們只fork了一次,只創建了一個子進程,因此如下修改即可。

2.1第二個參數?

重點我們得講解一下第二個參數 status ,他是輸出型參數,我們可以隨便定義一個int變量,將變量的值傳遞給第二個參數,waitpid會將子進程退出碼和信號寫到這個變量里

這里我們將子進程的退出碼設置為10,看看打印出來的status值為多少。

發現status為2560,這似乎不像退出碼。他是通過下面這個圖片的方式得來的,int整形32位,只用低16位,其中高8位代表退出碼,低8位代表終止信號

正常終止看高八位即可,因為未收到信號,因此低8位為0。

被信號所殺,看低八位,其中第7位不看,他代表core dump標志(暫時不考慮),只看0-6位。?

那么2560的二進制為 0000 1010 0000 0000?如果右移8位,也就是只看高8位,即0000 1010,即為10,我們退出碼也就是10。

公式為? :? *status = (exit_code<<8)| exit_signal

status不能直接使用,如下經過右移操作和與操作,就可以得到準確的退出碼和信號了。

執行一下,沒有問題?

小總結一下

  • 當一個進程異常了(收到信號) ,那么退出碼就無意義了
  • 通過信號碼是否非零,來判斷是否收到信號
  • 手動殺死子進程,也能得到相應信號

雖然我們會通過位運算來得到退出碼與信號,但這樣也比較麻煩,linux系統提供了如下兩個接口幫我們處理status。

WIFEXITED(status): 若為正常終止子進程返回的狀態,則為真。(查看進程是否是正常退出)

WEXITSTATUS(status): 若WIFEXITED非零,提取子進程退出碼。(查看進程的退出碼)

如下,查看是否正常退出,退出碼為多少。?

結果也符合預期?

2.2第三個參數?

0:即阻塞等待

WNOHANG::若pid指定的子進程沒有結束,則waitpid()函數返回0,不予以等待。若正常結束,則返回該子進程的ID。(非阻塞式等待)

講個小故事:

????????張三約翠花去電競酒店打麻將, 他已經到翠花樓下了,現在在等待翠花先來一起出發。

????????此時張三有兩個方法,一個是打電話,詢問翠花在干嘛,什么時候下樓,打完翠花說等一下,她化個妝,于是張三就在樓下開一把金鏟鏟之戰,過了半個小時又打,翠花說在穿鞋了等一小下,于是張三掛斷電話去刷抖音,過一會再打電話,翠花說已經下樓了,剛剛準備出門又上了個廁所,張三沒有什么脾氣,誰叫我想約人家呢,于是掛斷電話,又去看看淘寶,要買點什么,最后再打電話,翠花此時終于到達了,于是兩個人開開心心的去打麻將了。

????????另一個方法也是打電話,詢問翠花在干嘛,什么時候下樓,翠花也說等一下,還在化妝,但是張三今天電話不掛,就一直等翠花,時刻知道翠花在干嘛,直到翠花下樓一起去打麻將。

在這個故事中

張三:父進程

翠花:子進程

打電話:調用系統接口

第一個方法:等待的條件不滿足,wait/waitpid不阻塞,而是立即返回!可以做自己占據時間并不多的事情。這是非阻塞式調用,即非阻塞+輪詢方案進行進程等待,該方案往往要進行重復調用。返回值>0等待成功,子進程已退出;返回值==0;等待成功,子進程未退出,返回值<0等待失敗

第二個方法:翠花不結束,電話不掛機,即阻塞式調用。子進程不退出,wait/waitpid不返回。

代碼如下,waitpid第三個參數為 WNOHANG?借此觀看非阻塞輪詢等待。?

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void Work(int number)
{printf("我是子進程, pid: %d, ppid: %d, number: %d\n",getpid(),getppid(),number);
}int main()
{pid_t id = fork();if(id == 0){//childint number = 5;while(number){Work(number);number--;sleep(1);}exit(10);}//father                                                                                          int status = 0;while(1){pid_t rid = waitpid(id,&status,WNOHANG);if(rid>0){//等待成功,子進程退出了printf("等待子進程成功,子進程退出碼: %d,退出信號: %d\n",WEXITSTATUS(status),status&0x7F);break;}else if(rid == 0){//等待成功,但子進程沒有退出printf("等待成功,子進程還沒推出,父進程做其他事情去了\n");sleep(2);}else{printf("等待失敗\n");break;}}return 0;
}

運行結果如下,父進程間歇性詢問子進程是否完成,沒完成就做自己的事情,待會再來詢問。

3.?等待多個進程

我們使用for循壞來fork多個進程,同時給每個進程編號,創建順序從0-9。waitpid第一個參數為-1,代表等待任意的子進程。

雖然我們也可以用數組的方式,將子進程的pid放到數組里,但是這樣就只能一個一個進程的等待,比如最先會等待退出碼為0進程,如果該進程不結束,父進程會一直等待,那么后續的進程永遠不會被回收,就會造成內存泄漏的問題。

 1: myprocess.c ? ?                                                                        ?? buffers 
#include<sys/types.h>
#include<sys/wait.h>void Work(int number)
{int cnt = 2;while(cnt){printf("我是子進程, pid: %d, ppid: %d, cnt: %d, number: %d\n",getpid(),getppid(),cnt--,number);sleep(1);}
}const int n = 10;int main()
{int i = 0;for(;i<n;i++){pid_t id = fork();if(id == 0){//childWork(i);exit(i);}}//fork的子進程已近全部退出了,下面是父進程執行的代碼for(i=0;i<n;i++){int status;pid_t rid = waitpid(-1,&status,0);  //-1:任意一個子進程退出 if(rid>0)                                                                                      {printf("等待子進程 %d 成功, 退出碼: %d\n",rid, WEXITSTATUS(status));}}return 0;
}

看看運行的情況,發現調度運行與終止都是沒有規律的,誰先誰后我們不確定,我們只知道肯定是父進程先創建并最后退出。

三、為什么不用全局變量獲取子進程的退出信息

剛剛我們提到, 可以用數組獲取子進程的pid,然后傳值進行等待,雖然效果不一定很好,但這也算是一個解決辦法,為什么不用全局變量獲取子進程的退出信息,而是采用寫入的方式進行傳參獲取呢?

這是因為進程之間具有獨立性,父進程無法直接獲取子進程的退出信息,比如status我們設置為0,父子進程看到的status值就都為0,此時我們獲取到了子進程的退出碼,將子進程的退出碼寫入status變量,此時會發生寫時拷貝,子進程看到的是我自己寫的新值,而父進程看到的還是0。父子進程代碼共享,但數據不一定相同

而父進程通過fork,返回的id是子進程的pid,子進程返回的id為0,已經寫時拷貝過了,因此可以獲取。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/717918.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/717918.shtml
英文地址,請注明出處:http://en.pswp.cn/news/717918.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

048 異常

什么是異常 異常體系結構 異常的繼承關系 Error Exception 異常處理機制 try&#xff1a;用{}將可能產生異常的代碼包裹catch&#xff1a;與try搭配使用&#xff0c;捕獲try包裹代碼中拋出的異常并進行后續動作finally&#xff1a;跟在try后&#xff0c;在try和catch之后執行…

web3時事粥報

比特幣正成為更具有吸引力的通脹對沖工具 在通脹的宏觀經濟浪潮中&#xff0c;比特幣正逐漸嶄露頭角&#xff0c;成為那些渴望多元化投資組合的投資者眼中的璀璨明星。Kooner 預測&#xff0c;2024年&#xff0c;各種宏觀經濟挑戰可能進一步提升比特幣、黃金和白銀等資產的避險…

3月3日做題總結(C/C++真題)

第一題 參加位運算的數據其類型不能是&#xff08;&#xff09;。 A---int B---char C---float D---long int 正確答案&#xff1a;C 解析&#xff1a;無論是float&#xff0c;還是double&#xff0c;在內存中的存儲分為三部分&#xff1a;符號位、指數位、尾數位&#…

Google Dremel和parquet的復雜嵌套數據結構表征方法解析

轉載請注明出處。作者&#xff1a;archimekai 核心參考文獻&#xff1a; Dremel: Interactive Analysis of Web-Scale Datasets 文章目錄 引言復雜嵌套數據結構的無損表征問題Dremel論文中提出的表征方法parquet備注 引言 Dremel是Google的交互式分析系統。Google大量采用prot…

全量知識系統問題及SmartChat給出的答復 之17 知識系統中的兩個特權類(超類和欠類) :腳本和場景

Q.45 知識系統中的兩個特權類 &#xff1a;腳本和場景 知識系統中的兩個特權類&#xff08;也是集合論中兩個特權集合&#xff09;&#xff1a;腳本script和場景scene 。 一個$Demonstrate類型的腳本script&#xff1a; 表示“值val”&#xff08; 形式上是應用程序的實用工…

如何學習openfoam

學習OpenFOAM的詳細步驟、流程、學習網站、練習案例以及B站學習資源推薦如下&#xff1a; 一、詳細步驟和流程 安裝OpenFOAM&#xff1a;首先&#xff0c;你需要在你的計算機上安裝OpenFOAM。你可以從OpenFOAM的官方網站下載適合你的操作系統的安裝包&#xff0c;然后按照官方提…

搭建服務器及跨域處理

使用內置的模塊搭建服務器 自己電腦: 域名:localhost ip:127.0.0.1 http模塊搭建服務器 const http = require(http)// 創建一個http對應的服務器,每次改完服務器的代碼后都需要重新啟動下服務器 /*方式一: const server = http.createServer((request,response)=>{…

對簡單工廠模式、工廠方法模式的思考

目錄 1 背景1.1 題目描述1.2 輸入描述1.3 輸出描述1.4 輸入示例1.5 輸出示例 2 簡單工廠模式3 工廠方法模式4 思考4.1 改進工廠方法模式 1 背景 題目源自&#xff1a;【設計模式專題之工廠方法模式】2.積木工廠 1.1 題目描述 小明家有兩個工廠&#xff0c;一個用于生產圓形積木…

鐵路關基保護新規發布!鐵路軟件供應鏈安全洞察與治理思路

近日&#xff0c;國家鐵路局發布《鐵路關鍵信息基礎設施安全保護管理辦法》&#xff0c;《辦法》第十四條提到&#xff1a;“運營者應當加強鐵路關鍵信息基礎設施供應鏈安全保護&#xff0c;優先采購安全可信的網絡產品和服務。運營者采購網絡產品和服務&#xff0c;應當預判該…

Intel FPGA IP之LVDS SerDes IP學習

FPGA 視頻數據輸入輸出直通工程&#xff1a; 屏&#xff1a;13.2吋8bit色深&#xff0c;屏幕分辨率為1440*192060&#xff0c;具有兩個Port&#xff0c;每個Port有4個差分數據對與1個差分時鐘對&#xff0c;差分對均支持LVDS協議芯片&#xff1a;Cyclone V系列FPGA目的&#x…

標簽轉格式問題之——xml_2_txt.py

import xml.etree.ElementTree as ET#xml 是python自帶的package import osclasses[walnut]#寫自己的分類名 pre_dirF:/2023walnut/labels#xml文件所在文件夾 target_dirF:/2023walnut/yolo#想要存儲txt文件的文件夾 pathos.listdir(pre_dir)for path1 in path: # path1rC:\Use…

[變壓器故障診斷分類及預測】基于Elman神經網絡

課題名稱&#xff1a;基于Elman神經網絡的變壓器故障診斷分類及預測 版本日期&#xff1a;2024-02-10 運行方式&#xff1a;直接運行Elman0507.m文件 代碼獲取方式&#xff1a;私信博主或QQ&#xff1a;491052175 模型描述&#xff1a; 對變壓器油中溶解氣體進行分析是變壓…

Noise Conditional Score Networks(NCSN)學習

參考&#xff1a; [1] https://zhuanlan.zhihu.com/p/597490389 [2] https://www.zhangzhenhu.com/aigc/Score-Based_Generative_Models.html TOC 1 基于分數的生成模型1.1 簡介和動機1.2 Score Matching及其改進1.2.1 Score Matching1.2.2 Sliced score matching&#xff08;不…

XSS_lab(level1-level5)

level1 直接輸入頁面沒有發現輸入框&#xff0c;觀察url發現有傳參 嘗試修改傳參為&#xff1a;<script>alert(1)</script> 過啦&#xff01; level2 頁面中有輸入框&#xff0c;嘗試構建語句&#xff1a;<script>alert(1)</script>,傳輸后查看源代…

國際心理學導師-葉子文JeffreyYip的《意識地圖》

“物質就是能量。” ---愛因斯坦 “時常保持覺知&#xff0c;有意識地發現情緒起伏 你隨時都能翻轉人生 做自己人生的導演 當你頻率高時&#xff0c;萬事萬物為你而來” ---大衛霍金斯 葉子文-《意識地圖》&#xff1a;高階心理學課程 宇宙間萬物的本質是能量。一切都靠能量…

Java基礎---lambda表達式

一、為什么要引入lambda表達式 lambda 表達式是一個可傳遞的代碼塊 &#xff0c; 可以在以后執行一次或多次 。 在介紹lambda表達式之前&#xff0c;我們看一下&#xff0c;以前&#xff0c;我們對于一個問題的通常寫法。 假設你已經了解了如何按指定時間間隔完成工作&#xf…

js字符串轉json的3種方法

1.eval方式解析 function strToJson(str){var json eval("(" str ")");return json;}console.log(strToJson("{int:1, string:demo}")); 運行截圖&#xff1a; 注&#xff1a; 記得別忘了str兩旁的小括號。 永遠不要使用 eval !!! eval() 是一…

611. 有效三角形的個數 - 力扣

1. 題目 給定一個包含非負整數的數組 nums &#xff0c;返回其中可以組成三角形三條邊的三元組個數。 2. 示例 3. 分析 利用已升序了的數組通過 a b > c 這條公式找出符合要求的三元組&#xff0c;利用這個公式的前提是三條邊為從小到大&#xff0c;再利用單調性快速統計…

STM32 (1)

1.基本信息 stm32是由ST公司生產的一種32位微控制器&#xff08;單片機&#xff09;。 1.1 各種型號 stm32是32位單片機的總稱&#xff0c;有多種不同的系列。 32即用32個比特位表示一個地址&#xff0c;尋址范圍&#xff1a;0x00000000 --0xffffffff (4GB) 1.2 存儲密度 …

Mysql事務的兩段式提交

binlog和redo log區別 為了滿足Mysql的事物ACID特性&#xff0c;InnoDB引入了redo log和 undo log日志文件。為了滿足主從同步Mysql引入了binlog日志文件。redo log和binlog文件都保存的數據庫對數據庫的修改&#xff0c;但是binlog和redo log本質上是不一樣的&#xff1a; r…