日韩天堂,国产精品久久久久久久久久一区,羞羞羞网站,自拍视频网站,久久亚洲欧美成人精品,桃花阁成人网在线观看

Hello! 歡迎來到小浪云!


父子進(jìn)程的故事:解讀Linux中的fork機(jī)制


avatar
小浪云 2025-04-17 47

前言

linux系統(tǒng)中,進(jìn)程是操作系統(tǒng)最重要的執(zhí)行單元,而父子進(jìn)程的創(chuàng)建與管理更是系統(tǒng)資源分配和任務(wù)并行的關(guān)鍵。通過fork函數(shù),linux能夠快速高效地復(fù)制一個進(jìn)程,使得父子進(jìn)程協(xié)同工作成為可能。理解父子進(jìn)程的運(yùn)行機(jī)制不僅有助于掌握系統(tǒng)編程的核心技能,更能為優(yōu)化資源利用與提高程序性能提供理論基礎(chǔ)。本文將帶你從基礎(chǔ)原理出發(fā),解析linux父子進(jìn)程的運(yùn)行特性、fork的核心機(jī)制及其在實際開發(fā)中的應(yīng)用。


一、進(jìn)程PID

PID 是用來唯一標(biāo)識一個進(jìn)程的屬性,我們可以使用 ps 指令查看一個進(jìn)程的部分屬性。進(jìn)程的屬性信息是由操作系統(tǒng)來維護(hù)的,這些信息被存儲在一個 task_struct 結(jié)構(gòu)體中,屬于操作系統(tǒng)內(nèi)核中的數(shù)據(jù)。由于操作系統(tǒng)本身是不相信用戶的,所以用戶無法直接去訪問 task_struct 對象中的成員,因此 ps 指令能夠顯示進(jìn)程的屬性信息,本質(zhì)上是通過系統(tǒng)調(diào)用接口去實現(xiàn)的。

1.1 通過系統(tǒng)調(diào)用接口查看進(jìn)程PID

獲取進(jìn)程的 PID 需要用到系統(tǒng)調(diào)用接口 getpid() ,該函數(shù)會返回調(diào)用該函數(shù)的進(jìn)程的 PID,返回值類型為 pid_t 。如下圖我們使用 man getpid 指令去查看 getpid 的基礎(chǔ)文檔:

父子進(jìn)程的故事:解讀Linux中的fork機(jī)制在這里插入圖片描述

注意上圖中還有一個 getppid 是什么呢?不難猜到,這應(yīng)該是用來獲取父進(jìn)程 PID 的系統(tǒng)調(diào)用接口,接下來我們寫段代碼來具象化 PID 吧。 注意上圖中還有一個 getppid 是什么呢?不難猜到,這應(yīng)該是用來獲取父進(jìn)程 PID 的系統(tǒng)調(diào)用接口,接下來我們寫段代碼來具象化 PID 吧。

代碼語言:JavaScript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制

#include <stdio.h>    #include <unistd.h>    #include <sys>    int main()    {        while(1)        {            printf("I am a process, my id is: %d, parent id is: %dn", getpid(), getppid());                                          sleep(1);        }        return 0;    }</sys></unistd.h></stdio.h>

我們可以寫一個腳本來實時獲取上面這段代碼執(zhí)行起來后的進(jìn)程信息。

父子進(jìn)程的故事:解讀Linux中的fork機(jī)制在這里插入圖片描述
父子進(jìn)程的故事:解讀Linux中的fork機(jī)制在這里插入圖片描述

可以看到,我一個將這段代碼執(zhí)行了兩次,每一次的子進(jìn)程 PID 都在發(fā)生變化,但是父進(jìn)程的 PID 從未更改。

為了保證數(shù)據(jù)的準(zhǔn)確性,我們再使用 ps 指令對比以下獲取到的進(jìn)程 PID 是否真的一樣。

代碼語言:javascript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制

while :; do ps axj | head -1 ; ps axj |grep process | grep -v grep ;  sleep 1 ; done
父子進(jìn)程的故事:解讀Linux中的fork機(jī)制在這里插入圖片描述
父子進(jìn)程的故事:解讀Linux中的fork機(jī)制在這里插入圖片描述

結(jié)論:我們用 getpid 和 getppid 得到的父子進(jìn)程的 PID 和 ps 指令獲取到的進(jìn)程 PID 是一樣的

