0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深度剖析Linux中進(jìn)程控制(上)

jf_78858299 ? 來(lái)源:一口Linux ? 作者:土豆居士 ? 2023-05-12 10:49 ? 次閱讀

一、進(jìn)程創(chuàng)建

fork函數(shù)初識(shí)

Linux中,fork函數(shù)是非常重要的函數(shù),它從已存在進(jìn)程中創(chuàng)建一個(gè)新進(jìn)程。新進(jìn)程為子進(jìn)程,而原進(jìn)程為父進(jìn)程。

返回值:

在子進(jìn)程中返回0,父進(jìn)程中返回子進(jìn)程的PID,子進(jìn)程創(chuàng)建失敗返回-1。

進(jìn)程調(diào)用fork,當(dāng)控制轉(zhuǎn)移到內(nèi)核中的fork代碼后,內(nèi)核做:

  • 分配新的內(nèi)存塊和內(nèi)核數(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)程列表當(dāng)中。
  • fork返回,開(kāi)始調(diào)度器調(diào)度。

fork之后,父子進(jìn)程代碼共享。例如:

圖片

運(yùn)行結(jié)果如下:

圖片

這里可以看到,Before只輸出了一次,而After輸出了兩次。其中,Before是由父進(jìn)程打印的,而調(diào)用fork函數(shù)之后打印的兩個(gè)After,則分別由父進(jìn)程和子進(jìn)程兩個(gè)進(jìn)程執(zhí)行。也就是說(shuō),fork之前父進(jìn)程獨(dú)立執(zhí)行,而fork之后父子兩個(gè)執(zhí)行流分別執(zhí)行。

注意: fork之后,父進(jìn)程和子進(jìn)程誰(shuí)先執(zhí)行完全由調(diào)度器決定。

fork函數(shù)返回值

fork函數(shù)為什么要給子進(jìn)程返回0,給父進(jìn)程返回子進(jìn)程的PID?

一個(gè)父進(jìn)程可以創(chuàng)建多個(gè)子進(jìn)程,而一個(gè)子進(jìn)程只能有一個(gè)父進(jìn)程。因此,對(duì)于子進(jìn)程來(lái)說(shuō),父進(jìn)程是不需要被標(biāo)識(shí)的;而對(duì)于父進(jìn)程來(lái)說(shuō),子進(jìn)程是需要被標(biāo)識(shí)的,因?yàn)楦高M(jìn)程創(chuàng)建子進(jìn)程的目的是讓其執(zhí)行任務(wù)的,父進(jìn)程只有知道了子進(jìn)程的PID才能很好的對(duì)該子進(jìn)程指派任務(wù)。

為什么fork函數(shù)有兩個(gè)返回值?

父進(jìn)程調(diào)用fork函數(shù)后,為了創(chuàng)建子進(jìn)程,fork函數(shù)內(nèi)部將會(huì)進(jìn)行一系列操作,包括創(chuàng)建子進(jìn)程的進(jìn)程控制塊、創(chuàng)建子進(jìn)程的進(jìn)程地址空間、創(chuàng)建子進(jìn)程對(duì)應(yīng)的頁(yè)表等等。子進(jìn)程創(chuàng)建完畢后,操作系統(tǒng)還需要將子進(jìn)程的進(jìn)程控制塊添加到系統(tǒng)進(jìn)程列表當(dāng)中,此時(shí)子進(jìn)程便創(chuàng)建完畢了。

圖片

也就是說(shuō),在fork函數(shù)內(nèi)部執(zhí)行return語(yǔ)句之前,子進(jìn)程就已經(jīng)創(chuàng)建完畢了,那么之后的return語(yǔ)句不僅父進(jìn)程需要執(zhí)行,子進(jìn)程也同樣需要執(zhí)行,這就是fork函數(shù)有兩個(gè)返回值的原因。

寫時(shí)拷貝

當(dāng)子進(jìn)程剛剛被創(chuàng)建時(shí),子進(jìn)程和父進(jìn)程的數(shù)據(jù)和代碼是共享的,即父子進(jìn)程的代碼和數(shù)據(jù)通過(guò)頁(yè)表映射到物理內(nèi)存的同一塊空間。只有當(dāng)父進(jìn)程或子進(jìn)程需要修改數(shù)據(jù)時(shí),才將父進(jìn)程的數(shù)據(jù)在內(nèi)存當(dāng)中拷貝一份,然后再進(jìn)行修改。

