當(dāng)前位置:首頁 > 公眾號精選 > wenzi嵌入式軟件
[導(dǎo)讀]引言在上一則發(fā)表的關(guān)于Linux的文章中,敘述了Linux的相關(guān)概念,其中就包括進(jìn)程的資源,進(jìn)程的狀態(tài),以及進(jìn)程的屬性等相關(guān)內(nèi)容,在本則教程中,將著重敘述Linux進(jìn)程管理的內(nèi)容,其中就包括Linux進(jìn)程的創(chuàng)建,進(jìn)程的終止,進(jìn)程的等待相關(guān)內(nèi)容。Linux進(jìn)程的創(chuàng)建函數(shù)fork現(xiàn)有...

引言

在上一則發(fā)表的關(guān)于 Linux 的文章中,敘述了 Linux 的相關(guān)概念,其中就包括進(jìn)程的資源,進(jìn)程的狀態(tài),以及進(jìn)程的屬性等相關(guān)內(nèi)容,在本則教程中,將著重敘述 Linux 進(jìn)程管理的內(nèi)容,其中就包括 Linux 進(jìn)程的創(chuàng)建,進(jìn)程的終止,進(jìn)程的等待相關(guān)內(nèi)容。

Linux 進(jìn)程的創(chuàng)建

函數(shù) fork

現(xiàn)有的一個進(jìn)程可以調(diào)用 fork 函數(shù)創(chuàng)建一個新進(jìn)程:

#include?
pid_t?fork(void);
/*?返回值:子進(jìn)程返回?0,父進(jìn)程返回子進(jìn)程 ID;若出錯,返回?-1 */
由 fork 創(chuàng)建的新進(jìn)程被稱為子進(jìn)程。fork 函數(shù)被調(diào)用一次,但返回兩次。兩次返回的區(qū)別是子進(jìn)程返回值是0,而父進(jìn)程的返回值是新建子進(jìn)程的進(jìn)程 ID,子進(jìn)程創(chuàng)建的過程大概是這樣的:從調(diào)用系統(tǒng)調(diào)用 fork 后就有了子進(jìn)程,fork?創(chuàng)建子進(jìn)程是以父進(jìn)程為模板的、

下面是一個 fork 函數(shù)創(chuàng)建一個進(jìn)程的例子:

int?main(int?argc,?char?**argv)
{
????printf("I?am?process!\r\n");
????pid_t?id?=?fork();
????if?(id?0)
????{
????????printf("fork?error\r\n");
????}
????else?if?(id?==?0)
????{
????????printf("I?am?child?process?and?myid?is?:%d,?my?parent?id?is?:%d\r\n",getpid(),getppid());
????????sleep(3);
????}
????else
????{
????????printf("I?am?parent?process?and?myid?is:%d\r\n",getpid());
????????sleep(3);
????}
????printf("Now?you?can?see?me!\r\n");
????sleep(3);
????return?0;
}
下面是代碼的運(yùn)行結(jié)果:

image-20210626175003144
在使用 fork 創(chuàng)建子進(jìn)程的時候,內(nèi)核所做的工作是:

  • 分配新的內(nèi)存塊和描述進(jìn)程的數(shù)據(jù)結(jié)構(gòu)給子進(jìn)程

  • 將父進(jìn)程部分?jǐn)?shù)據(jù)結(jié)構(gòu)內(nèi)容拷貝到子進(jìn)程

  • 添加子子進(jìn)程到系統(tǒng)進(jìn)程列表中

  • fork 返回,開始調(diào)度器調(diào)度

需要注意的是:fork 之前父進(jìn)程獨(dú)立運(yùn)行,fork 之后,父子兩個執(zhí)行流分別運(yùn)行。且 fork 之后,由調(diào)度器決定運(yùn)行順序

子進(jìn)程獲得父進(jìn)程數(shù)據(jù)空間、堆和棧的副本。需要注意的是,這是子進(jìn)程所擁有的副本。父進(jìn)程和子進(jìn)程并不共享這些存儲空間部分,但是由于在 fork 之后經(jīng)常跟隨著 exec,所以現(xiàn)在很多實現(xiàn)并不執(zhí)行一個父進(jìn)程數(shù)據(jù)段、堆和棧的完全副本,作為替代,使用了寫時復(fù)制技術(shù),這些區(qū)域由父進(jìn)程和子進(jìn)程共享,而且內(nèi)核將他們的訪問權(quán)限改變?yōu)橹蛔x。

