知名商城網(wǎng)站建設(shè)公司seo搜狗
💓博主CSDN主頁:杭電碼農(nóng)-NEO💓
?
?專欄分類:Linux從入門到精通?
?
🚚代碼倉庫:NEO的學(xué)習日記🚚
?
🌹關(guān)注我🫵帶你學(xué)更多操作系統(tǒng)知識
? 🔝🔝
進程狀態(tài)管理
- 1. 前言
- 2. 查看進程的第二種方式
- 3. 如何創(chuàng)建一個子進程?
- 4. fork函數(shù)詳解(一)
- 5. fork函數(shù)詳解(二)
- 6. fork函數(shù)詳解(三)
- 7. fork函數(shù)詳解(四)
- 8. fork函數(shù)詳解(五)
- 9. 總結(jié)以及拓展
1. 前言
我們已經(jīng)會使用getpid/getppid
函數(shù)來查看pid和ppid了,本篇文章
會介紹第二種查看進程的方式
本章重點:
本篇文章著重介紹創(chuàng)建子進程
的函數(shù):fork的概念以及返回值
本篇文章主要解決以下問題:
fork函數(shù)干了什么事?
為什么fork有兩個返回值?
為啥fork的返回給父子進程的內(nèi)容不同?
fork之后,父子進程誰先運行?
如何理解同一個變量有不同的值?
這些問題的答案會在文章中給出
2. 查看進程的第二種方式
在Linux系統(tǒng)中,有一個動態(tài)文件proc
它里面存放著所有進程的信息,之所以
叫動態(tài)文件是因為它會隨著進程的改變
而隨時更新它的內(nèi)容!
查看所有進程文件:
使用指令:
ls /proc/
查看特點的進程文件:
使用指令:
ls /proc/pid
比如我現(xiàn)在寫一個死循環(huán)代碼
然后通過此文件來查看我這個進程:
查看動態(tài)文件
可以發(fā)現(xiàn),在自行創(chuàng)建的進程中
有很多我們看不懂的文件,這些文件
也不需要掌握,但是有兩個文件需要
大家注意,一個是cwd
一個是exe
exe指向可執(zhí)行程序的位置
cwd代表默認的當前文件
我們經(jīng)常聽見一句話:在當前文件
創(chuàng)建一個文件,在當前文件怎么怎么樣
這個當前文件就是cwd指向的文件
并且Linux外殼的bash中,pwd指令
其實就是從cwd中找到當前路徑的!
3. 如何創(chuàng)建一個子進程?
眾所周知啊,Linux系統(tǒng)是用C語言寫的
所以Linux中創(chuàng)建一個進程實際上也要
調(diào)用C語言的函數(shù),也就是用代碼創(chuàng)建
進程,用戶使用代碼創(chuàng)建進程叫系統(tǒng)調(diào)用
使用函數(shù):
fork
使用man指令查看fork函數(shù)信息:
寫一段代碼創(chuàng)建子進程觀察情況:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{printf("我是一個進程,我的pid:%d\n",getpid());fork();printf("i am a process,pid:%d\n",getpid());sleep(1);return 0;
}
請看下面的圖片觀察情況:
接下來再打一個死循環(huán)觀察情況:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{ printf("我是一個進程,pid:%d ppid:%d\n",getpid(),getppid()); while(1) { fork(); printf("i am a process,pid:%d ppid:%d\n",getpid(),getppid()); sleep(1); } return 0;
}
請看以下圖片觀察情況:
它會循環(huán)打印pid和ppid,可以發(fā)現(xiàn)
藍色框里面的ppid明顯是命令行解釋器
bash的pid,這個進程的pid是31063
創(chuàng)建的子進程的pid是31064,并且子進程
的ppid也就是父親id是31063,這就已經(jīng)
說明了一個情況:fork之后,已經(jīng)創(chuàng)建了子進程
并且此進程的父進程是我們自己寫的程序!
4. fork函數(shù)詳解(一)
通過上面的代碼和圖文,可以發(fā)現(xiàn)
fork之前的代碼只有父進程執(zhí)行
然而fork之后的代碼父子進程都要執(zhí)行
fork函數(shù)不僅會幫我們創(chuàng)建子進程
它還有兩個返回值,父進程會接受到
子進程的pid,子進程會接收到值:0
那么你可能有疑問?既然fork之后
父子進程會執(zhí)行一樣的代碼,那么子進程
的意義是什么?其實fork是這樣用的:
int forkid = fork();
if(forkod==0)
{執(zhí)行子進程的專有代碼
}
else
{執(zhí)行父進程的專有代碼
}
實際上我們創(chuàng)建子進程的意義就是
為了讓子進程執(zhí)行和父進程不一樣
的代碼,實現(xiàn)和父進程不一樣的功能
比如我們可以一邊下載軟件一邊播放
音樂,這兩個過程就是不同的進程在執(zhí)行!
改修代碼后查看fork的返回值:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{ printf("我是一個父進程,我的pid是: %d\n",getpid()); pid_t id = fork(); if(id==0)//子進程的代碼片段 { while(1) { printf("我是子進程: pid:%d ppid: %d ret:%d,我在進行下載任務(wù)\n",getpid(),getppid(),id); sleep(1); } } else if(id>0)//父進程的代碼片段 { while(1) { printf("我是父進程: pid:%d ppid: %d ret:%d,我在進行播放任務(wù)\n",getpid(),getppid(),id); sleep(1); } } return 0;
}
請看下圖觀察情況:
5. fork函數(shù)詳解(二)
觀察上面的情況,fork函數(shù)到底做了什么?
現(xiàn)在我來回答這個問題:
fork會創(chuàng)建子進程,系統(tǒng)中會多出
一個子進程,操作系統(tǒng)以父進程為
模板為子進程創(chuàng)建PCB,但是子進程
中是沒有代碼和數(shù)據(jù)的,當前狀態(tài)
子進程和父進程共享代碼和數(shù)據(jù)
所以fork之后,父子進程會執(zhí)行一樣的代碼
父子進程的關(guān)系可以用下圖來理解:
理解了這一點后,第三點也很好理解
首先,一個父進程可以創(chuàng)建很多個子
進程,然而一個子進程只對應(yīng)一個父
進程,所以fork函數(shù)會返回子進程的id
給父進程,方便父進程管理它的子進程
現(xiàn)在就已經(jīng)解決了開頭的第1.3問題了
6. fork函數(shù)詳解(三)
現(xiàn)在我想來解答第二個問題,眾所周知啊
C/C++函數(shù)只能有一個返回值,然而這里
的fork函數(shù)既然也是C函數(shù),為什么會有兩個
返回值呢?請看以下的解釋:
首先,fork之后,父子進程都會執(zhí)行
代碼的本質(zhì)是它們都被內(nèi)存調(diào)度了
而當一個函數(shù)執(zhí)行到return時,它的
核心工作才算執(zhí)行完成,于是我們可以
想象一下fork函數(shù)內(nèi)部的一些代碼信息:
可以發(fā)現(xiàn),在fork函數(shù)return之前,
就已經(jīng)創(chuàng)建了子進程,并且將子進程
放入調(diào)度隊列中運行了,所以當子進程
在調(diào)度隊列時,它和父進程就已經(jīng)分流了
而不是真正在fork函數(shù)return之后才分流的
并且創(chuàng)建完子進程后代碼是共享的
很明顯return也是一句代碼
,所以父子進程
都會執(zhí)行return語句
,fork函數(shù)有兩個返回值
7. fork函數(shù)詳解(四)
現(xiàn)在,我來回答第四個問題
fork之后,父子進程誰先運行?
在講解第二問時我們知道,創(chuàng)建完成
子進程后,這只是一個開始,系統(tǒng)的其他
進程,父進程,子進程接下來會被調(diào)度執(zhí)行!
問題是先調(diào)度誰?先創(chuàng)建就先調(diào)度嗎?
答案明顯不是!在調(diào)度隊列中,CPU
會選擇一個進程去運行它,誰先被調(diào)度
誰就先運行!所以fork之后父子進程誰
先運行用戶是不確定的,這是由各自
進程PCB中的調(diào)度信息決定的,比如
優(yōu)先級,算法信息等等
下面有一篇拓展閱讀,有興趣可以看看:
fork函數(shù)拓展閱讀
8. fork函數(shù)詳解(五)
最后來回答第五個問題
前面一個函數(shù)有兩個返回值你可能
能夠理解,因為兩個進程都被調(diào)度了
但是同一個變量怎么可能有兩個不同
的值呢?變量id在父進程和子進程中值不同
首先我們要清楚一點:
干掉父進程不會影響子進程運行,反之也是
請看下面的視頻驗證:
kill父進程不會影響子進程
通過以上實驗看出,進程是有獨立性的
首先是表現(xiàn)在進程各自的PCB運行時
不會相互影響,很明顯,代碼本身只是可讀
的,所以不會影響代碼,但是對于數(shù)據(jù)來說
父子的數(shù)據(jù)是可能不同的(可能會被修改)
所以系統(tǒng)是怎樣做到讓數(shù)據(jù)在各個
進程都自己私有一份的?答案是寫時拷貝
類似于學(xué)習類和對象時的深淺拷貝
數(shù)據(jù)會在需要使用時被寫時拷貝到PCB
然而fork返回值賦值給變量時,本質(zhì)也是
寫入,返回時也會發(fā)生寫時拷貝,所以不同
的進程執(zhí)行的代碼中的變量id獲取的值不同!
9. 總結(jié)以及拓展
fork函數(shù)的細節(jié)還有很多,但是以目前
的學(xué)習進度來說,要完全理解它很困難
所以文章采用了較為簡單的方式幫助理解
只要理解了fork函數(shù)的五個問題的答案
那么在目前階段就已經(jīng)干的很好了!
拓展閱讀:
Linux下的PCB源碼解析