圖片

這種在需要進(jìn)行數(shù)據(jù)修改時(shí)再進(jìn)行拷貝的技術(shù),稱為寫時(shí)拷貝技術(shù)。

1、為什么數(shù)據(jù)要進(jìn)行寫時(shí)拷貝?

進(jìn)程具有獨(dú)立性。多進(jìn)程運(yùn)行,需要獨(dú)享各種資源,多進(jìn)程運(yùn)行期間互不干擾,不能讓子進(jìn)程的修改影響到父進(jìn)程。

2、為什么不在創(chuàng)建子進(jìn)程的時(shí)候就進(jìn)行數(shù)據(jù)的拷貝?

子進(jìn)程不一定會(huì)使用父進(jìn)程的所有數(shù)據(jù),并且在子進(jìn)程不對(duì)數(shù)據(jù)進(jìn)行寫入的情況下,沒(méi)有必要對(duì)數(shù)據(jù)進(jìn)行拷貝,我們應(yīng)該按需分配,在需要修改數(shù)據(jù)的時(shí)候再分配(延時(shí)分配),這樣可以高效的使用內(nèi)存空間。

3、代碼會(huì)不會(huì)進(jìn)行寫時(shí)拷貝?

90%的情況下是不會(huì)的,但這并不代表代碼不能進(jìn)行寫時(shí)拷貝,例如在進(jìn)行進(jìn)程替換的時(shí)候,則需要進(jìn)行代碼的寫時(shí)拷貝。

fork常規(guī)用法

  1. 一個(gè)進(jìn)程希望復(fù)制自己,使子進(jìn)程同時(shí)執(zhí)行不同的代碼段。例如父進(jìn)程等待客戶端請(qǐng)求,生成子進(jìn)程來(lái)處理請(qǐng)求。
  2. 一個(gè)進(jìn)程要執(zhí)行一個(gè)不同的程序。例如子進(jìn)程從fork返回后,調(diào)用exec函數(shù)。

fork調(diào)用失敗的原因

fork函數(shù)創(chuàng)建子進(jìn)程也可能會(huì)失敗,有以下兩種情況:

  1. 系統(tǒng)中有太多的進(jìn)程,內(nèi)存空間不足,子進(jìn)程創(chuàng)建失敗。
  2. 實(shí)際用戶的進(jìn)程數(shù)超過(guò)了限制,子進(jìn)程創(chuàng)建失敗。

二、進(jìn)程終止

進(jìn)程退出場(chǎng)景

進(jìn)程退出只有三種情況:

  1. 代碼運(yùn)行完畢,結(jié)果正確。
  2. 代碼運(yùn)行完畢,結(jié)果不正確。
  3. 代碼異常終止(進(jìn)程崩潰)。

進(jìn)程退出碼

我們都知道m(xù)ain函數(shù)是代碼的入口,但實(shí)際上main函數(shù)只是用戶級(jí)別代碼的入口,main函數(shù)也是被其他函數(shù)調(diào)用的,例如在VS2013當(dāng)中main函數(shù)就是被一個(gè)名為_(kāi)_tmainCRTStartup的函數(shù)所調(diào)用,而__tmainCRTStartup函數(shù)又是通過(guò)加載器被操作系統(tǒng)所調(diào)用的,也就是說(shuō)main函數(shù)是間接性被操作系統(tǒng)所調(diào)用的。

既然main函數(shù)是間接性被操作系統(tǒng)所調(diào)用的,那么當(dāng)main函數(shù)調(diào)用結(jié)束后就應(yīng)該給操作系統(tǒng)返回相應(yīng)的退出信息,而這個(gè)所謂的退出信息就是以退出碼的形式作為main函數(shù)的返回值返回,我們一般以0表示代碼成功執(zhí)行完畢,以非0表示代碼執(zhí)行過(guò)程中出現(xiàn)錯(cuò)誤,這就是為什么我們都在main函數(shù)的最后返回0的原因。