二、通過系統(tǒng)調(diào)用創(chuàng)建進(jìn)程-fork初識

之前我們自己創(chuàng)建進(jìn)程都是通過寫一份源代碼,然后去編譯運(yùn)行,最終得到一個進(jìn)程,今天給大家介紹另一種通過系統(tǒng)調(diào)用接口 fork 去創(chuàng)建進(jìn)程的方式。一樣的,我們使用 man fork 去查看一下 fork 的相關(guān)文檔:

父子進(jìn)程的故事:解讀Linux中的fork機(jī)制在這里插入圖片描述

大致意思就是:fork 函數(shù)會以調(diào)用該函數(shù)的進(jìn)程作為父進(jìn)程去創(chuàng)建一個子進(jìn)程.

父子進(jìn)程的故事:解讀Linux中的fork機(jī)制在這里插入圖片描述

創(chuàng)建成功時,會在父進(jìn)程中返回子進(jìn)程的 PID ,在子進(jìn)程中返回 0 。否則就在父進(jìn)程中返回 -1 ,子進(jìn)程創(chuàng)建失敗。

2.1 調(diào)用fork函數(shù)后的現(xiàn)象代碼語言:javascript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制

#include <stdio.h>      #include <unistd.h>      #include <sys> int main()                                                           {        printf("before:only one linen");        fork();        printf("after:only one linen");            return 0;    }</sys></unistd.h></stdio.h>
父子進(jìn)程的故事:解讀Linux中的fork機(jī)制在這里插入圖片描述

如上圖所示,fork 后面的代碼執(zhí)行了兩次!這是什么原因呢?我們再寫一段代碼跑跑。

代碼語言:javascript代碼運(yùn)行次數(shù):0運(yùn)行復(fù)制

#include <stdio.h>      #include <unistd.h>      #include <sys> int main()    {        printf("begin:我是一個進(jìn)程,pid:%d, ppid:%dn",getpid(), getppid());            pid_t id = fork();        if(id &gt; 0)        {            while(1)            {                printf("我是父進(jìn)程,pid:%d,ppid:%dn",getpid(),getppid());                sleep(1);            }        }        else if(id == 0)        {            while(1)            {                printf("我是子進(jìn)程,pid:%d,ppid:%dn",getpid(),getppid());                sleep(1);            }        }        else        {            perror("子進(jìn)程創(chuàng)建失敗!n");        }         return 0;    }</sys></unistd.h></stdio.h>
父子進(jìn)程的故事:解讀Linux中的fork機(jī)制在這里插入圖片描述

通過結(jié)果我們可以得出,在上面的一份代碼中 id 大于0和 id 等于0同時存在, if 和 else if 同時滿足,并且有兩個死循環(huán)在同時跑。這個現(xiàn)象說明此時一定存在兩個進(jìn)程,即原來的 myprocess 進(jìn)程和在 myprocess 進(jìn)程中創(chuàng)建的子進(jìn)程,因為在一個進(jìn)程中 if 和 else if 是不可能同時滿足的。這也符合 fork 函數(shù)創(chuàng)建子進(jìn)程的目的,fork 函數(shù)創(chuàng)建子進(jìn)程后,會從原來的一個執(zhí)行流變成兩個執(zhí)行流。

2.2 為什么fork要給子進(jìn)程返回0,給父進(jìn)程返回子進(jìn)程 pid?1. fork 返回值的設(shè)計目的

fork 是 unix 系統(tǒng)中用于創(chuàng)建新進(jìn)程的核心系統(tǒng)調(diào)用。調(diào)用一次 fork,系統(tǒng)會“分裂”出兩個進(jìn)程:父進(jìn)程和子進(jìn)程。它的返回值有以下特點:

在父進(jìn)程中:fork 返回新創(chuàng)建的子進(jìn)程的 PID,使得父進(jìn)程可以通過該 PID 來管理和操作子進(jìn)程(如使用 wait 或 kill 等操作)。在子進(jìn)程中:fork 返回 0,標(biāo)識自己是子進(jìn)程,無需再通過 PID 區(qū)分。

這種設(shè)計的核心目的正如您提到的,用于區(qū)分不同執(zhí)行流,即便父子共享同一套代碼,也可以根據(jù)返回值選擇性地執(zhí)行不同代碼。

