欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報激活碼778899分享:【Linux】進程

柚子快報激活碼778899分享:【Linux】進程

http://yzkb.51969.com/

文章目錄

1.進程2.task_stuct的屬性2.1 標示符2.1.1 PID2.1.2 PPID2.1.3 創(chuàng)建子進程

2.2 狀態(tài)2.2.1 進程的狀態(tài)2.2.2 Linux下進程的狀態(tài)

2.3 優(yōu)先級2.4 進程切換(上下文數(shù)據(jù)、程序計數(shù)器 )2.5 Linux的進程調(diào)度算法

1.進程

在沒有正式學(xué)習(xí)進程之前,通過理解操作系統(tǒng)的管理,我們就可以知道操作系統(tǒng)是怎么進行進程管理的了。

很簡單,先把進程描述起來,再把進程組織起來!

那什么叫進程呢?

課本概念:程序的一個執(zhí)行實例,正在執(zhí)行的程序等 內(nèi)核觀點:擔當分配系統(tǒng)資源(CPU時間,內(nèi)存)的實體。

但是看完上面兩句話我還是不懂進程是什么意思。

操作系統(tǒng)為了管理進程,必須得描述進程,描述進程就要有結(jié)構(gòu)體(課本上稱之為進程控制塊,PCB process control block,Linux操作系統(tǒng)下的PCB是: task_struct ),有結(jié)構(gòu)體就可以連接起來,連接起來就可以將管理進程轉(zhuǎn)化為數(shù)據(jù)結(jié)構(gòu)的增刪查改操作。 我們先對進程簡單建一下模:

所以, 進程 = 內(nèi)核的數(shù)據(jù)結(jié)構(gòu)(task_struct)+程序的代碼和數(shù)據(jù) ,調(diào)度運行進程,本質(zhì)就是讓進程控制塊task_struct進行排隊!

那么task_struct中存放的都是什么呢?

2.task_stuct的屬性

task_struct是Linux內(nèi)核的一種數(shù)據(jù)結(jié)構(gòu),它會被操作系統(tǒng)在RAM(內(nèi)存)里創(chuàng)建,并且里面包含著進程的信息。

task_ struct內(nèi)容分類:

標示符:描述本進程的唯一標示符,用來區(qū)別其他進程。狀態(tài):任務(wù)狀態(tài),退出代碼,退出信號等。優(yōu)先級: 相對于其他進程的優(yōu)先級。程序計數(shù)器:程序中即將被執(zhí)行的下一條指令的地址(pc指針)。上下文數(shù)據(jù):進程執(zhí)行時處理器的寄存器中的數(shù)據(jù)。內(nèi)存指針: 包括程序代碼和進程相關(guān)數(shù)據(jù)的指針,還有和其他進程共享的內(nèi)存塊的指針I(yè)/O狀態(tài)信息:包括顯示的I/O請求,分配給進程的I/O設(shè)備和被進程使用的文件列表。記賬信息: 可能包括處理器時間總和,使用的時鐘數(shù)總和,時間限制,記賬號等。其他信息

為了查看相關(guān)屬性,我們先寫一個簡單的程序 當程序編譯好以后,我們 ./myproc運行它,根據(jù)馮諾依曼體系結(jié)構(gòu)的設(shè)計,它會被加載到內(nèi)存中。此時它就不叫程序了,而應(yīng)該叫做進程。

那么我們應(yīng)該如何查看該進程的屬性呢?

命令:ps axj ,用于顯示系統(tǒng)中當前運行的進程的信息,包括進程ID(PID)、父進程ID(PPID)、CPU使用率、內(nèi)存使用率等 ps(process status)

此時我們我們發(fā)現(xiàn)有很多進程在運行,所以我們可以使用grep命令過濾一下,并且使用head命令顯示文件的第一行。 可使用反向過濾,去除grep myproc 進程

ps ajx | head -1 && ps axj | grep myproc | grep -v grep

因此,將程序運行起來,本質(zhì)上就是在系統(tǒng)中啟動了一個進程,此時進程可分為兩種

