本文目錄:

9.1 進程的簡單說明

9.11 進程和程序的區(qū)別

9.12 多任務(wù)和cpu時間片

9.13 父子進程及創(chuàng)建進程的方式

9.14 進程的狀態(tài)

9.15 舉例分析進程狀態(tài)轉(zhuǎn)換過程

9.16 進程結(jié)構(gòu)和子shell

9.2 job任務(wù)

9.3 終端和進程的關(guān)系

9.4 信號

9.41 需知道的信號

9.42 SIGHUP

9.43 僵尸進程和SIGCHLD

9.44 手動發(fā)送信號(kill命令)

9.45 pkill和killall

9.5 fuser和lsof


9.1 進程簡單說明

進程是一個非常復(fù)雜的概念,涉及的內(nèi)容也非常非常多。在這一小節(jié)所列出內(nèi)容,已經(jīng)是我極度簡化后的內(nèi)容了,應(yīng)該盡可能都理解下來,我覺得這些理論比如何使用命令來查看狀態(tài)更重要,而且不明白這些理論,后面查看狀態(tài)信息時基本上不知道狀態(tài)對應(yīng)的是什么意思。

但對于非編程人員來說,更多的進程細節(jié)也沒有必要去深究,當(dāng)然,多多益善是肯定的。

9.1.1 進程和程序的區(qū)別

程序是二進制文件,是靜態(tài)存放在磁盤上的,不會占用系統(tǒng)運行資源(cpu/內(nèi)存)。

進程是用戶執(zhí)行程序或者觸發(fā)程序的結(jié)果,可以認為進程是程序的一個運行實例。進程是動態(tài)的,會申請和使用系統(tǒng)資源,并與操作系統(tǒng)內(nèi)核進行交互。在后文中,不少狀態(tài)統(tǒng)計工具的結(jié)果中顯示的是system類的狀態(tài),其實system狀態(tài)的同義詞就是內(nèi)核狀態(tài)。

9.1.2 多任務(wù)和cpu時間片

現(xiàn)在所有的操作系統(tǒng)都能"同時"運行多個進程,也就是多任務(wù)或者說是并行執(zhí)行。但實際上這是人類的錯覺,一顆物理cpu在同一時刻只能運行一個進程,只有多顆物理cpu才能真正意義上實現(xiàn)多任務(wù)。

人類會產(chǎn)生錯覺,以為操作系統(tǒng)能并行做幾件事情,這是通過在極短時間內(nèi)進行進程間切換實現(xiàn)的,因為時間極短,前一刻執(zhí)行的是進程A,下一刻切換到進程B,不斷的在多個進程間進行切換,使得人類以為在同時處理多件事情。

不過,cpu如何選擇下一個要執(zhí)行的進程,這是一件非常復(fù)雜的事情。在Linux上,決定下一個要運行的進程是通過"調(diào)度類"(調(diào)度程序)來實現(xiàn)的。程序何時運行,由進程的優(yōu)先級決定,但要注意,優(yōu)先級值越低,優(yōu)先級就越高,就越快被調(diào)度類選中。在Linux中,改變進程的nice值,可以影響某類進程的優(yōu)先級值。

有些進程比較重要,要讓其盡快完成,有些進程則比較次要,早點或晚點完成不會有太大影響,所以操作系統(tǒng)要能夠知道哪些進程比較重要,哪些進程比較次要。比較重要的進程,應(yīng)該多給它分配一些cpu的執(zhí)行時間,讓其盡快完成。下圖是cpu時間片的概念。

Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),移動開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn) 

由此可以知道,所有的進程都有機會運行,但重要的進程總是會獲得更多的cpu時間,這種方式是"搶占式多任務(wù)處理":內(nèi)核可以強制在時間片耗盡的情況下收回cpu使用權(quán),并將cpu交給調(diào)度類選中的進程,此外,在某些情況下也可以直接搶占當(dāng)前運行的進程。隨著時間的流逝,分配給進程的時間也會被逐漸消耗,當(dāng)分配時間消耗完畢時,內(nèi)核收回此進程的控制權(quán),并讓下一個進程運行。但因為前面的進程還沒有完成,在未來某個時候調(diào)度類還是會選中它,所以內(nèi)核應(yīng)該將每個進程臨時停止時的運行時環(huán)境(寄存器中的內(nèi)容和頁表)保存下來(保存位置為內(nèi)核占用的內(nèi)存),這稱為保護現(xiàn)場,在下次進程恢復(fù)運行時,將原來的運行時環(huán)境加載到cpu上,這稱為恢復(fù)現(xiàn)場,這樣cpu可以在當(dāng)初的運行時環(huán)境下繼續(xù)執(zhí)行。