2. 現(xiàn)實類比的深入解讀父親喊“兒子”:如果不區(qū)分,所有子進(jìn)程都會響應(yīng),導(dǎo)致混亂。通過分配唯一的 PID,每個子進(jìn)程可以被單獨識別。子進(jìn)程喊“爸爸”:由于每個子進(jìn)程只能有一個父進(jìn)程,所以子進(jìn)程通過調(diào)用 getppid() 即可找到其唯一的父進(jìn)程。3. 為什么子進(jìn)程返回值為 0簡單區(qū)分:子進(jìn)程無需知道自己的 PID 來執(zhí)行自己的任務(wù),而只需通過返回值 0 知道自己是子進(jìn)程。效率和邏輯一致性:如果子進(jìn)程也返回自己的 PID,會引入額外的復(fù)雜性,而且父進(jìn)程需要一個單獨機(jī)制區(qū)分這些值。2.3 一個函數(shù)是如何做到返回兩次的?如何理解?

在調(diào)用 fork 函數(shù)之前就只有一個進(jìn)程,我們先來回顧一下什么是進(jìn)程?進(jìn)程 = 內(nèi)核數(shù)據(jù)結(jié)構(gòu) + 代碼和數(shù)據(jù),其中的內(nèi)核數(shù)據(jù)結(jié)構(gòu)就是進(jìn)程對應(yīng)的 PCB 對象

父子進(jìn)程的故事:解讀Linux中的fork機(jī)制在這里插入圖片描述

進(jìn)程的 PCB 對象會找到相應(yīng)的代碼和數(shù)據(jù),然后 CPU 就要去調(diào)度這個進(jìn)程,也就是找到該進(jìn)程的代碼和數(shù)據(jù)去執(zhí)行。調(diào)用 fork 函數(shù)創(chuàng)建子進(jìn)程,本質(zhì)上是操作系統(tǒng)多了一個進(jìn)程,因此 fork 函數(shù)創(chuàng)建出來的子進(jìn)程,它要先創(chuàng)建自己的 PCB 對象,子進(jìn)程的 PCB 對象大部分都是以父進(jìn)程的 PCB 對象為模板創(chuàng)建的,即從父進(jìn)程的 PCB 對象中拷貝過來,再對部分屬性稍作修改,子進(jìn)程的 PCB 對象就有了。但是它沒有自己的代碼和數(shù)據(jù),所以只能用父進(jìn)程的,所以 fork 函數(shù)之后,父子進(jìn)程的代碼共享,這就解釋了為什么上面 fork 函數(shù)之后的代碼輸出了兩次,其實就是父子進(jìn)程各自執(zhí)行了一次。

創(chuàng)建子進(jìn)程的目的就是為了幫助父進(jìn)程做不同的事情,但是父子進(jìn)程共享一份代碼,所以我們應(yīng)該在代碼中對它們加以區(qū)分。fork 函數(shù)就幫我們完成了這個需求,它會在父子進(jìn)程中返回不同的值,用戶只需要根據(jù)返回值的不同讓父子進(jìn)程執(zhí)行不同的代碼。 fork 函數(shù)的實現(xiàn)過程:

創(chuàng)建子進(jìn)程創(chuàng)建子進(jìn)程的PCB填充PCB對應(yīng)的內(nèi)容讓子進(jìn)程和父進(jìn)程指向同樣的代碼此時父子進(jìn)程都有獨立的task_struct對象,可以被CPU調(diào)度運(yùn)行了return ret;

由于父子進(jìn)程會共享一份代碼,所以在 fork 函數(shù)執(zhí)行 return 語句之前,子進(jìn)程的 PCB 對象就已經(jīng)被創(chuàng)建出來了,CPU 已經(jīng)可以去同時調(diào)度父子進(jìn)程。由于 fork 函數(shù)中的 return 語句也是被共享的,所以 fork 函數(shù)有兩個返回值。

2.4 一個變量怎么會有不同的內(nèi)容?1. fork 的返回值如何寫入不同的變量空間

當(dāng)調(diào)用 fork 時,父進(jìn)程與子進(jìn)程會各自接收一個返回值,并且寫入同名變量 id。但這并不意味著他們共享同一塊內(nèi)存,而是因為:

獨立的進(jìn)程地址空間 每個進(jìn)程都有自己獨立的虛擬地址空間。在 fork 之后,父進(jìn)程與子進(jìn)程的地址空間是彼此獨立的。盡管子進(jìn)程初始時看起來與父進(jìn)程完全相同,但實際上它們的數(shù)據(jù)是分離的。寫時拷貝(COW)機(jī)制 操作系統(tǒng)為提高效率并節(jié)省資源,采用了寫時拷貝技術(shù)。在 fork 之后: 父子進(jìn)程共享同一份內(nèi)存數(shù)據(jù),直到有一方嘗試修改這些數(shù)據(jù)。當(dāng)某個進(jìn)程試圖修改數(shù)據(jù)時,操作系統(tǒng)會為該進(jìn)程分配新的物理內(nèi)存空間,并將被修改的數(shù)據(jù)復(fù)制到新分配的空間中。2. fork 中變量 id 的本質(zhì)

在代碼中,變量 id 是存儲 fork 返回值的地方。以下幾點解釋了為什么同名變量可以存儲不同的值:

父子獨立運(yùn)行 fork 返回后,父子進(jìn)程的執(zhí)行路徑分開。父進(jìn)程的 id 變量存儲的是子進(jìn)程的 PID,而子進(jìn)程的 id 變量存儲的是 0。不同的內(nèi)存空間 由于父子進(jìn)程的地址空間獨立,id 實際上存在于兩塊不同的內(nèi)存區(qū)域,即父進(jìn)程的 id 和子進(jìn)程的 id 是完全獨立的變量。賦值過程 fork 的返回值通過操作系統(tǒng)寫入到父子進(jìn)程各自的 id 變量中: 父進(jìn)程在 return 時向 id 寫入子進(jìn)程的 PID。子進(jìn)程在 return 時向 id 寫入 0。


結(jié)語

Linux父子進(jìn)程的運(yùn)行機(jī)制展示了操作系統(tǒng)設(shè)計的高效性與靈活性。從fork的返回值設(shè)計到寫時拷貝(COW)的優(yōu)化方案,這一切都體現(xiàn)了Linux在性能與資源利用上的巧妙平衡。通過深入理解父子進(jìn)程的特性,不僅能夠提升系統(tǒng)編程的能力,還能為并發(fā)和并行程序設(shè)計提供堅實的理論支持。希望本文能為你的學(xué)習(xí)和實踐帶來啟發(fā),在Linux系統(tǒng)的探索中邁向更高的層次。

父子進(jìn)程的故事:解讀Linux中的fork機(jī)制在這里插入圖片描述

今天的分享到這里就結(jié)束啦!如果覺得文章還不錯的話,可以三連支持一下,17的主頁還有很多有趣的文章,歡迎小伙伴們前去點評,您的支持就是17前進(jìn)的動力!

相關(guān)閱讀

主站蜘蛛池模板: 草比电影| 伊人干综合| 国产日韩视频 | 欧美综合视频在线 | 性色网站 | 性生活视频网站 | 日本a级精品一区二区三区 欧洲精品在线观看 | 这里只有精品久久 | 伊人婷婷色香五月综合缴激情 | 日本免费一区二区视频 | 伊人国产在线 | 亚洲成人一级电影 | 视频福利在线 | 在线观看日韩欧美 | 亚洲午夜国产片在线观看 | 久久久精品久久久久久 | 麻豆专媒体公司网站 | 九九精品影院 | 五月激情综合 | 国内精品麻豆 | 男女羞羞视频网站18 | 亚洲精品第一国产综合野 | 国产剧情自拍 | 精品日本久久久久久久久久 | 欧美在线视频免费播放 | 亚洲国产精品yw在线观看 | 亚洲视频在线免费播放 | 色婷婷电影网 | 亚洲狠狠成人综合网 | 亚洲精品9999久久久久 | 在线99视频 | 国产精品久久久久久影院 | 亚洲午夜国产精品 | 久久精品视频免费看 | 久久精品国产99久久久 | 亚洲国产成人久久综合一区77 | 日本国产一区二区三区 | 欧美成人免费观看久久 | 亚洲一区二区福利视频 | 亚洲免费在线观看视频 | 一区二区三区伦理高清 |