執(zhí)行完就退出的進程,例如指令 ls mkdir等一直不退出,除非用戶主動關(guān)閉 - - 常駐進程

2.1 標示符

2.1.1 PID

PID 標示符: 描述本進程的唯一標示符,用來區(qū)別其他進程。

每次運行我們的程序,它的PID都是不同的。

在程序中,可以通過調(diào)用getpid()函數(shù)獲取當前進程的PID

當前我已經(jīng)知道了這個進程的PID,如果我想終止該進程怎么辦呢?

在程序運行處,直接Ctrl + C使用kill命令,該命令可向進程發(fā)送信號。該命令的編號是9,所以我們直接可以:kill -9 + PID

此時,該進程就被殺掉了

查看進程信息時,除了使用ps命令外,如果我想看到進程更多的信息,那我應(yīng)該怎么查呢?

linux下,一切皆文件,因此進程的信息也是以文件的方式保存的。因此在linux系統(tǒng)中,存在一個特殊的目錄proc

proc目錄中以數(shù)字命名的目錄,這個數(shù)字就是指定進程的PID,每一個目錄就代表一個進程,里面包含進程的所有信息。

這也就意味著,當我們運行一個程序后,操作系統(tǒng)就會立即在proc目錄下,創(chuàng)建一個以該進程PID命名的目錄,目錄中存放進程的所有信息,方便上層去查看。進程結(jié)束后,該目錄就被刪除了,不存在了,是實時更新的。

不知道大家有沒有一個疑問,頻繁的創(chuàng)建、刪除、修改文件會不會影響系統(tǒng)的效率?

注意:proc文件不是磁盤級別的文件,它是內(nèi)存級別的文件,因此不會影響

我們進程的PID為2947,可以發(fā)現(xiàn)它確實存在于proc目錄中。

因此,我們?nèi)绻榭催M程的詳細信息,可以使用ls /proc/PID -l,此時就顯示出了該進程文件下的所有信息,即進程的信息。這時顯示的比我們使用ps顯示的就詳細的多。 在這些信息中,我們重點關(guān)注exe 和cwd。

exe :形成這個進程的源可執(zhí)行程序文件是誰cwd:current work directory當前工作目錄

在下面的代碼中,我們打開了一個文件,此時文件不存在,它會自動創(chuàng)建一個文件,但它怎么知道在哪里創(chuàng)建文件呢?

運行程序我們會發(fā)現(xiàn),test.txt文件和我們的進程 myproc創(chuàng)建在同一個位置了

所以,它應(yīng)該是在“當前工作目錄下”創(chuàng)建了文件test.txt,此時的cwd就是當前進程的cwd,將要創(chuàng)建的文件直接拼接在cwd路徑后,即可創(chuàng)建對應(yīng)文件。如果指定了絕對路徑,則會在指定路徑下創(chuàng)建。

在linux系統(tǒng)中,提供了一個系統(tǒng)級的接口chdir(),可供我們改變進程的cwd。

2.1.2 PPID

在Linux系統(tǒng)中,啟動之后,新創(chuàng)建任何進程的時候,都是由父進程創(chuàng)建的?。ǜ高M程讓操作系統(tǒng)建的)

getppid()函數(shù)可以獲得父進程的id。 我們多次運行程序可以發(fā)現(xiàn),子進程的id會變化,但是父進程的id沒有改變。

那么父進程是誰呢?- - bash(命令行解釋器,linux中是bash)

在命令行中,執(zhí)行命令/程序的本質(zhì)是:一個叫做bash的進程,創(chuàng)建了子進程,由子進程執(zhí)行我們的代碼!

那么子進程是如何創(chuàng)建的呢?

2.1.3 創(chuàng)建子進程

如何創(chuàng)建子進程?

fork函數(shù):用來創(chuàng)建子進程。

該函數(shù)的返回值非常特殊,如果進程創(chuàng)建成功,它會返回子進程的PID給父進程(給父進程返回子進程的pid是為了方便父進程管理孩子),返回0給子進程;如果失敗,返回-1給父進程。

創(chuàng)建成功它為什么會返回兩個值呢?我們寫個代碼來看一下