看書上說,Linux的調(diào)度器不是通過cpu的時間片流逝來選擇下一個要運行的進程的,而是考慮進程的等待時間,即在就緒隊列等待了多久,那些對時間需求最嚴(yán)格的進程應(yīng)該盡早安排其執(zhí)行。另外,重要的進程分配的cpu運行時間自然會較多。

調(diào)度類選中了下一個要執(zhí)行的進程后,要進行底層的任務(wù)切換,也就是上下文切換,這一過程需要和cpu進程緊密的交互。進程切換不應(yīng)太頻繁,也不應(yīng)太慢。切換太頻繁將導(dǎo)致cpu閑置在保護和恢復(fù)現(xiàn)場的時間過長,保護和恢復(fù)現(xiàn)場對人類或者進程來說是沒有產(chǎn)生生產(chǎn)力的(因為它沒有在執(zhí)行程序)。切換太慢將導(dǎo)致進程調(diào)度切換慢,很可能下一個進程要等待很久才能輪到它執(zhí)行,直白的說,如果你發(fā)出一個ls命令,你可能要等半天,這顯然是不允許的。

至此,也就知道了cpu的衡量單位是時間,就像內(nèi)存的衡量單位是空間大小一樣。進程占用的cpu時間長,說明cpu運行在它身上的時間就長。注意,cpu的百分比值不是其工作強度或頻率高低,而是"進程占用cpu時間/cpu總時間",這個衡量概念一定不要搞錯。

9.1.3 父子進程及創(chuàng)建進程的方式

根據(jù)執(zhí)行程序的用戶UID以及其他標(biāo)準(zhǔn),會為每一個進程分配一個唯一的PID。

父子進程的概念,簡單來說,在某進程(父進程)的環(huán)境下執(zhí)行或調(diào)用程序,這個程序觸發(fā)的進程就是子進程,而進程的PPID表示的是該進程的父進程的PID。由此也知道了,子進程總是由父進程創(chuàng)建。

在Linux,父子進程以樹型結(jié)構(gòu)的方式存在,父進程創(chuàng)建出來的多個子進程之間稱為兄弟進程。CentOS 6上,init進程是所有進程的父進程,CentOS 7上則為systemd。

Linux上創(chuàng)建子進程的方式有三種(極其重要的概念):一種是fork出來的進程,一種是exec出來的進程,一種是clone出來的進程。

(1).fork是復(fù)制進程,它會復(fù)制當(dāng)前進程的副本(不考慮寫時復(fù)制的模式),以適當(dāng)?shù)姆绞綄⑦@些資源交給子進程。所以子進程掌握的資源和父進程是一樣的,包括內(nèi)存中的內(nèi)容,所以也包括環(huán)境變量和變量。但父子進程是完全獨立的,它們是一個程序的兩個實例。

(2).exec是加載另一個應(yīng)用程序,替代當(dāng)前運行的進程,也就是說在不創(chuàng)建新進程的情況下加載一個新程序。exec還有一個動作,在進程執(zhí)行完畢后,退出exec所在的shell。所以為了保證進程安全,若要形成新的且獨立的子進程,都會先fork一份當(dāng)前進程,然后在fork出來的子進程上調(diào)用exec來加載新程序替代該子進程。例如在bash下執(zhí)行cp命令,會先fork出一個bash,然后再exec加載cp程序覆蓋子bash進程變成cp進程。

(3).clone用于實現(xiàn)線程。clone的工作原理和fork相同,但clone出來的新進程不獨立于父進程,它只會和父進程共享某些資源,在clone進程的時候,可以指定要共享的是哪些資源。