寫時復(fù)制原理

在講述寫時復(fù)制的原理之前,首先得弄明白虛擬內(nèi)存和物理內(nèi)存兩個概念:

  • 物理內(nèi)存:也就是相電腦的內(nèi)存條,如果電腦安裝了 2GB 的內(nèi)存條,那么系統(tǒng)就擁有 0~2GB 的物理內(nèi)存空間。

  • 虛擬內(nèi)存:虛擬內(nèi)存是使用軟件模擬的,例如在 32 位的操作系統(tǒng)下,那么每個進(jìn)程都獨(dú)占 4GB 的虛擬內(nèi)存空間

應(yīng)用程序使用的是虛擬內(nèi)存,而虛擬內(nèi)存必須要映射到物理內(nèi)存中才可以使用,如果沒有映射到虛擬內(nèi)存地址,那么就會導(dǎo)致缺頁異常。下面是虛擬內(nèi)存和物理內(nèi)存映射時的一個示意圖:

image-20210626182114158
通過上述的示意圖可以看出來,引入了虛擬內(nèi)存的概念之后,兩個進(jìn)程相同的虛擬內(nèi)存地址能夠映射到不同的物理地址中。

在介紹了虛擬內(nèi)存和物理內(nèi)存之后,緊接著來介紹寫時復(fù)制的基本原理,在前面的介紹中,我們知道虛擬內(nèi)存要能夠進(jìn)行使用,必須映射到物理內(nèi)存,如果不同進(jìn)程的虛擬內(nèi)存地址映射到相同的物理內(nèi)存地址,那么就實現(xiàn)了共享內(nèi)存機(jī)制。也就是如下圖所示:

image-20210627101948327
通過上述的示意圖可以看出來,進(jìn)程 A 的虛擬內(nèi)存空間和進(jìn)程 B 的虛擬內(nèi)存空間映射到了一塊相同的物理內(nèi)存地址中,所以呢,當(dāng)修改進(jìn)程 A 的虛擬內(nèi)存空間的數(shù)據(jù)時,那么進(jìn)程 B 虛擬內(nèi)存的數(shù)據(jù)也會跟著改變。

依據(jù)這樣一個原理,實現(xiàn)了寫時復(fù)制的機(jī)制:

寫時復(fù)制的一個過程大致如下所示:

  • 創(chuàng)建子進(jìn)程時,將父進(jìn)程的虛擬內(nèi)存與物理內(nèi)存映射關(guān)系復(fù)制到子進(jìn)程,并將內(nèi)存設(shè)置為只讀

  • 當(dāng)子進(jìn)程或者父進(jìn)程對內(nèi)存數(shù)據(jù)進(jìn)行修改的時候,便會觸發(fā)寫時復(fù)制機(jī)制,將原來的內(nèi)存頁復(fù)制一份新的,并重新設(shè)置其內(nèi)存映射關(guān)系,將父子進(jìn)程的內(nèi)存讀寫權(quán)限設(shè)置為可讀寫。

image-20210627103516488
但這個時候只能對內(nèi)存進(jìn)行讀操作,如果父進(jìn)程或子進(jìn)程對內(nèi)存進(jìn)行寫操作,那么將會觸發(fā)?缺頁異常,而在?缺頁異常?處理中會對物理內(nèi)存進(jìn)行復(fù)制,并且重新映射其內(nèi)存映射關(guān)系,這也就是寫時復(fù)制的機(jī)制。

回過頭來,對于 fork 來講,有以下兩種用法:

  • 一個父進(jìn)程希望復(fù)制自己,使得父進(jìn)程和子進(jìn)程同時執(zhí)行不同的代碼段,這在網(wǎng)絡(luò)服務(wù)進(jìn)程中是常見的,父進(jìn)程等待客戶端的服務(wù)請求。當(dāng)這種請求到達(dá)的時候,父進(jìn)程調(diào)用 fork ,使子進(jìn)程處理此請求。父進(jìn)程則繼續(xù)等待下一服務(wù)請求。

  • 一個進(jìn)程要執(zhí)行一個不同的程序,在這種情況下,子進(jìn)程調(diào)用 fork 返回后立即調(diào)用 exec 。