經(jīng)過fork后,該程序就會有兩個執(zhí)行分支,因此第二條printf會執(zhí)行兩次。 而且我們可以發(fā)現(xiàn),第三個printf的父進程是執(zhí)行第二條printf的進程,因此二三條printf是父子進程。 第二條就是執(zhí)行該程序的進程,第三條就是程序中又創(chuàng)建的子進程。

而且我們可以發(fā)現(xiàn),父進程可以有多個子進程,而子進程只能有一個父進程。所以,linux系統(tǒng)中,所有的進程都是樹形結(jié)構(gòu)!

對于fork的返回值,我們使用下面的代碼來驗證一下: 在調(diào)用fork函數(shù)創(chuàng)建子進程后,我們的if 和else兩個條件同時滿足了,而且兩個死循環(huán)同時在跑,這是為什么呢?

這里就證明了,在fork以后,有兩個循環(huán)同時在跑,那么它就一定是有兩個執(zhí)行流,分別叫做父進程和子進程。

那fork為什么會有兩個返回值呢?

因為fork后,會有兩個進程,兩進程為父子關(guān)系。一般而言,代碼是會共享的,但是數(shù)據(jù)只有一份。

通過下面的代碼我們可以發(fā)現(xiàn),父進程與子進程二者不是同一個gval,也就是說它倆各自有一個gval,

為什么父子進程代碼共享,但是數(shù)據(jù)各自私有一份呢?- - 進程是相互獨立的,對各進程之間運行時互不影響,即便是父子。 對于代碼,二者都是只讀的;對于數(shù)據(jù),各自私有一份(使用寫時拷貝的思想) 對于接收fork返回值的id,它也是數(shù)據(jù),因此父子進程中會各有一份,所以我們的if和else這兩個條件都會走,兩個進程各自運行。

fork是一個系統(tǒng)調(diào)用調(diào)用函數(shù),創(chuàng)建子進程的時候,需要先拷貝父進程的task_struct,然后調(diào)整部分屬性,最后在鏈入到進程列表中。在這個函數(shù)執(zhí)行return語句之前,父子進程絕對都已經(jīng)存在了,那兩個進程執(zhí)行各自執(zhí)行依次return語句,返回兩個值也就是理所當然的了。

如何創(chuàng)建多進程呢?一個程序中多次調(diào)用fork函數(shù)即可。并且在父進程中可以收集到子進程的pid,方便管理。

2.2 狀態(tài)

2.2.1 進程的狀態(tài)

相信學(xué)習(xí)過操作系統(tǒng)的伙伴,對進程的理解就是上圖這些東西,但是你真的理解了嗎?上圖是宏觀的描述了操作系統(tǒng)的狀態(tài),對于不同的操作系統(tǒng),它們都滿足上面的狀態(tài),但是不同的操作系統(tǒng)的處理方式又有區(qū)別。

在真正理解進程的狀態(tài)之前,我們補充幾點知識:

并發(fā)和并行

并發(fā):在單CPU的計算機中,并不是把當前進程執(zhí)行完畢以后再執(zhí)行下一個,而是給每個進程分配一個時間片,基于時間片,進行輪換調(diào)度,輪換調(diào)度的過程就叫做并發(fā)。

并行:多個進程在多個CPU下分別、同時運行,叫做并行。

時間片

Linux/Windows等民用操作系統(tǒng),都是分時操作系統(tǒng)(給每個進程分配一個時間片,當時間片耗盡時,就必須從CPU上剝離下來,然后把另一個進程放上去)。分時操作系統(tǒng)的特點:調(diào)度任務(wù)追求公平。

實時操作系統(tǒng):任務(wù)一旦執(zhí)行,從開始到結(jié)束盡量、優(yōu)先的執(zhí)行完畢。

等待的本質(zhì)

阻塞

對于每一個CPU,操作系統(tǒng)都要給其提供一個叫做運行隊列的東西。每次執(zhí)行了一個進程,就是將進程的task_struct鏈入到運行隊列中,CPU在處理進程時,只需要到運行隊列中去取隊頭的task_struct。 當一個進程處于運行隊列當中時,我們就稱該進程為運行狀態(tài)。