一般情況下,兄弟進程之間是相互獨立、互不可見的,但有時候通過特殊手段,它們會實現(xiàn)進程間通信。例如管道協(xié)調(diào)了兩邊的進程,兩邊的進程屬于同一個進程組,它們的PPID是一樣的,管道使得它們可以以"管道"的方式傳遞數(shù)據(jù)。

進程是有所有者的,也就是它的發(fā)起者,某個用戶如果它非進程發(fā)起者、非父進程發(fā)起者、非root用戶,那么它無法殺死進程。且殺死父進程(非終端進程),會導(dǎo)致子進程變成孤兒進程,孤兒進程的父進程總是init/systemd。

9.1.4 進程的狀態(tài)

進程并非總是處于運行中,至少cpu沒運行在它身上時它就是非運行的。進程有幾種狀態(tài),不同的狀態(tài)之間可以實現(xiàn)狀態(tài)切換。下圖是非常經(jīng)典的進程狀態(tài)描述圖,個人感覺右圖更加易于理解。

 Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),移動開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)Android培訓(xùn),安卓培訓(xùn),手機開發(fā)培訓(xùn),移動開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

運行態(tài):進程正在運行,也即是cpu正在它身上。

就緒(等待)態(tài):進程可以運行,已經(jīng)處于等待隊列中,也就是說調(diào)度類下次可能會選中它

睡眠(阻塞)態(tài):進程睡眠了,不可運行。

各狀態(tài)之間的轉(zhuǎn)換方式為:(也許可能不太好理解,可以結(jié)合稍后的例子)

(1)新狀態(tài)->就緒態(tài):當(dāng)?shù)却犃性试S接納新進程時,內(nèi)核便把新進程移入等待隊列。

(2)就緒態(tài)->運行態(tài):調(diào)度類選中等待隊列中的某個進程,該進程進入運行態(tài)。

(3)運行態(tài)->睡眠態(tài):正在運行的進程因需要等待某事件(如IO等待、信號等待等)的出現(xiàn)而無法執(zhí)行,進入睡眠態(tài)。

(4)睡眠態(tài)->就緒態(tài):進程所等待的事件發(fā)生了,進程就從睡眠態(tài)排入等待隊列,等待下次被選中執(zhí)行。

(5)運行態(tài)->就緒態(tài):正在執(zhí)行的進程因時間片用完而被暫停執(zhí)行;或者在搶占式調(diào)度方式中,高優(yōu)先級進程強制搶占了正在執(zhí)行的低優(yōu)先級進程。

(6)運行態(tài)->終止態(tài):一個進程已完成或發(fā)生某種特殊事件,進程將變?yōu)榻K止?fàn)顟B(tài)。對于命令來說,一般都會返回退出狀態(tài)碼。

注意上面的圖中,沒有"就緒-->睡眠"和"睡眠-->運行"的狀態(tài)切換。這很容易理解。對于"就緒-->睡眠",等待中的進程本就已經(jīng)進入了等待隊列,表示可運行,而進入睡眠態(tài)表示暫時不可運行,這本身就是沖突的;對于"睡眠-->運行"這也是行不通的,因為調(diào)度類只會從等待隊列中挑出下一次要運行的進程。

再說說運行態(tài)-->睡眠態(tài)。從運行態(tài)到睡眠態(tài)一般是等待某事件的出現(xiàn),例如等待信號通知,等待IO完成。信號通知很容易理解,而對于IO等待,程序要運行起來,cpu就要執(zhí)行該程序的指令,同時還需要輸入數(shù)據(jù),可能是變量數(shù)據(jù)、鍵盤輸入數(shù)據(jù)或磁盤文件中的數(shù)據(jù),后兩種數(shù)據(jù)相對cpu來說,都是極慢極慢的。但不管怎樣,如果cpu在需要數(shù)據(jù)的那一刻卻得不到數(shù)據(jù),cpu就只能閑置下來,這肯定是不應(yīng)該的,因為cpu是極其珍貴的資源,所以內(nèi)核應(yīng)該讓正在運行且需要數(shù)據(jù)的進程暫時進入睡眠,等它的數(shù)據(jù)都準(zhǔn)備好了再回到等待隊列等待被調(diào)度類選中。這就是IO等待。

