上一篇嵌入式未來一段時(shí)間還是Linux天下,一位嵌入式er初探Linux kernel經(jīng)驗(yàn),我們講到了Linux內(nèi)核開發(fā)和應(yīng)用程序開發(fā),今天我們來講講Linux重點(diǎn)部分Linux的進(jìn)程管理。
OS是干啥的?處理提供對(duì)硬件層的抽象以外,還擔(dān)負(fù)著很多的硬件管理功能,而這些功能,用一句話來說,就是來處理各個(gè)部件的時(shí)空復(fù)用問題(時(shí)間和空間的重用問題,如cpu是分時(shí)重用的(當(dāng)然還有多核cpu的特例,而內(nèi)存是即分時(shí)又分空的……)。
往古至今,大牛們對(duì)OS的定義不下其數(shù),而本人認(rèn)為最有說服力的OS的定義就是:“所有軟件(特指應(yīng)用程序)的交集”,料想計(jì)算機(jī)誕生初期都是專用機(jī),在一款機(jī)器上只能跑訂制的專用程序,而這個(gè)程序要自己做好所有硬件的協(xié)調(diào)且還要完成他應(yīng)有的本分工作,慢慢的計(jì)算機(jī)向通用型發(fā)展,為了提高系統(tǒng)利用率和避免盲目且重復(fù)的底層實(shí)現(xiàn),OS隨著需求一步步形成且不斷完善……
不知你發(fā)現(xiàn)沒有,好多東西都是在歷史舞臺(tái)上重復(fù)出現(xiàn)的,仿佛對(duì)應(yīng)著“20年后又是一條好漢”這句話,看現(xiàn)在的google的Chrome OS,其實(shí)最初的原型說白了就是把chrome瀏覽器必要的底層重新從OS中剝離,使其具有獨(dú)立運(yùn)行的能力,這不是有點(diǎn)最早的專用機(jī)的味道,計(jì)算機(jī)這東西很是神奇,一個(gè)好的點(diǎn)子就可能改變整個(gè)市場,甚至一個(gè)世紀(jì)……
扯遠(yuǎn)了,重新回來,回到復(fù)用性說起吧,因?yàn)閏pu要跑的程序很多,但是cup個(gè)數(shù)有限,這就牽扯到cpu的重用,也就是被多個(gè)進(jìn)程重用(進(jìn)程與程序的區(qū)別就不多說了,自己熟悉OS知識(shí)去吧,簡單的提一下程序運(yùn)行的本質(zhì)就是從內(nèi)核申請(qǐng)個(gè)進(jìn)程,再把程序包中的代碼拷貝到對(duì)應(yīng)進(jìn)程的代碼域并設(shè)置好相關(guān)變量數(shù)據(jù)令進(jìn)程跑起來,所以程序只是靜態(tài)的代碼,而進(jìn)程是一個(gè)不斷從程序加載代碼的執(zhí)行過程),這是由于進(jìn)程間要復(fù)用cpu,所以就要求有人來負(fù)責(zé)他們有組織有紀(jì)律的復(fù)用,并協(xié)調(diào)進(jìn)程間的輕重緩急、切換規(guī)則、切換后的一些處理……
另外還有怎樣生產(chǎn)進(jìn)程、怎樣切換、怎樣銷毀……這些由誰完成?當(dāng)然是OS,對(duì)于linux,當(dāng)然是kernel了。畢竟OS就是用來跑程序的,而進(jìn)程就是程序的靈魂,可見進(jìn)程管理的重要性,咱就從進(jìn)程說起先。
雖然吧,進(jìn)程是處于執(zhí)行期的程序,但是要明白,進(jìn)程并不僅僅局限于一段可執(zhí)行的代碼,你想,要想跑進(jìn)程,你得知道是誰的進(jìn)程,要區(qū)別于其他進(jìn)程;還要保證當(dāng)前進(jìn)程不能隨意訪問其他進(jìn)程的地址空間,要是連這都不限制,那寫個(gè)黑客程序多隨意啊;還牽扯到多線程問題;另外,由于進(jìn)程間是復(fù)用cpu的,就是一會(huì)兒這個(gè)執(zhí)行,一會(huì)兒換另一個(gè),那你還要保證它可以接著上次的執(zhí)行啊,要不不就亂了套了……如此說來,進(jìn)程需要的東西大概有:
打開的文件
掛起的信號(hào) (linux一個(gè)事件的處發(fā)是基于信號(hào)機(jī)制的,就像windows的事件機(jī)制)
內(nèi)核內(nèi)部數(shù)據(jù) (這就是傳說中恢復(fù)現(xiàn)場用的,要還原到進(jìn)程切換前的狀態(tài)需要保存現(xiàn)場)
處理器狀態(tài) (沒理解錯(cuò)的話,這也是現(xiàn)場保持的一部分,因?yàn)橛行┏绦虻膱?zhí)行是)
地址空間
一個(gè)或多個(gè)執(zhí)行線程(Linux下的線程實(shí)現(xiàn)非常有趣,也非常簡單,本質(zhì)上也就是幾個(gè)共享進(jìn)程,沒有設(shè)置專門的線程數(shù)據(jù)結(jié)構(gòu))
以上是Linux下進(jìn)程的主要組成部分,當(dāng)然了,進(jìn)程管理么,有了進(jìn)程還要有管理,管理相關(guān)著進(jìn)程的策略和生命周期等一些東西,我們會(huì)慢慢講來。
話說很久很久以前,進(jìn)程自創(chuàng)建時(shí)刻起就開始存活了,活在Linux世界的進(jìn)程爹fork()系統(tǒng)調(diào)用一下,就會(huì)生個(gè)小進(jìn)程,比和女兒國的水來的還快。進(jìn)程這東西沒耳朵沒眼睛的,他爹咋知道啥時(shí)候生好了。既然fork()是生婆,那這是生婆最懂了。fork()系統(tǒng)調(diào)用會(huì)返回兩次:一次回到父進(jìn)程,一次回到子進(jìn)程。
新的進(jìn)程是為了立即執(zhí)行新的不同的程序,而接著調(diào)用exec*()這族函數(shù)就可以創(chuàng)建新的地址空間,并把新的程序載入。(fork()實(shí)際上是由clone()系統(tǒng)調(diào)用實(shí)現(xiàn)的。)
最終,程序會(huì)通過exit()系統(tǒng)調(diào)用退出執(zhí)行。這個(gè)函數(shù)會(huì)終結(jié)進(jìn)程并將其占用的資源釋放。父進(jìn)程會(huì)通過wait4()系統(tǒng)調(diào)用來查詢子進(jìn)程是否終結(jié),這就使得進(jìn)程擁有了等待特定進(jìn)程執(zhí)行的能力。進(jìn)程退出后被設(shè)置為僵死狀態(tài),直到父進(jìn)程調(diào)用wait()或waitpid()為止。
知道了進(jìn)程不僅僅是由一段執(zhí)行代碼組成的,咱們就說說linux下的進(jìn)程的大概過程。其實(shí)一個(gè)進(jìn)程就相當(dāng)于一個(gè)軟件的動(dòng)態(tài)執(zhí)行(嚴(yán)格的說是某個(gè)軟件子系統(tǒng)的動(dòng)態(tài)執(zhí)行,當(dāng)然我們可以把該子系統(tǒng)想象成一個(gè)子軟件,這樣會(huì)便于理解)。Linux中創(chuàng)建一個(gè)進(jìn)程要用到fork()系統(tǒng)調(diào)用,一個(gè)子進(jìn)程的生成是通過拷貝父進(jìn)程來實(shí)現(xiàn)的。fork以后,會(huì)返回兩次:一次回到父進(jìn)程,一次回到子進(jìn)程。為何要返回兩次,兩次又是如何區(qū)別的呢??剛開始我也在像這個(gè)問題,因?yàn)樽舆M(jìn)程拷貝了父進(jìn)程的代碼,返回時(shí)處于fork()返回點(diǎn)的上下文是一樣的,但是返回的值不一樣,借此來區(qū)分是父進(jìn)程還是子進(jìn)程……
這不,新的子進(jìn)程創(chuàng)建好了,然后干什么?肯定是做不是當(dāng)前進(jìn)程的工作,要不創(chuàng)建他干什么?所以,這時(shí)候就接著調(diào)用exec*()這一族函數(shù),該族函數(shù)可以創(chuàng)建新的地址空間,并加載到當(dāng)前進(jìn)程執(zhí)行。
最后,程序通過exit() syscall(系統(tǒng)調(diào)用,以后都用這個(gè)代指了) 退出執(zhí)行。這個(gè)函數(shù)會(huì)終結(jié)進(jìn)程并將其占用的資源釋放掉。父進(jìn)程通過wait4() syscall來查詢子進(jìn)程是否終結(jié),這使得進(jìn)程擁有了等待特定進(jìn)程執(zhí)行完的能力(這不就是傳說中的同步么?有木有?有木有?哈哈)。進(jìn)程退出后被設(shè)置為僵死狀態(tài),知道父進(jìn)程調(diào)用wait()或waitpid()為止(其實(shí)他們貌似都是基于wait4()實(shí)現(xiàn)的)。
上面就是進(jìn)程創(chuàng)建到回收的簡單過程。子進(jìn)程由父進(jìn)程創(chuàng)建,父進(jìn)程回收,有點(diǎn)恢復(fù)現(xiàn)場的感覺,不過在比較安全的系統(tǒng)里面,哪里都可以看的“恢復(fù)現(xiàn)場”等類似概念的身影,就像借錢一樣,“好借好還再借不難”,做程序也是這個(gè)道理,哈哈,慢慢體會(huì),編程里面蘊(yùn)含很多的哲學(xué)道理的。
1、進(jìn)程描述符及任務(wù)結(jié)構(gòu)
在軟件設(shè)計(jì)中,第一就是抽象名詞,一切名詞都會(huì)在計(jì)算機(jī)中找到它的數(shù)據(jù)抽象,就是偉大的數(shù)據(jù)結(jié)構(gòu)童鞋。他可能被抽象成一個(gè)變量,一個(gè)數(shù)組,一個(gè)struct,一個(gè)對(duì)象……可能是任何一種類型,只要滿足你的需求,他就是最perfect的抽象。
進(jìn)程在kernel中是被放在任務(wù)隊(duì)列(task list)中的,task list 是個(gè)雙向循環(huán)鏈表。鏈表中的每一項(xiàng)都是task_struct類型,即進(jìn)程描述符。定義在中,包含著一個(gè)具體進(jìn)程的所有信息。
1.1 分配進(jìn)程描述符
進(jìn)程有的表達(dá)了,但是不能胡亂表達(dá)啊,就像追女朋友一樣,不能見人就表白,那不成耍流氓了,名額有限,見好就收啊。OS能多道并跑的進(jìn)程也就那幾個(gè),若肆意創(chuàng)建進(jìn)程,不跑癱了機(jī)子,那就變成病毒程序了,狂吃cpu。Linux使用slab分配器分配task_struct 結(jié)構(gòu)。由slab動(dòng)態(tài)生成task_struct,只需在棧底創(chuàng)建一個(gè)新的thread_info ,再用這個(gè)結(jié)構(gòu)的數(shù)據(jù)可以容易的計(jì)算出偏移量。
其中包含了task_struct 的指針和進(jìn)程的相關(guān)信息。
1.2 進(jìn)程描述符的存放
Kernel通過PID唯一標(biāo)識(shí)一個(gè)進(jìn)程,PID是pid_t類型,其實(shí)也是int型的,pid最大值是32768 (short int 的Max 值)??梢愿钠渲?,在/proc/sys/kernel/pid_max 中,因?yàn)榇蠊镜膚eb服務(wù)器集群工作時(shí)32768個(gè)進(jìn)程多開貌似不夠啊。
內(nèi)核在處理進(jìn)程時(shí)一般是直接通過task_struct進(jìn)程的,都是通過current宏直接找到或計(jì)算當(dāng)前task_struct的。有的平臺(tái)寄存器豐富,不用專門計(jì)算其值,一直把當(dāng)前運(yùn)行進(jìn)程的值保存在專用的寄存器中就OK。如powerPC用r2寄存器,而x86寄存器少要專門計(jì)算。
1.3 進(jìn)程狀態(tài)
進(jìn)程一直處于下面五種狀態(tài)之一:
TASK_RUNNING//運(yùn)行狀態(tài)
TASK_INTERRUPTIBLE//可中斷狀態(tài)
TASK_UNINTRRUPTIBLE//不可中斷狀態(tài)
TASK_ZOMBIE//僵死
TASK_STOPPED//停止
下圖是大概的轉(zhuǎn)換過程。不很詳細(xì),大家可以search一下……
-
cpu
+關(guān)注
關(guān)注
68文章
10854瀏覽量
211574 -
Linux
+關(guān)注
關(guān)注
87文章
11292瀏覽量
209323 -
進(jìn)程
+關(guān)注
關(guān)注
0文章
203瀏覽量
13960 -
Kernel
+關(guān)注
關(guān)注
0文章
48瀏覽量
11159
原文標(biāo)題:揭開OS的面紗,一位嵌入式er 初探Linux kernel之重點(diǎn)Linux的進(jìn)程管理
文章出處:【微信號(hào):gh_c472c2199c88,微信公眾號(hào):嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論