計算機大多數(shù)情況下都是在做IO的操作(外設(shè)的訪問),比如獲取鍵盤上的數(shù)據(jù),執(zhí)行scanf。

但是有時候鍵盤遲遲沒有按下,進程也就獲取不數(shù)據(jù),但是CPU不會一直等該進程,此時該進程就會被設(shè)置為阻塞狀態(tài),等待對應(yīng)的底層硬件準備好,該進程才會被重新放到運行隊列中。

那阻塞狀態(tài)具體是怎么做的呢?

由于操作系統(tǒng)也需要管理底層的硬件(先描述,再組織),所以它清楚的知道你硬件此時是什么狀態(tài)(到底有沒有數(shù)據(jù)),如果沒有就該進程就會阻塞,那該進程去哪里“阻塞”呢?- - 去外部設(shè)備上,這里的外部設(shè)備指的是OS所管理的外設(shè)的PCB。 因此,等CPU的就叫做運行狀態(tài),等外設(shè)的叫做阻塞狀態(tài)。

阻塞掛起

阻塞期間,進程不會被調(diào)度,但進程對應(yīng)的PCB與代碼和數(shù)據(jù)也是會占用內(nèi)存的,此時該部分內(nèi)存是被白白浪費的。

當內(nèi)存資源嚴重不足時,操作系統(tǒng)為了保證整個系統(tǒng)的安全,會將進程的代碼和數(shù)據(jù)換出到磁盤中;當進程不阻塞時,OS會再將進程對應(yīng)的代碼和數(shù)據(jù)換入內(nèi)存中。

磁盤中會有一塊分區(qū),專門進行換入和換出工作,該分區(qū)叫做交換分區(qū)(swap分區(qū)),是一種用時間換空間的策略。

當進程處于阻塞狀態(tài),并且操作系統(tǒng)將其代碼和數(shù)據(jù)換出到磁盤中,此時被換出的進程就處于阻塞掛起狀態(tài)。

2.2.2 Linux下進程的狀態(tài)

上面我們已經(jīng)知道了操作系統(tǒng)宏觀上的狀態(tài)解釋,那在具體的Linux系統(tǒng)下又是什么樣的呢?

我們看看Linux內(nèi)核源代碼怎么說

static const char * const task_state_array[] = {

"R (running)", /* 0 */

"S (sleeping)", /* 1 */

"D (disk sleep)", /* 2 */

"T (stopped)", /* 4 */

"t (tracing stop)", /* 8 */

"X (dead)", /* 16 */

"Z (zombie)", /* 32 */

};

R 和 S 狀態(tài)

R:運行狀態(tài)S:休眠狀態(tài)(阻塞等待狀態(tài)),可中斷睡眠(淺睡眠,即可直接被kill掉)

D 狀態(tài)

D:disk sleep,也是阻塞等待的一種狀態(tài)(不可中斷睡眠,深度睡眠),專門為磁盤設(shè)計的狀態(tài)。

為了保護訪問磁盤的進程(保護數(shù)據(jù)),禁止操作系統(tǒng)“殺掉”該進程,該進程就處于D狀態(tài)

T 狀態(tài)

T:暫停進程。

在認識T狀態(tài)前,我們先掌握兩個kill 18和19號。

使用kill -19,就可以讓指定進程暫停掉,此進程的狀態(tài)就變成了T。

使用kill -18,就可以讓指定進程繼續(xù)執(zhí)行,此進程的狀態(tài)就變成了S。為什么不是S+了呢?而且我發(fā)現(xiàn)無法使用Ctrl+C殺掉該進程了。只能使用kill -9 命令了。

為什么不是S+了呢?因為當一個進程被暫停又恢復(fù)后,它就變到后臺去運行。

t 狀態(tài)

t:tracing stop,遇到斷點,進程就被暫停掉了。此時該進程就是被追蹤狀態(tài),斷點處停下來,此時進程的狀態(tài)就是t.

gdb后,會有一個gdb進程,遇到斷點,gdb調(diào)試進程的狀態(tài)就是t了。

X 與 Z狀態(tài)