其實上面的圖中少了一種進程的特殊狀態(tài)——僵尸態(tài)。僵尸態(tài)進程表示的是進程已經(jīng)轉(zhuǎn)為終止態(tài),它已經(jīng)完成了它的使命并消逝了,但是內(nèi)核還沒有來得及將它在進程列表中的項刪除,也就是說內(nèi)核沒給它料理后事,這就造成了一個進程是死的也是活著的假象,說它死了是因為它不再消耗資源,調(diào)度類也不可能選中它并讓它運行,說它活著是因為在進程列表中還存在對應(yīng)的表項,可以被捕捉到。僵尸態(tài)進程并不占用多少資源,它僅在進程列表中占用一點點的內(nèi)存。大多數(shù)僵尸進程的出現(xiàn)都是因為進程正常終止(包括kill -9),但父進程沒有確認該進程已經(jīng)終止,所以沒有通告給內(nèi)核,內(nèi)核也就不知道該進程已經(jīng)終止了。僵尸進程更具體說明見后文。

另外,睡眠態(tài)是一個非常寬泛的概念,分為可中斷睡眠和不可中斷睡眠??芍袛嗨呤窃试S接收外界信號和內(nèi)核信號而被喚醒的睡眠,絕大多數(shù)睡眠都是可中斷睡眠,能ps或top捕捉到的睡眠也幾乎總是可中斷睡眠;不可中斷睡眠只能由內(nèi)核發(fā)起信號來喚醒,外界無法通過信號來喚醒,主要表現(xiàn)在和硬件交互的時候。例如cat一個文件時,從硬盤上加載數(shù)據(jù)到內(nèi)存中,在和硬件交互的那一小段時間一定是不可中斷的,否則在加載數(shù)據(jù)的時候突然被人為發(fā)送的信號手動喚醒,而被喚醒時和硬件交互的過程又還沒完成,所以即使喚醒了也沒法將cpu交給它運行,所以cat一個文件的時候不可能只顯示一部分內(nèi)容。而且,不可中斷睡眠若能被人為喚醒,更嚴(yán)重的后果是硬件崩潰。由此可知,不可中斷睡眠是為了保護某些重要進程,也是為了讓cpu不被浪費。一般不可中斷睡眠的存在時間極短,也極難通過非編程方式捕捉到。

其實只要發(fā)現(xiàn)進程存在,且非僵尸態(tài)進程,還不占用cpu資源,那么它就是睡眠的。包括后文中出現(xiàn)的暫停態(tài)、追蹤態(tài),它們也都是睡眠態(tài)。

9.1.5 舉例分析進程狀態(tài)轉(zhuǎn)換過程

進程間狀態(tài)的轉(zhuǎn)換情況可能很復(fù)雜,這里舉一個例子,盡可能詳細地描述它們。

以在bash下執(zhí)行cp命令為例。在當(dāng)前bash環(huán)境下,處于可運行狀態(tài)(即就緒態(tài))時,當(dāng)執(zhí)行cp命令時,首先fork出一個bash子進程,然后在子bash上exec加載cp程序,cp子進程進入等待隊列,由于在命令行下敲的命令,所以優(yōu)先級較高,調(diào)度類很快選中它。在cp這個子進程執(zhí)行過程中,父進程bash會進入睡眠狀態(tài)(不僅是因為cpu只有一顆的情況下一次只能執(zhí)行一個進程,還因為進程等待),并等待被喚醒,此刻bash無法和人類交互。當(dāng)cp命令執(zhí)行完畢,它將自己的退出狀態(tài)碼告知父進程,此次復(fù)制是成功還是失敗,然后cp進程自己消逝掉,父進程bash被喚醒再次進入等待隊列,并且此時bash已經(jīng)獲得了cp退出狀態(tài)碼。根據(jù)狀態(tài)碼這個"信號",父進程bash知道了子進程已經(jīng)終止,所以通告給內(nèi)核,內(nèi)核收到通知后將進程列表中的cp進程項刪除。至此,整個cp進程正常完成。

