引言:
北京時(shí)間:2023/3/21/7:17,這篇博客本來昨天晚上就能開始寫的,但是由于筆試強(qiáng)訓(xùn)的原因,導(dǎo)致時(shí)間用在了做題上,通過快2個(gè)小時(shí)的垂死掙扎,我充分意識(shí)到了自己做題能力的缺陷和運(yùn)用新知識(shí)的缺陷,所以我需要把重心給轉(zhuǎn)移一下了,以后做題才是我的頭號(hào)目標(biāo),雖然我在很久以前可能就說過這樣的話,但是這次我是認(rèn)真的,就算我做題不行,但是我看代碼還是行的,所以我每天必看一些做題代碼,來加深自己對(duì)知識(shí)的運(yùn)用,希望不久之后,能有所進(jìn)步吧!哪怕只是一絲絲!所以今天我們就來學(xué)習(xí)上篇博客剩下的有關(guān)進(jìn)程控制的內(nèi)容,和有關(guān)進(jìn)程程序替換的知識(shí)吧!
復(fù)習(xí)進(jìn)程等待
昨天已經(jīng)淺淺的了解了什么是進(jìn)程等待,進(jìn)程等待的目的就是為了終止僵尸進(jìn)程,回收僵尸進(jìn)程所占的資源,并且獲得進(jìn)程的退出碼和退出信號(hào),并且我們了解了進(jìn)行進(jìn)程等待的兩個(gè)接口函數(shù)wait/waitpid,通過這兩個(gè)接口,我們就可以去調(diào)用操作系統(tǒng)提供給我們的系統(tǒng)調(diào)用,這樣就可以利用系統(tǒng)調(diào)用的形式來完成進(jìn)程等待了;我們昨天已經(jīng)知道使用wait接口就是用來等待一個(gè)進(jìn)程的子進(jìn)程,并且有代碼演示,這里不多做理解,昨天我們也了解了waitpid這個(gè)接口,發(fā)現(xiàn)該接口有三個(gè)參數(shù),并且其中第一個(gè)參數(shù)pid
,它的作用和wait的參數(shù)差不多,本質(zhì)就是為了等待一個(gè)進(jìn)程,pid=0等待的是該進(jìn)程的子進(jìn)程,pid>0,等待的則是任意一個(gè)pid相等的進(jìn)程,和第二個(gè)參數(shù)status
,我們了解到,該參數(shù)是一個(gè)位圖結(jié)構(gòu)的參數(shù),不單單是整形類型,本質(zhì)為位圖結(jié)構(gòu)就是為了可以同時(shí)從一個(gè)參數(shù)上獲取退出碼和退出信號(hào)兩個(gè)碼值,第三個(gè)參數(shù)options
,就是用于判斷子進(jìn)程是否正常終止,父進(jìn)程是否繼續(xù)等待的問題,所以接下來,我們就承接這上篇博客,把剩下的有關(guān)位圖結(jié)構(gòu),也就是有關(guān)status參數(shù)的知識(shí)再深入學(xué)習(xí)一下,如下:
繼續(xù)談status
上篇博客,我們了解到status是一個(gè)位圖結(jié)構(gòu),可以同時(shí)返回退出碼和退出信號(hào),并且退出碼是由32個(gè)比特位中的后16個(gè)比特位中的次第8位構(gòu)成的,退出信號(hào)是由最后7個(gè)比特位構(gòu)成的(core down先不談),所以按照這個(gè)邏輯,此時(shí)我們想要拿到,status中的退出信號(hào)和退出碼是有辦法的,沒錯(cuò),就是使用我們?cè)贑語言中所學(xué)的位運(yùn)算的概念,具體這里不多做講解(具體就是左移和右移),左移以為表示的就是該值乘2,右移一位表示的就是除2,并且還涉及到了按位或、按位與、按位異或的知識(shí),如下圖:
通過上述的 (status >> 8)& 0xFF、status & 0x7F
此時(shí)我們就可以獲得該進(jìn)程的退出碼和退出信號(hào)了,但是要知道,上述獲取子進(jìn)程退出碼和退出信號(hào)的方式,是我們自己通過位圖結(jié)構(gòu)的概念,通過位運(yùn)算獲得的,但是注意,在Linux操作系統(tǒng)內(nèi)部,它本身是為我們提供了獲取子進(jìn)程退出碼和退出信號(hào)的方式,如下圖:
所以按照我們上述所說,父進(jìn)程就可以通過使用waitpid接口,來調(diào)用系統(tǒng)調(diào)用,從而獲取子進(jìn)程的退出碼和退出信號(hào),但是具體在調(diào)用系統(tǒng)調(diào)用接口的時(shí)候,本質(zhì)上這些工作還是由我們的操作系統(tǒng)來完成了,因?yàn)橹挥胁僮飨到y(tǒng)才有獲取進(jìn)程退出碼和退出信號(hào)的資格,所以此時(shí)就有一個(gè)問題,就是父進(jìn)程具體是如何通過操作系統(tǒng)來獲取子進(jìn)程的退出碼和退出信號(hào)呢?
首先明白,無論是父進(jìn)程還是子進(jìn)程,它們都是一個(gè)獨(dú)立的進(jìn)程,所以每個(gè)進(jìn)程都擁有自己的地址空間,進(jìn)程pcb、頁表和物理內(nèi)存等資源;并且明白,子進(jìn)程pcb內(nèi)不僅有從父進(jìn)程pcb中繼承的相關(guān)屬性,而且還有兩個(gè)和子進(jìn)程退出碼和退出信號(hào)至關(guān)重要的屬性 (int exit_code ,int exit_signal)
,從該屬性的名字就可以看出,這兩個(gè)子進(jìn)程pcb屬性代表的就是進(jìn)程退出碼和退出信號(hào),表示的僅僅就是兩個(gè)整數(shù)而已;
明白了這些之后,此時(shí)就可以回答該問題了,如下:
當(dāng)子進(jìn)程執(zhí)行完畢之時(shí),操作系統(tǒng)會(huì)將main函數(shù)對(duì)應(yīng)的返回值寫到該進(jìn)程pcb的int exit_code當(dāng)中,如果該進(jìn)程異常,則將對(duì)應(yīng)的退出信號(hào)寫到該進(jìn)程pcb的int exit_signal當(dāng)中,并且最后在進(jìn)程退出之后,操作系統(tǒng)會(huì)將該進(jìn)程的pcb維護(hù)起來,所以當(dāng)該子進(jìn)程pcb被操作系統(tǒng)維護(hù)起來之后,此時(shí)父進(jìn)程就可以通過調(diào)用系統(tǒng)接口 wait/waitpid
通過操作系統(tǒng)來去找到該退出進(jìn)程對(duì)應(yīng)的進(jìn)程pcb,可以找到子進(jìn)程pcb的具體原理是因?yàn)?,父進(jìn)程在調(diào)用waitpid接口的時(shí)候,已經(jīng)把子進(jìn)程的pid告訴了父進(jìn)程(也就是傳參的時(shí)候已經(jīng)把進(jìn)程子pid給給了waitpid接口),所以最后通過子進(jìn)程pid,進(jìn)而找到該子進(jìn)程的pcb,進(jìn)而找到pcb中的該子進(jìn)程的退出碼 int exit_code
和退出信號(hào) int exit_signal
,最后再利用waitpid中的第二個(gè)參數(shù)status(輸出型參數(shù))將退出碼和退出信號(hào)返回給父進(jìn)程(當(dāng)然也就是上述的ret_id
變量)
總:父進(jìn)程獲取子進(jìn)程的退出碼和退出信號(hào)的本質(zhì)就是通過操作系統(tǒng)讀取子進(jìn)程的內(nèi)核數(shù)據(jù)結(jié)構(gòu)(pcb)
并且有的同學(xué)非常的好奇,就是有沒有一種可能,就是父進(jìn)程在等待的時(shí)候,子進(jìn)程一直沒有退出呢?或者說父進(jìn)程在等待的時(shí)候具體是在干什么呢?
所以此時(shí)我們就借著上述問題來談一談,什么是阻塞等待和非阻塞輪詢等待
首先我們要明白,當(dāng)子進(jìn)程沒有退出的時(shí)候,父進(jìn)程在干什么呢?
談到這個(gè),此時(shí)又不得不談?wù)剋aitpid的第三個(gè)參數(shù),options,上篇博客我們說了,這個(gè)參數(shù)的作用就是用于判斷子進(jìn)程是否正常終止,父進(jìn)程是否繼續(xù)等待問題,并且該參數(shù)我們默認(rèn)給給的是一個(gè)叫 WNOHANG
的參數(shù),具體意思:就是用于判斷該子進(jìn)程是否終止,如果該子進(jìn)程未終止,那么此時(shí)waitpid()函數(shù)就返回0,如果該子進(jìn)程終止,則返回該子進(jìn)程的pid,此時(shí)就可以很好的通過這第三個(gè)參數(shù)來判斷一個(gè)進(jìn)程的子進(jìn)程是否處于終止?fàn)顟B(tài),所以明白了這點(diǎn),此時(shí)就可以明白父進(jìn)程是可以通過該參數(shù)來控制自己的行為,也就是上述所說的阻塞等待和非阻塞輪詢等待!
談?wù)劯高M(jìn)程的阻塞等待
從上述的知識(shí),我們可以知道,如果waitpid()函數(shù)返回0,表示的就是該子進(jìn)程沒有結(jié)束,子進(jìn)程沒有結(jié)束,那么父進(jìn)程此時(shí)可以干什么呢?首先第一種情況,父進(jìn)程一直在調(diào)用waitpid進(jìn)行等待 ,此時(shí)的一直進(jìn)行等待,本質(zhì)上表示的就是阻塞等待,也就是表示父進(jìn)程此時(shí)從本來的正常運(yùn)行狀態(tài)變成了一個(gè)阻塞狀態(tài),變成了一個(gè)等待子進(jìn)程的狀態(tài);所以我們可以明白,當(dāng)父進(jìn)程處于阻塞狀態(tài)之時(shí),父進(jìn)程是不在CPU的運(yùn)行隊(duì)列之中,而是在某一個(gè)阻塞隊(duì)列之中;并且此時(shí)再深入理解,還可以明白,父進(jìn)程處于阻塞等待之時(shí),子進(jìn)程處于運(yùn)行狀態(tài),那么等子進(jìn)程執(zhí)行完之后呢?父進(jìn)程如何從阻塞狀態(tài)重新變成運(yùn)行狀態(tài)來為子進(jìn)程收尸呢? 談到這個(gè)問題,此時(shí)就又涉及到了,子進(jìn)程的進(jìn)程pcb,在子進(jìn)程pcb內(nèi)部,我們可以理解存在一個(gè)指向父進(jìn)程的指針,所以當(dāng)子進(jìn)程一但退出(注意此時(shí)父進(jìn)程肯定是不在運(yùn)行隊(duì)列,而是在阻塞隊(duì)列的),此時(shí)子進(jìn)程就可以利用pcb中指向父進(jìn)程的指針,重新找到父進(jìn)程(喚醒),此時(shí)父進(jìn)程檢測(cè)到之后(被喚醒),就又會(huì)從阻塞狀態(tài)變成運(yùn)行狀態(tài),然后被操作系統(tǒng)又重新鏈接到運(yùn)行隊(duì)列之中,最后父進(jìn)程繼續(xù)執(zhí)行 wait/waitpid
,最終就將子進(jìn)程給回收
所以上述就是父進(jìn)程阻塞等待全過程
談?wù)劯高M(jìn)程的非阻塞輪詢等待
搞懂了上述父進(jìn)程的阻塞等待,此時(shí)就可以明白父進(jìn)程肯定還有一種非阻塞等待(畢竟我是父進(jìn)程,你是小子,我才是老子,我憑什么一定要等你呢?)所以,此時(shí)還是同理,當(dāng)我們通過waitpid()函數(shù)的返回值,判斷出該子進(jìn)程還沒有結(jié)束時(shí),我們就可以讓父進(jìn)程不需要一直進(jìn)行等待(不需要一直處于阻塞狀態(tài)),具體代碼如下:
所以此時(shí)通過上述的代碼,我們就可以很好的理解什么是父進(jìn)程的阻塞等待和非阻塞輪詢等待了,并且可以將其很好的抽象成一個(gè)生活中場(chǎng)景,如下:
場(chǎng)景一:
周末出去玩,李四打電話給張三,問張三去不去,此時(shí)張三說去,但是要準(zhǔn)備一下(子進(jìn)程未終止),李四說好,我等你(父進(jìn)程等待),但是電話別掛(阻塞等待)
場(chǎng)景二:
如法炮制,還是周末出去玩,李四打電話給張三,問張三去不去,張三還是去,并且也還是需要準(zhǔn)備一下,此時(shí)李四還是說好,但是此時(shí)李四卻把電話給掛斷了,在電話掛斷之后,李四想了想也去準(zhǔn)備了一下,過了一會(huì)之后,李四準(zhǔn)備的差不多了,就又打電話給張三,張三卻還是沒有準(zhǔn)備好,此時(shí)李四就又把電話掛了,又去準(zhǔn)備東西了,同理,李四一直打電話問張三好了沒有(非阻塞輪詢等待),而張三卻一直沒好,直到最后好了(子進(jìn)程終止)
所以上述的兩種情況(李四給張三打電話,表示的就是父進(jìn)程使用waitpid()等待子進(jìn)程,)第一種情況電話不掛表示的就是父進(jìn)程處于阻塞隊(duì)列,這種情況也就是阻塞調(diào)用;第二種情況,表示我打完電話,問完情況,我們把電話掛掉,此時(shí)表示的就是父進(jìn)程不處于阻塞等待,也就是表示該調(diào)用,此時(shí)是屬于非阻塞調(diào)用,(非阻塞調(diào)用的好處,就是父進(jìn)程可以干自己想干的事情,例:準(zhǔn)備自己的東西)
所以上述的兩種場(chǎng)景就是父進(jìn)程阻塞等待和非阻塞等待的兩個(gè)現(xiàn)實(shí)生活抽象場(chǎng)景,可以非常好的凸顯出父進(jìn)程等待的兩種情況
總:父進(jìn)程可以通過參數(shù)進(jìn)行判斷,然后來控制自己的行為,也就是上述所說的阻塞等待和非阻塞輪詢等待!
總結(jié):進(jìn)程控制的三個(gè)話題:進(jìn)程創(chuàng)建、進(jìn)程等待、進(jìn)程終止此時(shí)就算告一段落啦!現(xiàn)在讓我們呢一起開始新知識(shí)的學(xué)習(xí)吧!
進(jìn)程替換
搞定了上述的知識(shí),此時(shí)我們就進(jìn)入今天的正菜,什么是進(jìn)程替換,在弄懂什么是進(jìn)程替換之前,我們引入一個(gè)問題,就是為什么要?jiǎng)?chuàng)建子進(jìn)程?所以我們要明白,我們創(chuàng)建子進(jìn)程的目的:就是為了讓子進(jìn)程幫我執(zhí)行某些特定的任務(wù),但是此時(shí)讓子進(jìn)程去執(zhí)行某些代碼的時(shí)候,就會(huì)出現(xiàn)如下兩種情況:
1. 讓子進(jìn)程執(zhí)行父進(jìn)程的部分代碼,也就是一個(gè)可執(zhí)行文件中的部分代碼(這些代碼本質(zhì)都是屬于父進(jìn)程)
2. 子進(jìn)程想要執(zhí)行一個(gè)全新的屬于自己的代碼(本質(zhì)就是不想執(zhí)行父進(jìn)程的代碼)
所以碰到上述第二種情況,此時(shí)就可以引出我們的新概念:進(jìn)程程序替換
如下圖:就是一個(gè)最簡單的進(jìn)程程序替換demo
所以使用execl接口的本質(zhì)就是進(jìn)行進(jìn)程的切換,讓該可執(zhí)行文件形成的進(jìn)程去執(zhí)行Linux操作系統(tǒng)中bin目錄下的ls指令(ls可執(zhí)行文件),也就是表明,當(dāng)我們使用了進(jìn)程替換函數(shù)時(shí),我們可以在任意一個(gè)可執(zhí)行文件中執(zhí)行任意的指令,本質(zhì)是進(jìn)程替換
總:進(jìn)程程序替換,就是讓一個(gè)進(jìn)程去執(zhí)行另一個(gè)在磁盤(Linux操作系統(tǒng)下的bin目錄)當(dāng)中的程序(可執(zhí)行文件),把一個(gè)新的程序運(yùn)行起來
程序替換的原理
首先明白,創(chuàng)建一個(gè)進(jìn)程,該進(jìn)程肯定是有對(duì)應(yīng)的pcb、頁表、地址空間和物理內(nèi)存,所以我們就從這些進(jìn)程的必要條件入手,來看看進(jìn)程替換到底是什么,如下圖:
從圖中,我們可以了解到以下知識(shí):
當(dāng)前進(jìn)程的代碼等數(shù)據(jù)是需要通過頁表映射到物理內(nèi)存的特定區(qū)域,所以當(dāng)一個(gè)進(jìn)程執(zhí)行了部分代碼后,當(dāng)其執(zhí)行到了系統(tǒng)調(diào)用接口(execl等……),進(jìn)程此時(shí)就會(huì)根據(jù)我所傳入的程序的路徑(Linux操作系統(tǒng)下的bin
目錄)和我要執(zhí)行的程序的名稱和選項(xiàng)(“l(fā)s” “a”
),然后把磁盤當(dāng)中(通過對(duì)應(yīng)的路徑找到bin)的其它的程序的代碼和數(shù)據(jù)加載到內(nèi)存,最后用新程序的代碼來替換我們?cè)绦蛑械拇a,用新程序的數(shù)據(jù)來替換原程序中的數(shù)據(jù)(這個(gè)過程不是在地址空間完成的,是在物理內(nèi)存中完成的);所以總的來說: 程序在替換時(shí),就相當(dāng)于原進(jìn)程的內(nèi)核數(shù)據(jù)結(jié)構(gòu)不變,把原進(jìn)程在物理內(nèi)存中的代碼和數(shù)據(jù)替換為新進(jìn)程的代碼和數(shù)據(jù)(從磁盤中加載),此時(shí)這個(gè)行為的發(fā)生就叫做程序替換。
并且要明白,進(jìn)程在進(jìn)行程序替換的時(shí)候,是沒有創(chuàng)建新的進(jìn)程的,本質(zhì)還是程序替換的本質(zhì)。
并且此時(shí)明白,可以進(jìn)行進(jìn)程程序替換的接口一共有7個(gè),此時(shí)我們已學(xué)其一,剩下的6個(gè)都是同理,只是在使用上不同而已,所以我們今天淺淺的摸了一下程序替換就行了(明天要晨跑),今天博客就這樣吧!撤了!
補(bǔ)充小知識(shí)點(diǎn):
fork函數(shù)的兩個(gè)返回值淺顯理解:一個(gè)是父進(jìn)程返回子進(jìn)程的pid,一個(gè)是子進(jìn)程沒有子進(jìn)程所以返回0,感興趣的同學(xué),可以參看下面這個(gè)鏈接;為什么fork有兩個(gè)返回值文章來源:http://www.zghlxwxcb.cn/news/detail-831581.html
北京時(shí)間:2023/3/21/22:45文章來源地址http://www.zghlxwxcb.cn/news/detail-831581.html
總結(jié):像程序替換(execl)這樣的系統(tǒng)接口,等我們學(xué)到了文件操作和網(wǎng)絡(luò)的時(shí)候,我們就經(jīng)常會(huì)談到,并且會(huì)用到更多,所以革命還未勝利,挑戰(zhàn)任在繼續(xù),加油吧,少年!
到了這里,關(guān)于學(xué)習(xí)系統(tǒng)編程N(yùn)o.7【進(jìn)程替換】的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!