當(dāng)我們的代碼運(yùn)行起來(lái)就變成了進(jìn)程,當(dāng)進(jìn)程結(jié)束后main函數(shù)的返回值實(shí)際上就是該進(jìn)程的進(jìn)程退出碼,我們可以使用echo $?命令查看最近一次進(jìn)程退出的退出碼信息。

例如,對(duì)于下面這個(gè)簡(jiǎn)單的代碼:

圖片

代碼運(yùn)行結(jié)束后,我們可以查看該進(jìn)程的進(jìn)程退出碼。

[cl@VM-0-15-centos procTermination]$ echo $?

圖片

這時(shí)便可以確定main函數(shù)是順利執(zhí)行完畢了。

為什么以0表示代碼執(zhí)行成功,以非0表示代碼執(zhí)行錯(cuò)誤?

因?yàn)榇a執(zhí)行成功只有一種情況,成功了就是成功了,而代碼執(zhí)行錯(cuò)誤卻有多種原因,例如內(nèi)存空間不足、非法訪問(wèn)以及棧溢出等等,我們就可以用這些非0的數(shù)字分別表示代碼執(zhí)行錯(cuò)誤的原因。

C語(yǔ)言當(dāng)中的strerror函數(shù)可以通過(guò)錯(cuò)誤碼,獲取該錯(cuò)誤碼在C語(yǔ)言當(dāng)中對(duì)應(yīng)的錯(cuò)誤信息:

圖片

運(yùn)行代碼后我們就可以看到各個(gè)錯(cuò)誤碼所對(duì)應(yīng)的錯(cuò)誤信息:

圖片

實(shí)際上Linux中的ls、pwd等命令都是可執(zhí)行程序,使用這些命令后我們也可以查看其對(duì)應(yīng)的退出碼。

可以看到,這些命令成功執(zhí)行后,其退出碼也是0。

圖片

但是命令執(zhí)行錯(cuò)誤后,其退出碼就是非0的數(shù)字,該數(shù)字具體代表某一錯(cuò)誤信息。

圖片

注意: 退出碼都有對(duì)應(yīng)的字符串含義,幫助用戶確認(rèn)執(zhí)行失敗的原因,而這些退出碼具體代表什么含義是人為規(guī)定的,不同環(huán)境下相同的退出碼的字符串含義可能不同。

進(jìn)程正常退出

return退出

在main函數(shù)中使用return退出進(jìn)程是我們常用的方法。

例如,在main函數(shù)最后使用return退出進(jìn)程。

圖片

運(yùn)行結(jié)果:

圖片

exit函數(shù)

使用exit函數(shù)退出進(jìn)程也是我們常用的方法,exit函數(shù)可以在代碼中的任何地方退出進(jìn)程,并且exit函數(shù)在退出進(jìn)程前會(huì)做一系列工作:

  1. 執(zhí)行用戶通過(guò)atexit或on_exit定義的清理函數(shù)。
  2. 關(guān)閉所有打開(kāi)的流,所有的緩存數(shù)據(jù)均被寫入。
  3. 調(diào)用_exit函數(shù)終止進(jìn)程。

例如,以下代碼中exit終止進(jìn)程前會(huì)將緩沖區(qū)當(dāng)中的數(shù)據(jù)輸出。

圖片

運(yùn)行結(jié)果:

圖片

_exit函數(shù)

使用_exit函數(shù)退出進(jìn)程的方法我們并不經(jīng)常使用,_exit函數(shù)也可以在代碼中的任何地方退出進(jìn)程,但是_exit函數(shù)會(huì)直接終止進(jìn)程,并不會(huì)在退出進(jìn)程前會(huì)做任何收尾工作。

例如,以下代碼中使用_exit終止進(jìn)程,則緩沖區(qū)當(dāng)中的數(shù)據(jù)將不會(huì)被輸出。

圖片

運(yùn)行結(jié)果:

圖片

return、exit和_exit之間的區(qū)別與聯(lián)系

return、exit和_exit之間的區(qū)別

只有在main函數(shù)當(dāng)中的return才能起到退出進(jìn)程的作用,子函數(shù)當(dāng)中return不能退出進(jìn)程,而exit函數(shù)和_exit函數(shù)在代碼中的任何地方使用都可以起到退出進(jìn)程的作用。

