-- 作者 謝恩銘 轉載請註明出處
內容簡介
- 第三部分第四課:後臺執行及合併多個終端
- 第三部分第五課預告:延時執行,唯慢不破
後臺執行及合併多個終端
上一課 Linux探索之旅 | 第三部分第三課:監視系統活動,滴水不漏 中,我們簡單介紹了程式,也學習瞭如何列出系統中的程式,如何過濾列表結果,還有如何結束程式。
這一課我們繼續乘勝追擊,一路向北,來學習程式的後臺執行。
我們使用的終端讓我們難免有一種感覺:我們每次只能在一個終端中執行一個程式。但其實這是大錯特錯的。
終端還可以執行後臺程式。要使一個程式在後臺執行,有幾種方法,我們都將學習。
這一課的第三節還將學習screen這個程式,用於同時開多個終端視窗。
&符號和nohup命令:後臺執行程式
我們到目前為止用終端做的事情都是眼目所能及的,也就是說:我們執行的命令都是在前臺可見的。
這樣有一個好處是我們可以看到命令執行的過程,有什麼問題可以及時發現。但是也有缺陷,例如有的命令執行耗時良久,我們又不想無所事事,怎麼辦呢?難道我開一個終端專門執行一個耗時命令,然後為了能做其他事情,我再啟動一個終端,那也很不方便。而且,這樣的規避方法在非圖形介面的終端(還記得我們的tty1~tty6嗎?)中是難以實現的,因為只有一個終端視窗。
所以這課顯得尤為重要。
事實上,我們可以在同一個終端中同時執行好幾個命令。怎麼做呢?就需要用到後臺程式的概念。
前臺程式和後臺程式
預設情況下,使用者建立的程式都是前臺程式。前臺程式從鍵盤讀取資料,並把處理結果輸出到顯示器。
我們可以看到前臺程式的執行過程。例如,使用 ls 命令來遍歷當前目錄下的檔案。
ls複製程式碼
這個程式就執行在前臺,它會直接把結果輸出到顯示器。如果 ls 命令需要資料(實際上不需要),那麼它會等待使用者從鍵盤輸入。
當程式執行在前臺時,由於命令提示符($)還未出現,使用者不能輸入其他命令;即使程式需要執行很長時間,也必須等待程式執行結束才能輸入其他命令。
後臺程式與鍵盤沒有必然的關係。當然,後臺程式也可能會等待鍵盤輸入。
後臺程式的優點是不必等待程式執行結束就可以輸入其他命令。
那麼怎麼使一個程式(程式的例項)執行在後臺呢?
&符號:在後臺執行程式
前面說過,讓一個程式在後臺執行有幾種方法。
我們帶大家來學習第一種,很簡單:就是在你要執行的命令最後加上&這個符號。
我們可以用熟悉的cp命令做例子。例如,我執行cp命令來拷貝檔案:emacs的軟體包。
cp emacs-24.4.tar.gz emacs-24.4-copy.tar.gz &複製程式碼
上圖中,因為命令最後加了&符號,執行時此程式就成為了後臺程式。終端輸出了一些資訊:
[1] 16525複製程式碼
[1]:這是此終端的後臺程式的標號。因為這是第一個後臺程式,所以標號為1。
16525:這是程式號(PID),如果你想要結束這個後臺程式,你可以用我們上一課學習的kill命令:
kill 16525複製程式碼
我們雖然看不到這個拷貝程式的“所作所為”,但它確實在後臺默默進行著檔案的拷貝。
如果我們用其他命令試一下,例如find命令,你會吃驚的。
例如我們執行:
find / -name "*log" &複製程式碼
意思是:在根目錄/下查詢所有以log結尾的檔名的檔案,並且在後臺執行此程式。
但是你會發現,find命令雖然在後臺執行了,但是終端還是會不斷顯示所有找到的內容或錯誤資訊。雖然我們還可以在終端中輸入其他命令,但是一直會跳出find搜尋的結果還是很讓人感到厭煩的。最後小編不得不把它停止(用kill命令)。
幸好,我們之前學過重定向,我們可以把find的輸出結果重定向到檔案裡,就不會再來煩我們了。
find / -name "*log" > output_find &複製程式碼
這樣就不會一直有資訊輸出了。
當然了,我們還可以更保險一些,將標準錯誤輸出也重定向到同一個檔案,這樣就不會有任何輸出了。
find / -name "*log" > output_find 2>&1 &複製程式碼
但現在有一個問題:雖然我們的程式是被放到後臺了,在終端貌似看不到它的執行過程了。但是此程式還是與此終端相關聯的,假如我們把終端關閉,那麼這個程式也就會結束。
nohup命令:使程式與終端分離
&符號雖然常用,但卻有一個不可忽視的缺點:後臺程式與終端相關聯,一旦終端關閉或者使用者登出,程式就自動結束。
如果我們想讓程式在以上情況下仍然繼續在後臺執行,那麼我們須要用到nohup命令。
當使用者登出(logout)或者網路斷開時,終端會收到 HUP(是hangup的縮寫,英語《結束通話》的意思)訊號從而關閉其所有子程式;終端被關閉時也會關閉其子程式。
我們可以用nohup命令使命令不受HUP訊號影響。
我們用man來看一下nohup命令的解釋:
可以看到,nohup命令的簡單描述如下:
run a command immune to hangups, with output to a not-tty複製程式碼
翻譯出來大致就是:使得執行的命令不受hangup訊號影響,而且輸出會存放到一個非tty中。
nohup命令的用法很簡單:在nohup命令之後接要執行的命令。例如:
可以看到這次的輸出資訊是:ignoring input and appending output to nohup.out
大致意思是:忽略輸入,把輸出追加到nohup.out檔案中。
使用nohup命令後,輸出會被預設地追加寫入到一個叫nohup.out的檔案裡。
現在,我們的程式已經不受終端關閉或者使用者斷開連線的影響了,會一直執行。當然了,用kill命令還是可以結束此程式的。
nohup命令相當有用。想象以下場景:
我登入遠端伺服器,然後執行了一個耗時命令,或者一個需要一直執行的命令,例如一個遊戲的伺服器程式。這時假如我掉線了,或者我不小心用exit命令退出了登入。那麼這個耗時命令也會中止執行。那就很麻煩了。而且,如果這個程式本應該一直執行很久的,我也不可能一直保持登入狀態等它結束啊。
我家裡還有老婆孩子呢,不能不去做飯啊,我要下班。。。開個小玩笑。
幸好,nohup命令解決了這樣的難題。
Ctrl + Z,jobs,bg和fg命令:控制程式的前後臺切換
我們來考慮一種情況:假如你要將程式轉到後臺執行,但是執行命令時忘記了在最後加上&符號。
如何再使此程式轉為後臺程式呢?有幾種方法。我們一一來學習。
Ctrl + Z:轉到後臺,並暫停執行
我們用top命令來演示。執行:
top複製程式碼
因為top命令的作用是實時地顯示各種系統資訊和程式列表。這時,我們按下Ctrl + Z這個組合鍵:
可以看到終端顯示了
[1]+ Stopped top複製程式碼
這行資訊。
stopped是英語《停止的》的意思,然後我們又看到[1]這個熟悉的資訊,表示這是此終端第一個後臺程式。
所以表示top命令被放到了後臺,此程式還是駐留在記憶體中,但是被暫停執行了。這個時候命令提示符又出現了,我們可以做其他事情了。
bg命令:使程式轉到後臺
經過上面的Ctrl + Z操作,我們可憐的top程式已經被“打入冷宮”(轉入後臺,並且被暫停執行了)。
但是皇后不甘心啊:“臣妾雖然做不到,但即使在冷宮中,我也要工於心計,運籌帷幄,以期早日打敗甄嬛”。
那怎麼辦呢?可以執行bg命令。
就是很簡單地輸入bg,然後回車。bg是英語background的縮寫,表示《後臺》。
bg命令的作用是將命令轉入後臺執行。假如命令已經在後臺,並且暫停著,那麼bg命令會將其狀態改為執行。
不加任何引數,bg命令會預設作用於最近的一個後臺程式,也就是剛才被Ctrl + Z暫停的top程式。如果後面加 %1,%2這樣的引數(不帶%,直接1,2這樣也可以),則是作用於指定標號的程式。因為程式轉入後臺之後,會顯示它在當前終端下的後臺程式編號。例如目前top程式轉入了後臺,它的程式編號是1(可以由[1]+推斷)。依次類推,bg %2就是作用於編號為2的後臺程式。
我們輸入bg,然後回車。看到如下輸出:
上圖中,終端首先顯示了:
[1]+ top &複製程式碼
表示top命令被轉到了後臺,但是接著,它又顯示了這一條資訊:
[1]+ Stopped top複製程式碼
咦,為什麼top程式還是暫停著呢?anz理說bg命令會把在後臺暫停的程式重新喚醒,使之在後臺重新執行啊。
我們用ps命令來檢視一下程式資訊:
ps -aux複製程式碼
在上圖中可以看到,top這個程式的程式號是23051,狀態是T。
首先補充一些知識:
Linux中,程式有5種狀態:
- 執行 (正在執行或在執行佇列中等待)
- 中斷 (休眠中, 受阻, 在等待某個條件的形成或接受到訊號)
- 不可中斷 (收到訊號不喚醒和不可執行, 程式必須等待直到有中斷髮生)
- 僵死 (程式已終止, 但程式描述符存在, 直到父程式呼叫wait4()系統呼叫後釋放)
- 停止 (程式收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU訊號後停止執行執行)
ps命令標識程式的5種狀態碼:
- D 不可中斷 uninterruptible sleep (usually IO)
- R 執行 runnable (on run queue)
- S 中斷 sleeping
- T 停止 traced or stopped
- Z 僵死 a defunct ("zombie") process
因此,我們的top程式的狀態還是T,也就是停止(stopped)的狀態。很奇怪是嗎?
我們用其他的命令來測試看看。我們測試find命令。首先執行:
find / -name "*log" > find_log 2>&1複製程式碼
上面這個命令的作用是:在根目錄 / 下查詢以log結尾的檔案,將標準輸出和標準錯誤輸出都重定向到find_log檔案中。
因此,雖然上述命令在執行,但終端中看不到任何資訊。
我們用Ctrl + Z來暫停此程式,並將其轉到後臺。然後再執行bg命令,使其重新在後臺執行。
奇怪了,為什麼bg作用於暫停的find命令後,並沒有像剛才top命令一樣仍然顯示Stopped呢?
我們再用ps -aux 看一下:
可以看到,top命令的狀態是T,也就是停止(Stopped)。而find命令的狀態則是D,也即是不可中斷的睡眠(但其實是在執行,等會我們就會看到)。
疑問:所以小編也不清楚為什麼對普通的命令(例如find),bg命令是起作用的,會將其轉成後臺執行。
但是對於top命令,bg為什麼不能將其轉成後臺執行,可能是因為top命令本身比較特殊吧。
也許是因為top命令是前臺互動式命令,因此不能被置於後臺執行。
小結一下:
如果你本想要使一個命令執行在後臺,成為後臺程式,但是忘記加&符號了。那麼可以按下面的順序使此程式轉為後臺執行:
Ctrl + Z:使程式轉為後臺暫停。
bg:使程式轉為後臺執行。
那你也許要問:為什麼不直接用bg命令一步到位呢?
因為,如果不先用Ctrl + Z將此程式暫停,此程式就一直在前臺執行,你沒法在命令提示符後面輸入啊。
jobs命令:顯示後臺程式狀態
這個命令很強大,畢竟和賈伯斯喬老爺子(賈伯斯的英文就是jobs,全名是Steve Jobs。job是英語《工作》的意思,jobs就是job的複數形式)一樣名字麼。
jobs命令的作用是顯示當前終端裡的後臺程式狀態。雖然我們可以用ps命令來檢視程式狀態,但是ps命令輸出的程式列表太長了。
聰明如你一定想到了,我們可以用jobs命令來顯示剛才那兩個程式的狀態:top程式和find程式。
jobs命令的輸出共分三列,我們逐列來說明:
顯示後臺程式標號:比如上例中top程式的標號是1,find程式的標號是2,如果還有其他後臺程式,那麼就會有[3],[4],等等。這個標號和PID(程式號)是不一樣的。這個標號只是顯示當前終端下的後臺程式的一個編號。
顯示後臺程式狀態:比如Stopped是停止的意思,Running是執行的意思。還有其他狀態。
命令本身。
可以看到,我們的top程式確實是在後臺暫停了,因為顯示Stopped,是英語《停止的》的意思。find程式在後臺執行,因為顯示Running,是英語《執行中》的意思。
fg命令:使程式轉到前臺
fg是英語foreground的意思,表示《前臺》。
與bg命令相反,fg命令的作用是:使程式轉為前臺執行。
用法也很簡單,和bg一樣,如果不加引數,那麼fg命令作用於最近的一個後臺程式。如果加引數,如%2,那麼表示作用於本終端中第二個後臺程式。
好了,講了這麼多知識點,是不是有點暈呢?沒關係。
我們用下面這個狀態圖來做個總結,應該就很清楚了:
解釋一下上圖:
如果我們執行一個程式,預設情況下,它會成為一個前臺執行的程式。我們可以按組合鍵Ctrl + C來銷燬此程式。
我們也可以使此程式在後臺執行。假如執行程式時就用&放在命令最後,那麼程式就會在後臺執行。
假如在程式執行起來後,按Ctrl + Z,則程式會轉到後臺,並且停止。此時如果執行bg命令,則程式重新執行,並繼續在後臺。
fg命令可以使程式轉到前臺,並且執行。
花點時間好好理解一下這個狀態圖。這個圖很重要,幾乎概括了後臺前臺程式切換的所有情況。
screen命令:合併多個終端
最後一節,我們來學習一個特殊的命令:screen
screen是英語“螢幕”的意思。
screen這個程式(所有命令其實都是程式)通常沒有在Linux發行版裡預裝,如果你的Ubuntu系統裡沒有screen這個程式,那麼可以如此安裝:
sudo apt-get install screen複製程式碼
安裝完之後,你可以輸入screen命令,回車,會顯示以下內容:
按回車或空格跳過這個介紹頁面。
screen命令用於在一個終端中開啟多個終端,就好像在一個頁面中開多個標籤欄一樣(使用過瀏覽器的朋友肯定有這種使用經驗),很酷吧。
但是screen開啟的多個終端是重疊在一起的,如果你不知道,還以為只是開啟了一個終端,但是我們會學習如何在各個開啟的終端間切換。
在我們執行了screen命令後,再用Enter鍵跳過那頁介紹之後,我們看到終端裡好像沒發生什麼變化,就跟之前我們看到的終端一樣誒,那screen到底做了什麼呢?
其實,screen為我們開了一個虛擬終端,就是在當前實際的終端裡又開了一個終端。
如果你再執行screen,那麼它又會新開一個虛擬終端。那麼怎麼退出每個新開的虛擬終端呢?可以按Ctrl + D或者用exit命令。
每次你按Ctrl + D或執行exit命令,都會關閉當前所在的虛擬終端,直到最後一個虛擬終端被關閉,screen程式退出,回到我們的實際終端裡,如下圖:
上圖顯示了[screen is terminating],表示所有screen開的虛擬終端都已關閉,screen退出(terminate是英語《終結,停止》的意思。施瓦辛格的電影《終結者》就是《The Terminator》)。
現在我們已經知道如何退出screen了。
我們重新回去吧,再輸入screen就好了。
在screen程式中,幾乎所有的操作都是以Ctrl + a開始的。
以下所有的講解中,英文字母區分大小寫。也就是說:b和B是不同的,前者就是按下鍵盤上的b鍵,B則是需要用Shift + b。
如何操作呢?首先,按下Ctrl + a鍵,然後鬆開Ctrl鍵和a鍵,再按其他鍵來完成一定的操作。
Ctrl + a再加?號:顯示幫助頁面
我們先用Ctrl + a鍵(也就是同時按下Ctrl鍵和a鍵),然後鬆開這兩個鍵,再按下?號(需要Shift + /號)。
screen的幫助頁面就會顯示:
可以看到,幫助頁面顯示了各種操作的實現方法。(好好學英語,很有好處吧,可以參看 對於程式設計師, 為什麼英語比數學更重要? 如何學習)
幫助頁總共有3頁,可以通過第一頁第一行[Screen key bingings,page 1 of 3]知道。目前是在3頁中的第一頁,按空格可以翻到下一頁,按回車退出幫助頁。
那怎麼來閱讀這個幫助頁面呢?
比如說,你想要知道screen的版本號,那就是version,可以看到需要用到v鍵。但是光是按v鍵還不夠,因為我們看到第一頁第二行Command key: ^A ,就是說以下所有的操作,都需要先按下Ctrl + a鍵。^表示Ctrl鍵。
所以說,要知道screen的版本號,可以先按Ctrl + a鍵(也就是同時按下Ctrl鍵和a鍵),然後鬆開這兩個鍵,再按下v鍵。就會在左下角顯示screen的版本號了。
當然了,這個幫助頁面還是不太容易懂,不過我們也不需要全部記住,只要會用常用的一些組合鍵就好了。
常用的組合按鍵
Ctrl + a,鬆開,再按c:建立一個新的虛擬終端。
Ctrl + a,鬆開,再按w:顯示當前虛擬終端的列表。會顯示在左下角,類似下圖:
此處的 0$ bash 1-$ bash 2*$ bash 表示此時開啟了3個虛擬終端,都叫作bash,編號是0,1,2。
有*(星號)的那個虛擬終端就是我們目前所在的虛擬終端,也就是第3個,編號是2。
Ctrl + a,鬆開,再按A:重新命名當前虛擬終端。修改後的名字,你之後再用Ctrl + a,鬆開,再按w時就會看到。
Ctrl + a,鬆開,再按n:跳轉到下一個虛擬終端。
Ctrl + a,鬆開,再按p:跳轉到上一個虛擬終端。
Ctrl + a,鬆開,再按Ctrl + a:跳轉到最近剛使用的那個虛擬終端。
Ctrl + a,鬆開,再按0~9數字鍵:跳轉到第0~9號虛擬終端。
Ctrl + a,鬆開,再按 "(雙引號):會讓你選擇跳轉到哪個虛擬終端。
Ctrl + a,鬆開,再按k:關閉當前終端。
以上是一些常用的screen組合鍵,下面我們重點來看兩個很有用的組合鍵,分別用於分割虛擬終端和分離screen。
Ctrl + a,鬆開,再按S:分割虛擬終端為多個小虛擬終端
注意是大寫的S,所以是Shif + s。如果這樣操作一次,則當前虛擬終端被分割為上下兩部分。如下圖所示:
如果再按這樣操作,就分割成3部分,4部分...
可以看到我在上面的半部分中執行了ls命令,下面的半部分暫時還沒跳轉過去操作,因此下半部分連命令列提示符也沒有,空空的。
那我們如何跳轉到下半部分去操作呢?
Ctrl + a,鬆開,再按Tab鍵。
游標就會跳轉到下半部分了,但是還是沒見有命令列提示符,那是因為還沒為下半部分建立虛擬終端呢。
所以我們可以新建一個:Ctrl + a,鬆開,再按c。或者開啟一個現有的虛擬終端。
可以看到,我們用Ctrl + a,鬆開,再按c之後,下半部分的左下角的--變成了3 bash,說明新建了一個虛擬終端,編號是3,也就是第4個(虛擬終端的編號從0開始)。
我們在這個3 bash的虛擬終端裡執行top命令,如下:
那麼我們如何關閉新分割出來的虛擬終端呢?只要Ctrl + a,鬆開,再按X(是大寫的X,也就是Shift + x)。
Ctrl + a,鬆開,再按d:分離screen
如果我們在screen程式中,先按Ctrl + a,鬆開,再按d,就可以使screen程式與當前實際終端分離了,有點類似nohup命令的作用。
這樣我們就可以重回我們自己的實際終端了,而screen並沒有退出,還在後臺執行。
可以看到 [detached from 6815.pts-0.oscar-laptop]
表示我們的screen與實際終端分離(detach是英語《分離,掙脫》的意思)了。
之後如果你要重回screen中,可以輸入
screen -r複製程式碼
就又回到剛才的screen的虛擬終端裡了。
我們可以使好幾個screen進入分離(detached)狀態。
可以看到,我們繼剛才分離了編號6815的screen程式之後,現在又分離了一個編號13840的screen程式。
之後再執行screen -r想要回去的時候,因為有兩個screen分離程式了,實際終端會詢問你要回到哪一個,如下圖:
你想要回到哪一個就用:
screen -r 編號複製程式碼
就可以了。例如我要回到6815那個screen,只要這樣:
screen -r 6815複製程式碼
如果你在實際終端下,輸入:
screen -ls複製程式碼
則會列出當前開啟著的screen程式:
怎麼樣,screen是不是很有趣呢?好好練習,就會比較熟練了。
用過Emacs或Vim編輯器的讀者,也許覺得screen有點像Emcas或Vim中的一部分功能。
是有點像。不過,screen怎麼能和Emacs及Vim如此強大的編輯器相比呢。
總結
我們可以使程式在後臺執行,成為後臺程式。這樣在當前終端中我們就可以做其他事了,而不必等待此程式執行結束。
為了使一個程式在後臺執行,可以在命令的最後加上&這個符號。但是,如果你關閉終端或退出登入,此後臺程式還是會結束。為了將後臺程式與本終端分離,可以使用nohup命令,使得程式不再受終端關閉或使用者登出的影響。
如果你執行了一個前臺程式,但是想要將其轉為後臺執行程式。你可以先用Ctrl + Z組合按鍵將其轉為後臺暫停,然後執行bg命令使其在後臺重新執行。如果你要將一個後臺命令(不管它是後臺執行還是後臺暫停)重新轉為前臺執行,只要用fg命令就可以了。
screen是一個程式,你可以用apt-get來安裝。screen命令使使用者能夠在一個終端中開啟多個虛擬終端。
第三部分第五課預告
今天的課就到這裡,一起加油吧!
下一課我們學習:Linux探索之旅 | 第三部分第五課:延時執行,唯慢不破
微信公眾號「程式設計師聯盟」ProgrammerLeague
我是謝恩銘,在巴黎奮鬥的軟體工程師。
我的簡介
我的經歷
熱愛生活,喜歡游泳,略懂烹飪。
人生格言:“向著標杆直跑”