而調(diào)用 fork 失敗的原因主要是:

  • 系統(tǒng)中已經(jīng)有太多的進(jìn)程了

  • 該實際用戶 ID 的進(jìn)程總數(shù)超過了系統(tǒng)限制

進(jìn)程中止

進(jìn)程有五種正常終止以及3種異常終止方式。首先敘述下5種正常的終止方式:

  • 在 main 函數(shù)中執(zhí)行 return 語句,這等效于調(diào)用 exit。

  • 調(diào)用 exit 函數(shù)

  • 調(diào)用 _exit或 _Exit,對于 _Exit 來說,其目的是為進(jìn)程提供一種無需運(yùn)行終止處理程序或者信號處理程序而終止的方法。

  • 進(jìn)程的最后一個線程在啟動例程中執(zhí)行 return 語句。但是,該線程的返回值不用作進(jìn)程的返回值。當(dāng)最后一個線程從其啟動例程返回時,該進(jìn)程以終止?fàn)顟B(tài) 0 返回。

  • 進(jìn)程的最后一個線程調(diào)用?pthread_exit函數(shù),與前面一樣,進(jìn)程的終止?fàn)顟B(tài)總是?0。

三種異常終止具體如下:

  • 調(diào)用?abort,產(chǎn)生 SIGABRT 信號,這是下一種異常終止的特例。

  • 當(dāng)進(jìn)程收到某些信號時

  • 最后一個進(jìn)程對“取消”請求做出響應(yīng)

不管進(jìn)程如何終止,最后都會執(zhí)行內(nèi)核中的同一段代碼。這段代碼為相應(yīng)進(jìn)程關(guān)閉所有打開描述符,釋放它所使用的存儲器。

函數(shù) wait 和 waitpid

調(diào)用 wait 和 waitpid 會發(fā)生如下幾件事:

  • 如果所有子進(jìn)程都還在運(yùn)行,那么就阻塞

  • 如果一個子進(jìn)程已經(jīng)中止,正等待父進(jìn)程獲取其終止?fàn)顟B(tài),則取得該子進(jìn)程的終止?fàn)顟B(tài)并返回

  • 如果它沒有任何子進(jìn)程,則立即出錯返回。

如果進(jìn)程是在接受到 SIGABRT 信號而調(diào)用 wait ,我們期望 wait 會立即返回,但是如果是在隨機(jī)時間點(diǎn)調(diào)用 wait ,那么進(jìn)程可能會阻塞。

下面是這兩個函數(shù)的原型:

#include?

pid_t?wait(int?*statloc);
pid_t?waitpid(pid_t?pid,int?*statloc,int?options);
/*?兩個函數(shù)返回值:若成功,則返回進(jìn)程 ID;若失敗,則返回?0?或者?-1 */
除了這兩個函數(shù)之外,類似的調(diào)用還有其他的函數(shù),這里就不進(jìn)行贅述了。

競爭條件

當(dāng)多個進(jìn)程都企圖對共享數(shù)據(jù)進(jìn)行某種處理,而最后的結(jié)果又取決于進(jìn)程運(yùn)行的順序時,我們認(rèn)為發(fā)生了競爭條件。如果在 fork 之后的某種邏輯顯示或隱式地依賴于在 fork 之后是父進(jìn)程先運(yùn)行還是子進(jìn)程先運(yùn)行,那么 fork 函數(shù)就會是競爭條件活躍的滋生地。

如果一個進(jìn)程希望等待一個子進(jìn)程終止,則它必須調(diào)用 wait 函數(shù)中的一個,如果一個進(jìn)程要等待其父進(jìn)程終止,則可以使用下列形式的循環(huán):

