本文目錄:
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時間片的概念。
由此可以知道,所有的進程都有機會運行,但重要的進程總是會獲得更多的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)描述圖,個人感覺右圖更加易于理解。
運行態(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.