Z:僵尸狀態(tài) X:死亡狀態(tài),即進程結(jié)束。

先介紹linux下的一條命令:echo $ ?,顯示最近一條進程的退出信息(執(zhí)行狀態(tài),0為正常退出,非0為異常退出)

為什么一個進程要返回執(zhí)行信息呢?- - 通過進程的執(zhí)行結(jié)果,告訴父進程/操作系統(tǒng),我把任務(wù)執(zhí)行的怎么樣。

那什么是僵尸狀態(tài)呢?

當一個進程退出時,它的代碼和數(shù)據(jù)會被釋放掉;但是它的task_struct還存在,task_struct中存著該進程的退出信息(執(zhí)行信息)。當一個進程退出并且父進程沒有讀取到子進程退出的返回代碼時就會產(chǎn)生僵尸進程。僵尸進程會以終止狀態(tài)保持在進程表中,并且會一直在等待父進程讀取退出狀態(tài)代碼。所以,只要子進程退出,父進程還在運行,但父進程沒有讀取子進程狀態(tài),子進程則進入僵尸狀態(tài),僵尸狀態(tài)一直不退出,PCB就要一直維護,直到父進程回收。那一個父進程創(chuàng)建了很多子進程,就是不回收,是不是就會造成內(nèi)存資源的浪費?是的!因為數(shù)據(jù)結(jié)構(gòu)對象本身就要占用內(nèi)存,此時就會造成內(nèi)存泄漏(系統(tǒng)級的)。

為了驗證僵尸狀態(tài),我們寫一個測試代碼

//循環(huán)查詢命令

while :; do ps axj | head -1;ps axj | grep test; sleep 1;done

當我們的程序運行10秒后,子進程就結(jié)束了,但是依然可以看到子進程的信息,而且它的狀態(tài)變成了Z,處于了僵尸狀態(tài)。

孤兒進程

當子進程退出,父進程存在時,叫做僵尸狀態(tài)。那如果反過來,父進程退出,子進程存在呢?

驗證一下

當父進程被殺掉,子進程存在時,可以發(fā)現(xiàn)子進程的ppid變成了1。

我們top一下,發(fā)現(xiàn)1是系統(tǒng)。

因此,當父進程退出時,系統(tǒng)會領(lǐng)養(yǎng)子進程,以便接收子進程的退出信息,回收進程。這個被領(lǐng)養(yǎng)的進程就叫做孤兒進程。孤兒進程運行在后臺

2.3 優(yōu)先級

優(yōu)先級是什么?

獲得某種資源的先后順序。

為什么要有優(yōu)先級

因為資源有限。

優(yōu)先級是怎么做的呢?

task_struct中有一個優(yōu)先級屬性priority,里面包含特定的幾個int類型的變量表示優(yōu)先級。Linux中,優(yōu)先級數(shù)字越小,優(yōu)先級越高。

在linux中,一共有兩種數(shù)字來維護一個進程的優(yōu)先級

PRI:當前進程的優(yōu)先級,默認是80。NI:nice,優(yōu)先級的修正數(shù)據(jù)。最終優(yōu)先級 = PRI + NI

上圖中的UID是用來:標識該進程是誰啟動的。

那如何修改優(yōu)先級呢?

修改優(yōu)先級,只能通過nice來修改的。 方式:用top命令更改已存在進程的nice:輸入top,進入top后按“r”–>輸入進程PID–>輸入nice值

系統(tǒng)會禁止頻繁修改nice值,有時需要使用root修改。nice值的取值范圍是:[-20,19]最終優(yōu)先級是:使用默認pri(80),然后加上nice值,并不是使用上一次的pri,所以上圖中的pri是99和60。因此調(diào)整優(yōu)先級習(xí)慣稱作:優(yōu)先級的重置。

2.4 進程切換(上下文數(shù)據(jù)、程序計數(shù)器 )

當一個進程的時間片到了,它就需要被切換。Linux是基于時間片,進行調(diào)度輪轉(zhuǎn)的。

但是當一個進程的時間片到時,它并不一定能夠執(zhí)行完畢,可以在任何地方被重新調(diào)度切換。

