引言:
北京時(shí)間:2023/4/15/10:34,今天起床時(shí)間9:25,睡了快8小時(shí),昨天刷視屏刷了一個(gè)小時(shí),本來12點(diǎn)的時(shí)候發(fā)完博客洗把臉就要睡了,可惜,看到了一個(gè)標(biāo)題,說實(shí)話,現(xiàn)在的標(biāo)題黨是懂人性的,接下來就是無法自拔的一個(gè)小時(shí)快樂時(shí)光,但導(dǎo)致莫名間接熬夜,你說煩人不煩人!但是不怕,這個(gè)星期5天,幾乎沒有擺爛,只要今天和明天不擺爛 ,這個(gè)星期就是成功滴,一想美滋滋!所以讓我們抓緊進(jìn)入今天的學(xué)習(xí)吧! 今天我們就正式學(xué)習(xí)有關(guān)內(nèi)存共享的知識,當(dāng)然還有管道遺落的知識,因?yàn)楣艿勒娴氖翘?jīng)典了
,但是前提是,現(xiàn)在我要去把我的被單和毯子給曬一曬,不然晚上無法舒舒服服的睡覺啦!并且由于昨天買的羽毛球拍到了,所以下午是打羽毛球的快樂時(shí)光,所以該篇博客,今天能不能發(fā),是未可知的!
回顧管道控制進(jìn)程
之前開始學(xué)習(xí)進(jìn)程間通信的時(shí)候,我們就學(xué)習(xí)了如何使用管道來進(jìn)行進(jìn)程間通信,了解了通過一個(gè)管道進(jìn)行進(jìn)程間通信之后,我們又通過管道的讀寫規(guī)則了解了"血緣關(guān)系"
進(jìn)程之間的通信方法,此時(shí)我們通過一個(gè) 很經(jīng)典的通信方式(當(dāng)管道中沒有數(shù)據(jù)的時(shí)候,對應(yīng)讀取數(shù)據(jù)的進(jìn)程只能等待對應(yīng)寫數(shù)據(jù)的進(jìn)程)
,所以通過這一重要的讀寫原理,此時(shí)我們就設(shè)計(jì)出了進(jìn)程控制的結(jié)構(gòu)
,一個(gè)進(jìn)程(常常是父進(jìn)程)通過創(chuàng)建多個(gè)管道和子進(jìn)程,然后向?qū)?yīng)的管道中寫入數(shù)據(jù),進(jìn)而控制與該管道文件匹配的子進(jìn)程,所以明白了這個(gè)原理之后,此時(shí)就可以進(jìn)行具體的編碼和優(yōu)化,最終得到了上篇博客所示的進(jìn)程控制代碼,但是該代碼中,還存在一個(gè)很隱蔽的問題,上篇博客,我們沒有進(jìn)行詳細(xì)的講解,所以在這里,我們進(jìn)行補(bǔ)充講解,如下:
回收子進(jìn)程
1.讓子進(jìn)程退出
從進(jìn)程控制代碼中(上篇博客),我們看出,當(dāng)我們構(gòu)建好了進(jìn)程控制代碼,可以讓父進(jìn)程通過對管道文件的讀寫規(guī)則同時(shí)去控制子進(jìn)程完成不同的任務(wù)時(shí),在最后任務(wù)完成結(jié)束,代碼要退出時(shí),我們是需要將子進(jìn)程進(jìn)行回收(因?yàn)樽舆M(jìn)程占用的是內(nèi)存上的空間,避免內(nèi)存泄露),所以此時(shí)在回收子進(jìn)程的問題上,就存在那個(gè)很隱蔽的問題,此時(shí)我們就需要解決這個(gè)問題,如下:
想要解決這個(gè)問題,首先明白一點(diǎn),就是如何讓子進(jìn)程退出,這點(diǎn)想必大家都很清楚(上篇博客詳細(xì)介紹了),想要讓一個(gè)子進(jìn)程退出(場景:上篇博客代碼,子進(jìn)程讀取管道文件,父進(jìn)程向管道文件寫入),只要讓父進(jìn)程無法向?qū)?yīng)子進(jìn)程的管道文件中寫入數(shù)據(jù),此時(shí)操作系統(tǒng)就會因?yàn)椴辉试S資源浪費(fèi),使 用13號信號
,讓子進(jìn)程退出,所以讓一個(gè)子進(jìn)程退出,就是將對應(yīng)管道文件的寫端關(guān)閉就行
明白了這點(diǎn),此時(shí)我們距離解決這個(gè)隱蔽問題就剩下一個(gè)現(xiàn)象,看到了這個(gè)現(xiàn)象,此時(shí)一切都是So,So,如下圖所示:
如上圖所示,明白到了,除了第一次循環(huán)的子進(jìn)程是只繼承了一個(gè)寫文件描述符,其它子進(jìn)程的寫文件描述符都是在累加,所以導(dǎo)致除了最后一次循環(huán)創(chuàng)建的那個(gè)管道文件的寫端只有父進(jìn)程指向之外(因?yàn)樽詈笠淮窝h(huán)子進(jìn)程為了構(gòu)建單向信道,需要把寫端關(guān)閉),其它的管道文件都被除了父進(jìn)程以外的一個(gè)或多個(gè)進(jìn)程以寫的方式指向(如上圖,第五次循環(huán)之后的子進(jìn)程,此時(shí)對前面創(chuàng)建的4個(gè)管道文件的寫端都有寫入的能力),具體如下圖所示:
所以上圖的第一個(gè)管道文件的寫端由于被不止一個(gè)進(jìn)程打開,所以此時(shí)單單只是將父進(jìn)程對應(yīng)的寫端關(guān)閉,還有剩下多個(gè)子進(jìn)程可以向該管道文件寫入數(shù)據(jù),所以此時(shí)操作系統(tǒng)并不會使用13號信號
,將該子進(jìn)程退出,所以最好的解決方法就是如上圖所示:
從后向前關(guān)閉父進(jìn)程對應(yīng)管道文件的寫端,這樣就可以讓子進(jìn)程挨個(gè)退出了,進(jìn)而最終達(dá)到回收子進(jìn)程的目的
有關(guān)上述現(xiàn)象圖的繼承理解:注意:
在常見的操作系統(tǒng)中,當(dāng)子進(jìn)程從父進(jìn)程中繼承文件描述符表時(shí),子進(jìn)程會得到指向同一文件表項(xiàng)的新的文件描述符副本(繼承),關(guān)閉一個(gè)文件描述符只會減少該文件描述符所指向的文件表項(xiàng)的引用計(jì)數(shù),如果仍然有其他文件描述符指向該文件表項(xiàng),則該文件表項(xiàng)仍然存在;因此,無論父進(jìn)程關(guān)閉其中的一個(gè)文件描述符或者兩個(gè),當(dāng)子進(jìn)程從它那里繼承文件描述符表時(shí),子進(jìn)程都會得到對這兩個(gè)打開文件的文件描述符
總結(jié): 只要父進(jìn)程分別以讀寫的方式打開了同一文件,創(chuàng)建了兩個(gè)文件描述符,那么此時(shí)無論父進(jìn)程是關(guān)閉一個(gè)文件描述符,還是兩個(gè)都關(guān)閉,此時(shí)由于子進(jìn)程繼承了父進(jìn)程pcb大部分的內(nèi)容,并且因?yàn)?strong>進(jìn)程間具有獨(dú)立性,所以此時(shí)操作系統(tǒng)檢測都父進(jìn)程將要關(guān)閉文件描述符的時(shí)候(也就是修改數(shù)據(jù)的時(shí)候),就會進(jìn)行寫實(shí)拷貝(在內(nèi)存中重新開辟一個(gè)空間給父進(jìn)程去修改數(shù)據(jù)),所以導(dǎo)致子進(jìn)程中繼承的父進(jìn)程pcb是不會被改變,所以子進(jìn)程任然可以分別繼承以讀寫方式打開的兩個(gè)文件描述符
2.等待僵尸狀態(tài)的子進(jìn)程
明白了上述知識點(diǎn)之后,此時(shí)就可以明白除了最后一次循環(huán)創(chuàng)建的管道文件只被一個(gè)進(jìn)程以寫的方式打開之外,其它的管道文件都是同時(shí)被兩個(gè)或者兩個(gè)以上的進(jìn)程以寫的方式打開(父進(jìn)程和子進(jìn)程),所以最好的方法就是從后向前將父進(jìn)程對應(yīng)管道文件的寫端關(guān)閉,此時(shí)所有的子進(jìn)程就被很好的關(guān)閉了,所以當(dāng)子進(jìn)程被關(guān)閉之后,此時(shí)最后一步就是將處于僵尸狀態(tài)的子進(jìn)程回收(waitpid
),具體代碼如下:
具體現(xiàn)象如下:
所以此時(shí)因?yàn)檫M(jìn)程控制創(chuàng)建出的被控制進(jìn)程和創(chuàng)建的管道文件就都被關(guān)閉和回收啦!利用管道控制進(jìn)程的代碼就大功告成啦! 具體代碼貼在該博客最后(雖然上篇博客中有)
深入進(jìn)程控制結(jié)構(gòu)
通過上述的知識,我們知道,我們可以很好的利用進(jìn)程間通信的知識和模板去構(gòu)建出一個(gè)進(jìn)程控制的結(jié)構(gòu),并且這個(gè)結(jié)構(gòu)中存在的問題(上述問題),因?yàn)樽舆M(jìn)程會繼承上一進(jìn)程打開的所有文件描述符,導(dǎo)致一個(gè)子進(jìn)程同時(shí)具有多個(gè)讀端和寫端,此時(shí)根據(jù)這個(gè)現(xiàn)象,就會存在很多的問題,不僅僅只是上述回收子進(jìn)程的時(shí)候需要從后向前回收,更重要的是 ,此時(shí)還有引起另一個(gè)嚴(yán)重問題,就是后一個(gè)子進(jìn)程也可以向前一個(gè)管道文件中寫入數(shù)據(jù),有甚者更是可以向多個(gè)管道文件中寫入數(shù)據(jù)(雖然因?yàn)闆]有特定的文件描述符,子進(jìn)程并不能寫入,但是為了防止),所以需要避免這個(gè)問題,此時(shí)我們就可以通過將代碼結(jié)構(gòu)進(jìn)行一定的改變,進(jìn)而真正的構(gòu)建出像進(jìn)程間通信一般的單向信道,如下:
原理:本質(zhì)上,在實(shí)現(xiàn)上述進(jìn)程控制的前提下,讓每一個(gè)管道和進(jìn)程之間都互不干擾,實(shí)現(xiàn)單向通信是不難的,實(shí)際從問題出發(fā),就是要解決子進(jìn)程繼承父進(jìn)程文件描述符時(shí),多余進(jìn)程的文件描述符而已,所以此時(shí)我們只需要再創(chuàng)建一個(gè)數(shù)組,用這個(gè)數(shù)組專門來保存父進(jìn)程創(chuàng)建的寫端文件描述符,得到父進(jìn)程所有的寫端文件描述符,然后把這個(gè)數(shù)組中的數(shù)據(jù)拿給子進(jìn)程使用,間接在子進(jìn)程中遍歷這個(gè)數(shù)組,并且使用close關(guān)閉子進(jìn)程繼承的文件描述符表中對應(yīng)數(shù)組下標(biāo)對應(yīng)的寫端文件描述符,這樣子進(jìn)程中多余進(jìn)程到的寫端文件描述符就被全部關(guān)閉,最終真正的實(shí)現(xiàn)單向信道進(jìn)程控制結(jié)構(gòu)
具體代碼實(shí)現(xiàn)如下:
命名管道
無論是之前的進(jìn)程間數(shù)據(jù)傳送的知識,還是上述進(jìn)程控制的知識,本質(zhì)上都只是在利用管道進(jìn)行而已,準(zhǔn)確的來說也就是利用一個(gè)內(nèi)存級的文件而已,并且要明白,此時(shí)的這個(gè)管道是使用系統(tǒng)調(diào)用接口 pipe
來創(chuàng)建的,所以本質(zhì)上這個(gè)管道文件是操作系統(tǒng)給我們提供的,我們是摸不著,看不到的,所以準(zhǔn)確的來說,我們之前學(xué)習(xí)的進(jìn)程數(shù)據(jù)傳送和進(jìn)程控制利用的管道都是匿名管道,一個(gè)由內(nèi)核創(chuàng)建,操作系統(tǒng)管理的文件
并且還可以得出一個(gè)結(jié)論,我們一直利用的都是具有"血緣關(guān)系"
的進(jìn)程可以繼承同一文件描述符的特性和同時(shí)以讀寫方式打開同一匿名管道文件進(jìn)行學(xué)習(xí)有關(guān)進(jìn)程間通信的知識,此時(shí)提出問題:那么如果是兩個(gè)完全沒有關(guān)系的進(jìn)程,此時(shí)進(jìn)程間還可以進(jìn)行通信嗎?首先答案是可以的,不然它怎么能叫進(jìn)程間通信呢?就應(yīng)該叫血緣關(guān)系進(jìn)程進(jìn)程間通信了,具體如下述所說:
如何創(chuàng)建命名管道
首先明白,想要讓兩個(gè)不同的進(jìn)程支持相互通信,那么此時(shí)就不可以使用匿名管道,而要用命名管道,所以下面第一個(gè)知識點(diǎn),我們就來了解一下命名管道的創(chuàng)建,注意
:命名管道的創(chuàng)建區(qū)別于匿名管道的創(chuàng)建,它不僅可以直接使用命令行創(chuàng)建,指令:mkfifo filename
,也可以使用系統(tǒng)調(diào)用創(chuàng)建,調(diào)用接口:int mkfifo(const char *filename,mode_t mode);
基本使用:mkfifo("filename", 0644);
具體使用方式如下圖所示:
通過上圖中對 mkfifo
系統(tǒng)調(diào)用接口的描述,此時(shí)我們就可以知道,該接口的功能就是用來創(chuàng)建一個(gè)命名管道,頭文件為#include<sys/types.h>,#include<sys/stat.h>
等具體使用方式,此時(shí)我們就可以很好的自己創(chuàng)建一個(gè)命名管道出來啦!
注意:
雖然,mkfifo
是創(chuàng)建一個(gè)文件,并且這個(gè)文件不像是匿名管道文件一樣是由內(nèi)核創(chuàng)建的內(nèi)存級文件(不占用磁盤空間),并且該文件是位于文件系統(tǒng)中,但該文件卻并不對應(yīng)任何物理磁盤上的文件,而只是在內(nèi)存中分配一個(gè)空間來存儲不同進(jìn)程之間進(jìn)行進(jìn)程間通信時(shí)的讀取或?qū)懭氲臄?shù)據(jù)而已(類似于文件對象自帶的緩沖區(qū)),所以從這個(gè)意義上來說,我們可以將mkfifo創(chuàng)建的文件視為一種內(nèi)存級文件,因?yàn)樗鼈兊拇嬖趦H限于進(jìn)程之間的通信,當(dāng)進(jìn)程間完成通信之后,回收進(jìn)程時(shí),此時(shí)由于它們的生命周期與它們關(guān)聯(lián)的進(jìn)程相同,所以當(dāng)這些進(jìn)程終止時(shí),這些文件也會被自動刪除,所以本質(zhì)上命名管道文件就是一個(gè)內(nèi)存級緩沖區(qū)文件!
得出結(jié)論: 使用 mkfifo
創(chuàng)建的文件并不會占用磁盤空間,因?yàn)樗皇且粋€(gè)命名管道(內(nèi)存級文件),只有在進(jìn)程進(jìn)行通信時(shí),也就是一個(gè)進(jìn)程打開一個(gè)命名管道并向其中寫入數(shù)據(jù),另一個(gè)進(jìn)程打開同一個(gè)命名管道并從中讀取數(shù)據(jù),此時(shí)才會產(chǎn)生實(shí)際的數(shù)據(jù)傳輸和占用磁盤空間,否則執(zhí)行 mkfifo
命令只會在文件系統(tǒng)中創(chuàng)建一個(gè)新的文件節(jié)點(diǎn),并不會分配任何實(shí)際的磁盤空間。
管道文件類型:
對文件類型和文件屬性感興趣的同學(xué)可以參考該鏈接博客:文件類型詳解
文件類型 |
---|
普通文件 - |
目錄文件 d |
鏈接文件 l |
塊設(shè)備 b |
字符設(shè)備 c |
管道文件 p |
套接字文件 s |
深入命名管道
搞定了上述有關(guān)命名管道文件的知識,此時(shí)我們就可以具體的來看一看,是如何利用命名管道實(shí)現(xiàn)不同進(jìn)程之間的通信了,如下圖所示:
如上圖所示:此時(shí)我們就構(gòu)建出了一個(gè)和構(gòu)建父子進(jìn)程間通信結(jié)構(gòu)類似的模板,此時(shí)按照這個(gè)原理,我們就可以進(jìn)行代碼的編寫啦!簡單理解就是兩個(gè)進(jìn)程打開了同一個(gè)文件,一個(gè)進(jìn)程以讀的方式打開,一個(gè)進(jìn)程以寫的方式打開,本質(zhì)能夠通信還是因?yàn)?,操作系統(tǒng)為了節(jié)約資源,所有進(jìn)程共享同一個(gè)已經(jīng)打開的文件,而不是重復(fù)打開,并且 注意,
如果我們想要讓不同的兩個(gè)進(jìn)程打開同一個(gè)文件,此時(shí)就需要讓它們根據(jù)同一路徑
去尋找該對應(yīng)文件**,所以只要讓不同的進(jìn)程通過文件路徑和文件名看到同一文件,并打開,這就是兩個(gè)不同進(jìn)程看到同一份資源的前提,也就是進(jìn)程間通信的前提!
代碼實(shí)現(xiàn)
原理: 1.創(chuàng)建一個(gè)管道文件 2.讓讀寫端進(jìn)程分別按照自己的需求打開文件 3.開始通信
首先創(chuàng)建兩個(gè)文件,一個(gè)serves.cpp
,一個(gè)client.cpp
,用來分別表示兩個(gè)不一樣的進(jìn)程,具體代碼如下:
serves.cpp文件
client.cpp文件
command.hpp共享文件
兩個(gè)進(jìn)程代碼通信現(xiàn)象:
如上圖所示,此時(shí)一個(gè)進(jìn)程就可以很好的把數(shù)據(jù)傳送給另一個(gè)進(jìn)程,也就類似于一個(gè)用戶端可以把數(shù)據(jù)傳送給服務(wù)端
總:明白了使用匿名管道的進(jìn)程控制,玩一個(gè)命名管道So,So!
總結(jié):快樂生活每一天,有關(guān)管道的知識就這樣了吧!
使用匿名管道進(jìn)行進(jìn)程控制代碼如下:文章來源:http://www.zghlxwxcb.cn/news/detail-415752.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-415752.html
到了這里,關(guān)于學(xué)習(xí)系統(tǒng)編程N(yùn)o.20【進(jìn)程間通信之命名管道】的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!