假如cp這個子進程復(fù)制的是一個大文件,一個cpu時間片無法完成復(fù)制,那么在一個cpu時間片消耗盡的時候它將進入等待隊列。

假如cp這個子進程復(fù)制文件時,目標(biāo)位置已經(jīng)有了同名文件,那么默認會詢問是否覆蓋,發(fā)出詢問時它等待yes或no的信號,所以它進入了睡眠狀態(tài)(可中斷睡眠),當(dāng)在鍵盤上敲入yes或no信號給cp的時候,cp收到信號,從睡眠態(tài)轉(zhuǎn)入就緒態(tài),等待調(diào)度類選中它完成cp進程。

在cp復(fù)制時,它需要和磁盤交互,在和硬件交互的短暫過程中,cp將處于不可中斷睡眠。

假如cp進程結(jié)束了,但是結(jié)束的過程出現(xiàn)了某種意外,使得bash這個父進程不知道它已經(jīng)結(jié)束了(此例中是不可能出現(xiàn)這種情況的),那么bash就不會通知內(nèi)核回收進程列表中的cp表項,cp此時就成了僵尸進程。

9.1.6 進程結(jié)構(gòu)和子shell

  • 前臺進程:一般命令(如cp命令)在執(zhí)行時都會fork子進程來執(zhí)行,在子進程執(zhí)行過程中,父進程會進入睡眠,這類是前臺進程。前臺進程執(zhí)行時,其父進程睡眠,因為cpu只有一顆,即使是多顆cpu,也會因為執(zhí)行流(進程等待)的原因而只能執(zhí)行一個進程,要想實現(xiàn)真正的多任務(wù),應(yīng)該使用進程內(nèi)多線程實現(xiàn)多個執(zhí)行流。

  • 后臺進程:若在執(zhí)行命令時,在命令的結(jié)尾加上符號"&",它會進入后臺。將命令放入后臺,會立即返回父進程,并返回該后臺進程的的jobid和pid,所以后臺進程的父進程不會進入睡眠。當(dāng)后臺進程出錯,或者執(zhí)行完成,總之后臺進程終止時,父進程會收到信號。所以,通過在命令后加上"&",再在"&"后給定另一個要執(zhí)行的命令,可以實現(xiàn)"偽并行"執(zhí)行的方式,例如"cp /etc/fstab /tmp & cat /etc/fstab"。

  • bash內(nèi)置命令:bash內(nèi)置命令是非常特殊的,父進程不會創(chuàng)建子進程來執(zhí)行這些命令,而是直接在當(dāng)前bash進程中執(zhí)行。但如果將內(nèi)置命令放在管道后,則此內(nèi)置命令將和管道左邊的進程同屬于一個進程組,所以仍然會創(chuàng)建子進程。

說到這了,應(yīng)該解釋下子shell,這個特殊的子進程。

一般fork出來的子進程,內(nèi)容和父進程是一樣的,包括變量,例如執(zhí)行cp命令時也能獲取到父進程的變量。但是cp命令是在哪里執(zhí)行的呢?在子shell中。執(zhí)行cp命令敲入回車后,當(dāng)前的bash進程fork出一個子bash,然后子bash通過exec加載cp程序替代子bash。請不要在此糾結(jié)子bash和子shell,如果搞不清它們的關(guān)系,就當(dāng)它是同一種東西好了。

