在ssh、telnet斷開之後繼續執行程式
你是不是經常需要 SSH 或者 telent 遠端登入到 Linux 伺服器?你是不是經常為一些長時間運作的任務而頭疼,比如系統備份、ftp 傳輸等等。通常情況下我們都是為每一個這樣的任務開一個遠端終端視窗,因為他們執行的時間太長了。必須等待它執行完畢,在此期間可不能關掉視窗或者斷開連接配接,否則這個任務就會被殺掉,一切半途而廢了。
元兇:SIGHUP 信号 讓我們來看看為什麼關掉視窗/斷開連接配接會使得正在運作的程式死掉。 在Linux/Unix中,有這樣幾個概念: 程序組(process group):一個或多個程序的集合,每一個程序組有唯一一個程序組ID,即程序組長程序的ID。
會話期(session):一個或多個程序組的集合,有唯一一個會話期首程序(session leader)。會話期ID為首程序的ID。
會話期可以有一個單獨的控制終端(controlling terminal)。與控制終端連接配接的會話期首程序叫做控制程序(controlling process)。目前與終端互動的程序稱為前台程序組。其餘程序組稱為背景程序組。
根據POSIX.1定義: 挂斷信号(SIGHUP)預設的動作是終止程式。
當終端接口檢測到網絡連接配接斷開,将挂斷信号發送給控制程序(會話期首程序)。
如果會話期首程序終止,則該信号發送到該會話期前台程序組。
一個程序退出導緻一個孤兒程序組中産生時,如果任意一個孤兒程序組程序處于STOP狀态,發送SIGHUP和SIGCONT信号到該程序組中所有程序。
是以當網絡斷開或終端視窗關閉後,控制程序收到SIGHUP信号退出,會導緻該會話期内其他程序退出。 我們來看一個例子。打開兩個SSH終端視窗,在其中一個運作top指令。 [[email protected] root]# top 在另一個終端視窗,找到top的程序ID為5180,其父程序ID為5128,即登入shell。 [[email protected] root]# ps -ef|grep top root 5180 5128 0 01:03 pts/0 00:00:02 top root 5857 3672 0 01:12 pts/2 00:00:00 grep top 使用pstree指令可以更清楚地看到這個關系: [[email protected] root]# pstree -H 5180|grep top |-sshd-+-sshd---bash---top 使用ps-xj指令可以看到,登入shell(PID 5128)和top在同一個會話期,shell為會話期首程序,所在程序組PGID為5128,top所在程序組PGID為5180,為前台程序組。 [[email protected] root]# ps -xj|grep 5128 5126 5128 5128 5128 pts/0 5180 S 0 0:00 -bash 5128 5180 5180 5128 pts/0 5180 S 0 0:50 top 3672 18095 18094 3672 pts/2 18094 S 0 0:00 grep 5128 關閉第一個SSH視窗,在另一個視窗中可以看到top也被殺掉了。 [[email protected] root]# ps -ef|grep 5128 root 18699 3672 0 04:35 pts/2 00:00:00 grep 5128 ---------------------------------------------------------------------------- Unix/Linux下一般想讓某個程式在背景運作,很多都是使用 & 在程式結尾來讓程式自動運作。比如我們要運作mysql 在背景 /usr/local/mysql/bin/mysqld_safe –user=mysql & 但是我們很多程式并不象mysqld一樣可以做成守護程序 ,可能我們的程式隻是普通程式而已,一般這種程式即使使用 & 結尾,如果終端關閉,那麼程式也會被關閉。為了能夠背景運作,我們可以忽略SIGHUP信号,關掉視窗應該就不會影響程式的運作了。nohup指令可以達到這個目的,如果程式的标準輸出/标準錯誤是終端,nohup預設将其重定向到nohup.out檔案。值得注意的是nohup指令隻是使得程式忽略SIGHUP信号,還需要使用标記& 把它放在背景運作。我們需要使用nohup這個指令,比如我們有個start.sh需要在背景運作,并且希望在背景能夠一直運作,那麼就使用nohup:文法格式為 nohup <command> [argument...] & 如 nohup /root/start.sh 在shell中回車後提示: [~]$ appending output to nohup.out 原程式的的标準輸出被自動改向到目前目錄下的nohup.out檔案,起到了log的作用。 但是有時候在這一步會有問題,當把終端關閉後,程序會自動被關閉,察看nohup.out可以看到在關閉終端瞬間服務自動關閉。 咨詢紅旗Linux工程師後,他也不得其解,在我的終端上執行後,他啟動的程序竟然在關閉終端後依然運作。 在第二遍給我示範時,我才發現我和他操作終端時的一個細節不同:他是在當shell中提示了nohup成功後還需要按終端上鍵盤任意鍵退回到 shell輸入指令視窗,然後通過在shell中輸入exit來退出終端;而我是每次在nohup執行成功後直接點關閉程式按鈕關閉終端.。是以這時候會斷掉該指令所對應的session,導緻nohup對應的程序被通知需要一起shutdown。 這個細節有人和我一樣沒注意到,是以在這兒記錄一下了。 附:nohup指令參考 nohup 指令 用途:不挂斷地運作指令。 文法:nohup Command [ Arg … ] [ & ] 描述:nohup 指令運作由 Command 參數和任何相關的 Arg 參數指定的指令,忽略所有挂斷(SIGHUP)信号。在登出後使用 nohup 指令運作背景中的程式。要運作背景中的 nohup 指令,添加 & ( 表示”and”的符号)到指令的尾部。 無論是否将 nohup 指令的輸出重定向到終端,輸出都将附加到目前目錄的 nohup.out 檔案中。如果目前目錄的 nohup.out 檔案不可寫,輸出重定向到 $HOME/nohup.out 檔案中。如果沒有檔案能建立或打開以用于追加,那麼 Command 參數指定的指令不可調用。如果标準錯誤是一個終端,那麼把指定的指令寫給标準錯誤的所有輸出作為标準輸出重定向到相同的檔案描述符。 退出狀态:該指令傳回下列出口值: 126 可以查找但不能調用 Command 參數指定的指令。 127 nohup 指令發生錯誤或不能查找由 Command 參數指定的指令。 否則,nohup 指令的退出狀态是 Command 參數指定指令的退出狀态。 nohup指令及其輸出檔案 nohup指令:如果你正在運作一個程序,而且你覺得在退出帳戶時該程序還不會結束,那麼可以使用nohup指令。該指令可以在你退出帳戶/關閉終端之後繼續運作相應的程序。nohup就是不挂起的意思( n ohang up)。 該指令的一般形式為:nohup command & 使用nohup指令送出作業 如果使用nohup指令送出作業,那麼在預設情況下該作業的所有輸出都被重定向到一個名為nohup.out的檔案中,除非另外指定了輸出檔案: nohup command > myout.file 2>&1 & 在上面的例子中,輸出被重定向到myout.file檔案中。 使用 jobs 檢視任務。 使用 fg %n 關閉。 另外有兩個常用的ftp 工具ncftpget和ncftpput,可以實作背景的ftp上傳和下載下傳,這樣就可以利用這些指令在背景上傳和下載下傳檔案了。 ----------------------------------------------------------------------------
雖然nohup很容易使用,但還是比較”簡陋”的,對于簡單的指令能夠應付過來,對于複雜的需要人機互動的任務就麻煩了。 其實我們可以使用一個更為強大的實用程式screen。流行的Linux發行版(例如Red Hat Enterprise Linux 4)通常會自帶screen實用程式,如果沒有的話,可以從GNU screen的官方網站下載下傳。 [[email protected] ~]# rpm -qa|grep screen xscreensaver-4.18-5.rhel4.11 screen-4.0.2-5 開始使用Screen 簡單來說,Screen是一個可以在多個程序之間多路複用一個實體終端的視窗管理器。Screen中有會話的概念,使用者可以在一個screen會話中建立多個screen視窗,在每一個screen視窗中就像操作一個真實的telnet/SSH連接配接視窗那樣。在screen中建立一個新的視窗有這樣幾種方式: 1.直接在指令行鍵入screen指令 [[email protected] ~]# screen Screen将建立一個執行shell的全屏視窗。你可以執行任意shell程式,就像在ssh視窗中那樣。在該視窗中鍵入exit退出該視窗,如果這是該screen會話的唯一視窗,該screen會話退出,否則screen自動切換到前一個視窗。 2.Screen指令後跟你要執行的程式。 [[email protected] ~]# screen vi test.c Screen建立一個執行vi test.c的單視窗會話,退出vi将退出該視窗/會話。 3.以上兩種方式都建立新的screen會話。我們還可以在一個已有screen會話中建立新的視窗。在目前screen視窗中鍵入C-a c ,即Ctrl鍵+a鍵,之後再按下c鍵,screen 在該會話内生成一個新的視窗并切換到該視窗。 screen還有更進階的功能。你可以不中斷screen視窗中程式的運作而暫時斷開(detach)screen會話,并在随後時間重新連接配接(attach)該會話,重新控制各視窗中運作的程式。例如,我們打開一個screen視窗編輯/tmp/abc檔案: [[email protected] ~]# screen vi /tmp/abc 之後我們想暫時退出做點别的事情,比如出去散散步,那麼在screen視窗鍵入C-a d (直接斷開連接配接也可以的),Screen會給出detached提示:
暫時中斷會話
半個小時之後回來了,找到該screen會話: [[email protected] ~]# screen -ls There is a screen on: 16582.pts-1.tivf06 (Detached) 1 Socket in /tmp/screens/S-root. 重新連接配接會話: [[email protected] ~]# screen -r 16582 看看出現什麼了,太棒了,一切都在。繼續幹吧。 你可能注意到給screen發送指令使用了特殊的鍵組合C-a。這是因為我們在鍵盤上鍵入的資訊是直接發送給目前screen視窗,必須用其他方式向screen視窗管理器發出指令,預設情況下,screen接收以C-a開始的指令。這種指令形式在screen中叫做鍵綁定(key binding),C-a叫做指令字元(command character)。 可以通過C-a ? 來檢視所有的鍵綁定,常用的鍵綁定有: C-a ? 顯示所有鍵綁定資訊
C-a w 顯示所有視窗清單
C-a C-a 切換到之前顯示的視窗
C-a c 建立一個新的運作shell的視窗并切換到該視窗
C-a n 切換到下一個視窗
C-a p 切換到前一個視窗(與C-a n相對)
C-a 0..9 切換到視窗0..9
C-a a 發送 C-a到目前視窗
C-a d 暫時斷開screen會話
C-a k 殺掉目前視窗
C-a [ 進入拷貝/復原模式 Screen常用選項 使用鍵綁定C-a ?指令可以看到, 預設的指令字元(Command key)為C-a,轉義C-a(literal ^a)的字元為a:
Screen 常用選項
因為screen把C-a看作是screen指令的開始,是以如果你想要screen視窗接收到C-a字元,就要輸入C-a a。Screen也允許你使用-e選項設定自己的指令字元和轉義字元,其格式為: -exy x為指令字元,y為轉義指令字元的字元 下面指令啟動的screen會話指定了指令字元為C-t,轉義C-t的字元為t,通過C-t ?指令可以看到該變化。
自定義指令字元和轉義字元
[[email protected] root]# screen -e^tt 其他常用的指令選項有: -c file 使用配置檔案file,而不使用預設的$HOME/.screenrc
-d|-D [pid.tty.host] 不開啟新的screen會話,而是斷開其他正在運作的screen會話
-h num 指定曆史復原緩沖區大小為num行
-list|-ls 列出現有screen會話,格式為pid.tty.host
-d -m 啟動一個開始就處于斷開模式的會話
-r sessionowner/ [pid.tty.host] 重新連接配接一個斷開的會話。多使用者模式下連接配接到其他使用者screen會話需要指定sessionowner,需要setuid-root權限
-S sessionname 建立screen會話時為會話指定一個名字
-v 顯示screen版本資訊
-wipe [match] 同-list,但删掉那些無法連接配接的會話 下例顯示目前有兩個處于detached狀态的screen會話,你可以使用screen -r <screen_pid>重新連接配接上: [[email protected] root]# screen -ls There are screens on: 8736.pts-1.tivf18 (Detached) 8462.pts-0.tivf18 (Detached) 2 Sockets in /root/.screen. [[email protected] root]# screen -r 8736 如果由于某種原因其中一個會話死掉了(例如人為殺掉該會話),這時screen -list會顯示該會話為dead狀态。使用screen -wipe指令清除該會話: [[email protected] root]# kill -9 8462 [[email protected] root]# screen -ls There are screens on: 8736.pts-1.tivf18 (Detached) 8462.pts-0.tivf18 (Dead ???) Remove dead screens with 'screen -wipe'. 2 Sockets in /root/.screen. [[email protected] root]# screen -wipe There are screens on: 8736.pts-1.tivf18 (Detached) 8462.pts-0.tivf18 (Removed) 1 socket wiped out. 1 Socket in /root/.screen. [[email protected] root]# screen -ls There is a screen on: 8736.pts-1.tivf18 (Detached) 1 Socket in /root/.screen. [[email protected] root]# -d -m 選項是一對很有意思的搭檔。他們啟動一個開始就處于斷開模式的會話。你可以在随後需要的時候連接配接上該會話。有時候這是一個很有用的功能,比如我們可以使用它調試背景程式。該選項一個更常用的搭配是:-dmS sessionname 啟動一個初始狀态斷開的screen會話: [[email protected] tianq]# screen -dmS mygdb gdb execlp_test 連接配接該會話: [[email protected] tianq]# screen -r mygdb 管理你的遠端會話 先來看看如何使用screen解決SIGHUP問題,比如現在我們要ftp傳輸一個大檔案。如果按老的辦法,SSH登入到系統,直接ftp指令開始傳輸,之後。。如果網絡速度還可以,恭喜你,不用等太長時間了;如果網絡不好,老老實實等着吧,隻能傳輸完畢再斷開SSH連接配接了。讓我們使用screen 來試試。 SSH登入到系統,在指令行鍵入screen。 [[email protected] root]# screen 在screen shell視窗中輸入ftp指令,登入,開始傳輸。不願意等了?OK,在視窗中鍵入C-a d:
管理你的遠端會話
然後。。退出SSH登入?随你怎樣,隻要别殺掉screen會話。 是不是很友善?更進一步,其實我們可以利用screen這種功能來管理你的遠端會話,儲存你所有的工作内容。你是不是每次登入到系統都要開很多視窗,然後每天都要重複打開關閉這些視窗?讓screen來幫你"儲存"吧,你隻需要打開一個ssh視窗,建立需要的screen視窗,退出的時候C-a d"儲存"你的工作,下次登入後直接screen -r <screen_pid>就可以了。 最好能給每個視窗起一個名字,這樣好記些。使用C-a A給視窗起名字。使用C-a w可以看到這些視窗名字,可能名字出現的位置不同。使用putty:
putty
使用telnet:
telnet
更多Screen功能 Screen提供了豐富強大的定制功能。你可以在Screen的預設兩級配置檔案/etc/screenrc和$HOME/.screenrc中指定更多,例如設定screen選項,定制綁定鍵,設定screen會話自啟動視窗,啟用多使用者模式,定制使用者通路權限控制等等。如果你願意的話,也可以自己指定screen配置檔案。 以多使用者功能為例,screen預設是以單使用者模式運作的,你需要在配置檔案中指定multiuser on 來打開多使用者模式,通過acl*(acladd,acldel,aclchg...)指令,你可以靈活配置其他使用者通路你的screen會話。更多配置檔案内容請參考screen的man頁。 ------------------------------------- 實際應用中碰到的問題 [[email protected] ~]# screen -ls
There is a screen on:
4083.down_video (Detached)
1 Socket in /var/run/screen/S-root. 狀态顯示 down_video這個screen session已經被 斷開(detach) 此時我們用screen -r可以取回進行, 說明: -r :取回一個已被斷開的screen session 例: [[email protected] ~]# screen -r down_video
可以看到我們的程式仍在運作,中間未曾停止,而且此時我們又可以控制它了。
那麼如何中斷一個 screen session?
看例子: [[email protected] ~]# screen -ls
There is a screen on:
4083.down_video (Attached)
1 Socket in /var/run/screen/S-root. 請注意: 目前看到的session : down_video 正在被連接配接使用中,
這時我想使用此session [[email protected] ~]# screen -r down_video
There is a screen on:
4083.down_video (Attached)
There is no screen to be resumed matching down_video. 此時用 -r 不能取到此 screen session
這時隻能用 -d 參數來強制中斷它
[[email protected] ~]# screen -d down_video
[4083.down_video detached.] [[email protected] ~]# screen -r down_video
再用 -r 取回 screen就成功了
注意:原來的被我們中斷的連接配接會顯示 :
[[email protected] ~]# screen -r down_video
[remote detached]