while?(getppid()?!=?1)
????sleep(1);
這種形式的循環(huán)稱為輪詢,它的問題是浪費(fèi)了 CPU 時間,因為調(diào)用者每隔 1s 都被喚醒,然后進(jìn)行條件測試,為了避免競爭條件和輪詢,在多個進(jìn)程之間需要有某種形式的信號發(fā)送和接收的方法。詳細(xì)地在下次進(jìn)行敘述。

函數(shù) exec

在使用了 fork 函數(shù)創(chuàng)建新的子進(jìn)程后,子進(jìn)程往往要調(diào)用一種 exec 函數(shù)以執(zhí)行另一個程序。當(dāng)進(jìn)程調(diào)用一種 exec 函數(shù)時,該進(jìn)程執(zhí)行的程序完全替換為新程序。通俗地理解這句話,也就是說,在 Window 平臺下,我們可以通過雙擊運(yùn)行可執(zhí)行程序,讓這個可執(zhí)行程序成為一個進(jìn)程;然而在 Linux 平臺下,我們可以通過運(yùn)行?./,讓一個可執(zhí)行程序成為一個進(jìn)程。

如果我們本來就運(yùn)行著一個程序(進(jìn)程),如何在這個進(jìn)程內(nèi)部啟動一個外部程序,由內(nèi)核將這個外部程序讀入內(nèi)存,使其執(zhí)行起來成為一個進(jìn)程呢?這里通過?exec函數(shù)族來實現(xiàn)。

exec函數(shù)族,顧名思義,也就是一族函數(shù),在 Linux 中,也不存在著exec()函數(shù),exec指的是一組函數(shù) :

#include?
int?execl(const?char?*path,?const?char?*arg,?...);
int?execlp(const?char?*file,?const?char?*arg,?...);
int?execle(const?char?*path,?const?char?*arg,?...,?char?*?const?envp[]);
int?execv(const?char?*path,?char?*const?argv[]);
int?execvp(const?char?*file,?char?*const?argv[]);
int?execve(const?char?*path,?char?*const?argv[],?char?*const?envp[]);
其中只有execve()是真正意義上的系統(tǒng)調(diào)用,其它都是在此基礎(chǔ)上經(jīng)過包裝的庫函數(shù)。

進(jìn)程調(diào)用一種 exec 函數(shù)時,該進(jìn)程完全由新程序替換,而新程序則從其 main 函數(shù)開始執(zhí)行。因為調(diào)用 exec 并不創(chuàng)建新進(jìn)程,所以前后的進(jìn)程 ID (當(dāng)然還有父進(jìn)程號、進(jìn)程組號、當(dāng)前工作目錄……)并未改變。exec 只是用另一個新程序替換了當(dāng)前進(jìn)程的正文、數(shù)據(jù)、堆和棧段(進(jìn)程替換)。

image-20210627152307774
接下來舉一個例子,關(guān)于execl()?示例代碼:

#include?
#include?

int?main(int?argc,?char?*argv[])
{
?????printf("before?exec\n\n");

?????/*?/bin/ls:外部程序,這里是/bin目錄的 ls 可執(zhí)行程序,必須帶上路徑(相對或絕對)
?????? ls:沒有意義,如果需要給這個外部程序傳參,這里必須要寫上字符串,至于字符串內(nèi)容任意
???????-a,-l,-h:給外部程序 ls 傳的參數(shù)
?????? NULL:這個必須寫上,代表給外部程序 ls 傳參結(jié)束
????*/

?????execl("/bin/ls",?"ls",?"-a",?"-l",?"-h",?NULL);

?????//?如果?execl()?執(zhí)行成功,下面執(zhí)行不到,因為當(dāng)前進(jìn)程已經(jīng)被執(zhí)行的?ls?替換了
?????perror("execl");
?????printf("after?exec\n\n");

?????return?0;
}
下面是代碼執(zhí)行的結(jié)果:

image-20210627153014964

小結(jié)

本次內(nèi)容的分享就到這里了,主要是敘述了Linux進(jìn)程管理的相關(guān)內(nèi)容,其中就包括Linux進(jìn)程創(chuàng)建,進(jìn)程中止,進(jìn)程等待等內(nèi)容,在下一則內(nèi)容中將著重分享進(jìn)程間通信的相關(guān)內(nèi)容,每周一篇,堅持呀~


本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