使用exit函數(shù)退出進(jìn)程前,exit函數(shù)會(huì)執(zhí)行用戶定義的清理函數(shù)、沖刷緩沖,關(guān)閉流等操作,然后再終止進(jìn)程,而_exit函數(shù)會(huì)直接終止進(jìn)程,不會(huì)做任何收尾工作。

圖片

return、exit和_exit之間的聯(lián)系

執(zhí)行return num等同于執(zhí)行exit(num),因?yàn)檎{(diào)用main函數(shù)運(yùn)行結(jié)束后,會(huì)將main函數(shù)的返回值當(dāng)做exit的參數(shù)來(lái)調(diào)用exit函數(shù)。

圖片

使用exit函數(shù)退出進(jìn)程前,exit函數(shù)會(huì)先執(zhí)行用戶定義的清理函數(shù)、沖刷緩沖,關(guān)閉流等操作,然后再調(diào)用_exit函數(shù)終止進(jìn)程。

進(jìn)程異常退出

情況一:向進(jìn)程發(fā)生信號(hào)導(dǎo)致進(jìn)程異常退出。

例如,在進(jìn)程運(yùn)行過(guò)程中向進(jìn)程發(fā)生kill -9信號(hào)使得進(jìn)程異常退出,或是使用Ctrl+C使得進(jìn)程異常退出等。

情況二:代碼錯(cuò)誤導(dǎo)致進(jìn)程運(yùn)行時(shí)異常退出。

例如,代碼當(dāng)中存在野指針問(wèn)題使得進(jìn)程運(yùn)行時(shí)異常退出,或是出現(xiàn)除0的情況使得進(jìn)程運(yùn)行時(shí)異常退出等。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11123

    瀏覽量

    207900
  • PID
    PID
    +關(guān)注

    關(guān)注

    35

    文章

    1466

    瀏覽量

    84834
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4236

    瀏覽量

    61965
  • 數(shù)據(jù)結(jié)構(gòu)

    關(guān)注

    3

    文章

    568

    瀏覽量

    40030
  • Fork
    +關(guān)注

    關(guān)注

    0

    文章

    14

    瀏覽量

    3257
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Linux中進(jìn)程和線程的深度對(duì)比

    關(guān)于進(jìn)程和線程,在 Linux 中是一對(duì)兒很核心的概念。但是進(jìn)程和線程到底有啥聯(lián)系,又有啥區(qū)別,很多人還都沒(méi)有搞清楚。
    發(fā)表于 10-14 16:47 ?1232次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>中進(jìn)程</b>和線程的<b class='flag-5'>深度</b>對(duì)比

    Linux進(jìn)程是如何創(chuàng)建出來(lái)的?

    Linux 中,進(jìn)程是我們非常熟悉的東東了,哪怕是只寫過(guò)一天代碼的人也都用過(guò)它。但是你確定它不是你最熟悉的陌生人?我們今天通過(guò)深度剖析進(jìn)程
    發(fā)表于 11-15 09:27 ?529次閱讀

    linux內(nèi)核深度剖析,另附有光盤資料

    linux內(nèi)核深度剖析,對(duì)于想學(xué)linux內(nèi)核的人來(lái)說(shuō),絕對(duì)值得一看,另附有光盤資料。
    發(fā)表于 01-15 21:25

    C語(yǔ)言深度剖析

    C語(yǔ)言深度剖析[完整版].pdfC語(yǔ)言深度剖析[完整版].pdf (919.58 KB )
    發(fā)表于 03-19 05:11

    Linux系統(tǒng)中進(jìn)程如何查看及控制

    Linux系統(tǒng)中進(jìn)程的查看及控制
    發(fā)表于 06-09 08:34

    Linux系統(tǒng)中的進(jìn)程控制該怎樣去實(shí)現(xiàn)呢

    Linux系統(tǒng)編程、網(wǎng)絡(luò)編程》第5章 進(jìn)程控制 2008年畢業(yè)于沈陽(yáng)航空航...
    發(fā)表于 12-23 07:55

    Linux源碼分析系列的進(jìn)程

    概述:相關(guān)概念,進(jìn)程在整個(gè)內(nèi)核中的功能位置,源代碼中進(jìn)程相關(guān)的文件源代碼中進(jìn)程相關(guān)的文件。 Linux核心是多任務(wù)的,運(yùn)行的程序稱作進(jìn)程 (
    發(fā)表于 11-03 22:26 ?32次下載

    Linux進(jìn)程控制編程

    7.2 Linux進(jìn)程控制編程 1.fork() 在Linux中創(chuàng)建一個(gè)新進(jìn)程的惟一方法是使用fork()函數(shù)。fork()函數(shù)是Linux
    發(fā)表于 10-18 14:16 ?0次下載

    Linux守護(hù)進(jìn)程詳解

    分享到:標(biāo)簽:進(jìn)程控制 Linux 守護(hù)進(jìn)程進(jìn)程 7.3 Linux守護(hù)進(jìn)程 7.3.1 守
    發(fā)表于 10-18 14:24 ?0次下載
    <b class='flag-5'>Linux</b>守護(hù)<b class='flag-5'>進(jìn)程</b>詳解

    uClinux進(jìn)程調(diào)度器的實(shí)現(xiàn)分析

    uClinux中進(jìn)程調(diào)度器的實(shí)現(xiàn)原理,展示了uClinux中獨(dú)具特色的進(jìn)程調(diào)度機(jī)制。 關(guān)鍵詞:uClinux;調(diào)度策略;進(jìn)程調(diào)度器 0. 引言 uClinux是針對(duì)控制領(lǐng)域的嵌入式
    發(fā)表于 11-06 14:30 ?0次下載

    基于Linux進(jìn)程管理的詳細(xì)剖析

    一篇,我們講到了Linux內(nèi)核開(kāi)發(fā)和應(yīng)用程序開(kāi)發(fā),今天我們來(lái)講講Linux重點(diǎn)部分Linux進(jìn)程管理。
    的頭像 發(fā)表于 01-26 11:24 ?3636次閱讀
    基于<b class='flag-5'>Linux</b><b class='flag-5'>進(jìn)程</b>管理的詳細(xì)<b class='flag-5'>剖析</b>

    Linux和UNIX可以用什么命令查看運(yùn)行中進(jìn)程的相關(guān)信息

      你可以使用ps命令。它能顯示當(dāng)前運(yùn)行中進(jìn)程的相關(guān)信息,包括進(jìn)程的PID。Linux和UNIX都支持ps命令,顯示所有運(yùn)行中進(jìn)程的相關(guān)信息。ps命令能提供一份當(dāng)前
    發(fā)表于 01-20 09:42 ?6417次閱讀

    Linux0.11-進(jìn)程控制塊數(shù)據(jù)結(jié)構(gòu)

    嵌入式Linux中文站收集整理Linux0.11版本內(nèi)核學(xué)習(xí)筆記,本文分析了Linux進(jìn)程控制模塊的數(shù)據(jù)結(jié)構(gòu)。
    發(fā)表于 05-15 15:22 ?911次閱讀

    深度剖析Linux中進(jìn)程控制(下)

    Linux中,fork函數(shù)是非常重要的函數(shù),它從已存在進(jìn)程中創(chuàng)建一個(gè)新進(jìn)程。新進(jìn)程為子進(jìn)程,而原進(jìn)程
    的頭像 發(fā)表于 05-12 10:49 ?419次閱讀
    <b class='flag-5'>深度</b><b class='flag-5'>剖析</b><b class='flag-5'>Linux</b><b class='flag-5'>中進(jìn)程控制</b>(下)

    Linux中進(jìn)程、線程和協(xié)程的基礎(chǔ)概念

    進(jìn)程是計(jì)算機(jī)中運(yùn)行的程序的實(shí)例,它是操作系統(tǒng)中最基本的執(zhí)行單元之一。每個(gè)進(jìn)程都有自己的獨(dú)立內(nèi)存空間、系統(tǒng)資源和代碼執(zhí)行流。這意味著一個(gè)進(jìn)程的崩潰通常不會(huì)影響其他進(jìn)程,
    的頭像 發(fā)表于 12-06 09:22 ?664次閱讀