那如何切換呢?

當一個進程被切換走的時候,需要保存該進程執(zhí)行到了哪里;當進程被切換回來時,需要從上次執(zhí)行到的地方恢復(fù)執(zhí)行。即進程切換時,它“從哪里來,回哪里去”,那如何記錄它從哪來到哪去呢?

當一個進程運行的時候,會有很多的臨時數(shù)據(jù),這些臨時數(shù)據(jù)都是保存到CPU的寄存器中的,例如eip、ir。 這些寄存器中,是進程執(zhí)行時瞬時狀態(tài)的信息數(shù)據(jù),即:上下文數(shù)據(jù)。

eip 中存放當前執(zhí)行到的指令的下一條指令的地址ir 中存放正在執(zhí)行的指令

雖然CPU有很多寄存器,但是它只有一套寄存器,是被多個進程共享使用的。如果有多個進程同時運行時,會頻繁的更新寄存器中的數(shù)據(jù)。

如果不預(yù)先將寄存器中當前進程①的數(shù)據(jù)保存,則會被其它進程②覆蓋掉,當進程①又回來時,不知道從哪里開始了,此時就無法完成進程的調(diào)度與切換。

因此,進程切換的核心:進程上下文數(shù)據(jù)的保存和恢復(fù)。

那將寄存器中進程的上下文信息預(yù)先保存到哪里呢?

保存到當前進程的PCB中的一個任務(wù)狀態(tài)段tss中,也就是在內(nèi)存中

2.5 Linux的進程調(diào)度算法

下圖是Linux2.6內(nèi)核中進程隊列的數(shù)據(jù)結(jié)構(gòu)

一個CPU擁有一個runqueue,如果有多個CPU就要考慮進程個數(shù)的負載均衡問題。 優(yōu)先級

普通優(yōu)先級:100~139(我們都是普通的優(yōu)先級,想想nice值的取值范圍,[-20,19],剛好20個)實時優(yōu)先級:0~99

活動隊列

時間片還沒有結(jié)束的所有進程都按照優(yōu)先級放在該隊列nr_active: 總共有多少個運行狀態(tài)的進程queue[140]:一個元素就是一個進程隊列,相同優(yōu)先級的進程按照FIFO規(guī)則進行排隊調(diào)度,所以,數(shù)組下標就是優(yōu)先級! 相同優(yōu)先級的進程在同一個數(shù)組下標中,使用類似于hash桶的思想掛在同一個桶中

從該結(jié)構(gòu)中,選擇一個最合適的進程,過程是怎么的呢?

從0下表開始遍歷queue[140]找到第一個非空隊列,該隊列必定為優(yōu)先級最高的隊列拿到選中隊列的第一個進程,開始運行,調(diào)度完成!遍歷queue[140]時間復(fù)雜度是常數(shù)!但還是太低效了

借助位圖 bitmap[5]:一共140個優(yōu)先級,一共140個進程隊列,為了提高查找非空隊列的效率,就可以用5*32個比特位表示隊列是否為空(即一次檢查一個int,可一次跳躍32個比特位)。這樣,便可以大大提高查找效率。

過期隊列

過期隊列和活動隊列結(jié)構(gòu)一模一樣

過期隊列上放置的進程,都是時間片耗盡的進程/新建進程當活動隊列上的進程都被處理完畢之后,對過期隊列的進程進行時間片重新計算

active指針和expired指針

active指針永遠指向活動隊列 expired指針永遠指向過期隊列可是活動隊列上的進程會越來越少,過期隊列上的進程會越來越多,因為進程時間片到期時一直都存在的。在合適的時候,只要能夠交換active指針和expired指針的內(nèi)容,就相當于有具有了一批新的活動進程!

為什么進程會是運行起來的程序呢?因為進程會被調(diào)度,被切換。

柚子快報激活碼778899分享:【Linux】進程

http://yzkb.51969.com/

相關(guān)文章

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19518059.html

發(fā)布評論

您暫未設(shè)置收款碼

請在主題配置——文章設(shè)置里上傳

掃描二維碼手機訪問

文章目錄