那是否可以這樣理解,所有命令其運行環(huán)境都是在子shell中呢?顯然,上面所說的bash內(nèi)置命令不是在子shell中運行的。其他的所有方式,都是在子shell中完成,只不過方式不盡相同。完整的子shell參見man bash,在其中非常多的地方都提到了子shell。以下列出幾種常見的方式。

  • (1).直接執(zhí)行bash命令。這是一個很巧合的命令。bash命令本身是bash內(nèi)置命令,在當(dāng)前shell環(huán)境下執(zhí)行內(nèi)置命令本不會創(chuàng)建子shell,也就是說不會有獨立的bash進程出現(xiàn),而實際結(jié)果則表現(xiàn)為新的bash是一個子進程。其中一個原因是執(zhí)行bash命令會加載各種環(huán)境配置項,為了父bash的環(huán)境得到保護而不被覆蓋,所以應(yīng)該讓其以子shell的方式存在。雖然fork出來的bash子進程內(nèi)容完全繼承父shell,但因重新加載了環(huán)境配置項,所以子shell沒有繼承普通變量,更準(zhǔn)確的說是覆蓋了從父shell中繼承的變量。不妨試試在/etc/bashrc文件中定義一個變量,再在父shell中導(dǎo)出名稱相同值卻不同的環(huán)境變量,然后到子shell中看看該變量的值為何?

  • (2).執(zhí)行shell腳本。因為腳本中第一行總是"#!/bin/bash"或者直接"bash xyz.sh",所以這和上面的執(zhí)行bash進入子shell其實是一回事,都是使用bash命令進入子shell。只不過執(zhí)行腳本多了一個動作:命令執(zhí)行完畢后自動退出子shell。也因此執(zhí)行腳本時,腳本中不會繼承父shell的環(huán)境變量。

  • (3).非內(nèi)置命令的命令替換。當(dāng)命令中包含了命令替換部分時,將先執(zhí)行這部分內(nèi)容,如果這部分內(nèi)容不是內(nèi)置命令,將在子shell中完成,再將執(zhí)行結(jié)果返回給當(dāng)前命令。因為這次的子shell不是通過bash命令進入的子shell,所以它會繼承父shell的所有變量內(nèi)容。這也就解釋了"$(echo $$)"中"$$"的結(jié)果是當(dāng)前bash的pid號,而不是子shell的pid號,因為它不是使用bash命令進入的子shell。

還有兩種特殊的腳本調(diào)用方式:exec和source。

  • exec:exec是加載程序替換當(dāng)前進程,所以它不開啟子shell,而是直接在當(dāng)前shell中執(zhí)行命令或腳本,執(zhí)行完exec后直接退出exec所在的shell。這就解釋了為何bash下執(zhí)行cp命令時,cp執(zhí)行完畢后會自動退出cp所在的子shell。

  • source:source一般用來加載環(huán)境配置類腳本,無法直接加載命令。它也不會開啟子shell,直接在當(dāng)前shell中執(zhí)行調(diào)用腳本且執(zhí)行腳本后不退出當(dāng)前shell,所以腳本會繼承當(dāng)前已有的變量,且腳本執(zhí)行完畢后加載的環(huán)境變量會粘滯給當(dāng)前shell,在當(dāng)前shell生效。

9.2 job任務(wù)

大部分進程都能將其放入后臺,這時它就是一個后臺任務(wù),所以常稱為job,每個開啟的shell會維護一個job table,后臺中的每個job都在job table中對應(yīng)一個Job項。

手動將命令或腳本放入后臺運行的方式是在命令行后加上"&"符號。例如:

[root@server2 ~]# cp /etc/fstab  /tmp/ &[1] 8701

將進程放入后臺后,會立即返回其父進程,一般對于手動放入后臺的進程都是在bash下進行的,所以立即返回bash環(huán)境。在返回父進程的同時,還會返回給父進程其jobid和pid。未來要引用jobid,都應(yīng)該在jobid前加上百分號"%",其中"%%"表示當(dāng)前job,例如"kill -9 %1"表示殺掉jobid為1的后臺進程,如果不加百分號,完了,把Init進程給殺了。

通過jobs命令可以查看后臺job信息。

jobs [--l:jobs默認不會列出后臺工作的PID,加上---s:顯示后臺工作處于stopped狀態(tài)的jobs

通過"&"放入后臺的任務(wù),在后臺中仍會處于運行中。當(dāng)然,對于那種交互式如vim類的命令,將轉(zhuǎn)入暫停運行狀態(tài)。

[root@server2 ~]# sleep 10 &[1] 8710[root@server2 ~]# jobs
[1]+  Running                 sleep 10 &

一定要注意,此處看到的是running和ps或top顯示的R狀態(tài),它們并不總是表示正在運行,處于等待隊列的進程也屬于running。它們都屬于task_running標(biāo)識。

另一種手動加入后臺的方式是按下CTRL+Z鍵,這可以將正在運行中的進程加入到后臺,但這樣加入后臺的進程會在后臺暫停運行。

[root@server2 ~]# sleep 10^Z
[1]+  Stopped                 sleep 10[root@server2 ~]# jobs
[1]+  Stopped                 sleep 10

從jobs信息也看到了在每個jobid的后面有個"+"號,還有"-",或者不帶符號。

[root@server2 ~]# sleep 30&vim /etc/my.cnf&sleep 50&[1] 8915[2] 8916[3] 8917
[root@server2 ~]# jobs
[1]   Running                 sleep 30 &[2]+  Stopped                 vim /etc/my.cnf
[3]-  Running                 sleep 50 &

發(fā)現(xiàn)vim的進程后是加號,"+"表示執(zhí)行中的任務(wù),也就是說cpu正在它身上,"-"表示被調(diào)度類選中的下個要執(zhí)行的任務(wù),從第三個任務(wù)開始不會再對其標(biāo)注。從jobs的狀態(tài)可以分析出來,后臺任務(wù)表中running但沒有"+"的表示處于等待隊列,running且?guī)в?quot;+"的表示正在執(zhí)行,stopped狀態(tài)的表示處于睡眠狀態(tài)。但不能認為job列表中任務(wù)一直是這樣的狀態(tài),因為每個任務(wù)分配到的時間片實際上都很短,在很短的時間內(nèi)執(zhí)行完這一次時間片長度的任務(wù),立刻切換到下一個任務(wù)并執(zhí)行。只不過實際過程中,因為切換速度和每個任務(wù)的時間片都極短,所以任務(wù)列表較小時,顯示出來的順序可能不怎么會出現(xiàn)變動。

就上面的例子而言,下一個要執(zhí)行的任務(wù)是vim,但它是stop的,難道因為這個第一順位的進程stop,其他進程就不執(zhí)行嗎?顯然不是這樣的。事實上,過不了多久,會發(fā)現(xiàn)另外兩個sleep任務(wù)已經(jīng)完成了,但vim仍處于stop狀態(tài)。

[root@server2 ~]# jobs
[1]   Done                    sleep 30[2]+  Stopped                 vim /etc/my.cnf
[3]-  Done                    sleep 50

通過這個job例子,是不是更深入的理解了一點內(nèi)核調(diào)度進程的方式呢?

回歸正題。既然能手動將進程放入后臺,那肯定能調(diào)回到前臺,調(diào)到前臺查看了下執(zhí)行進度,又想調(diào)入后臺,這肯定也得有方法,總不能使用CTRL+Z以暫停方式加到后臺吧。

fg和bg命令分別是foreground和background的縮寫,也就是放入前臺和放入后臺,嚴(yán)格的說,是以運行狀態(tài)放入前臺和后臺,即使原來任務(wù)是stopped狀態(tài)的。

操作方式也很簡單,直接在命令后加上jobid即可(即[fg|bg] [%jobid]),不給定jobid時操作的將是當(dāng)前任務(wù),即帶有"+"的任務(wù)項。

[root@server2 ~]# sleep 20^Z                # 按下CTRL+Z進入暫停并放入后臺
[3]+  Stopped                 sleep 20
[root@server2 ~]# jobs
[2]-  Stopped                 vim /etc/my.cnf
[3]+  Stopped                 sleep 20       # 此時為stopped狀態(tài)
[root@server2 ~]# bg %3            # 使用bg或fg可以讓暫停狀態(tài)的進程變會運行態(tài)
[3]+ sleep 20 &
[root@server2 ~]# jobs
[2]+  Stopped                 vim /etc/my.cnf
[3]-  Running                 sleep 20 &     # 已經(jīng)變成運行態(tài)

使用disown命令可以從job table中直接移除一個job,僅僅只是移出job table,并非是結(jié)束任務(wù)。而且移除job table后,任務(wù)將掛在init/systemd進程下,使其不依賴于終端

disown [-ar] [-h] [%jobid ...]

回到系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7048359.html

轉(zhuǎn)載請注明出處:http://www.cnblogs.com/f-ck-need-u/p/7058920.html

http://www.cnblogs.com/f-ck-need-u/p/7058920.