1. 程式人生 > >解決Linux關閉終端(關閉SSH等)後執行的程式或者服務自動停止【後臺執行程式】

解決Linux關閉終端(關閉SSH等)後執行的程式或者服務自動停止【後臺執行程式】

 問題描述:當SSH遠端連線到伺服器上,然後執行一個服務 ./catalina.sh start,然後把終端開閉(切斷SSH連線)之後,發現該服務中斷,導致網頁無法訪問。

解決方法:使用nohup命令讓程式在關閉視窗(切換SSH連線)的時候程式還能繼續在後臺執行。

Unix/Linux下一般比如想讓某個程式在後臺執行,很多都是使用& 在程式結尾來讓程式自動執行。比如我們要執行mysql在後臺:

/usr/local/mysql/bin/mysqld_safe --user=mysql &

但是加入我們很多程式並不象mysqld一樣做成守護程序,可能我們的程式只是普通程式而已,一般這種程式使用& 結尾,但是如果終端關閉,那麼程式也會被關閉。但是為了能夠後臺執行,那麼我們就可以使用nohup這個命令,比如我們有個test.php需要在後臺執行,並且希望在後臺能夠定期執行,那麼就使用nohup:

       nohup /root/test.php &

  提示:

  [~]$ appending output to nohup.out

  嗯,證明執行成功,同時把程式執行的輸出資訊放到當前目錄的nohup.out 檔案中去。

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上傳和下載,這樣我就可以利用這些命令在後臺上傳和下載檔案了。

思考:問題1為什麼ssh一關閉,程式就不再運行了?

元凶:SIGHUP 訊號
讓我們來看看為什麼關掉視窗/斷開連線會使得正在執行的程式死掉。

在Linux/Unix中,有這樣幾個概念:
程序組(process group):一個或多個程序的集合,每一個程序組有唯一一個程序組ID,即程序組長程序的ID。
會話期(session):一個或多個程序組的集合,有唯一一個會話期首程序(session leader)。會話期ID為首程序的ID。
會話期可以有一個單獨的控制終端(controlling terminal)。與控制終端連線的會話期首程序叫做控制程序(controlling process)。當前與終端互動的程序稱為前臺程序組。其餘程序組稱為後臺程序組。
根據POSIX.1定義:
結束通話訊號(SIGHUP)預設的動作是終止程式。
當終端介面檢測到網路連線斷開,將結束通話訊號傳送給控制程序(會話期首程序)。
如果會話期首程序終止,則該訊號傳送到該會話期前臺程序組。
一個程序退出導致一個孤兒程序組中產生時,如果任意一個孤兒程序組程序處於STOP狀態,傳送SIGHUP和SIGCONT訊號到該程序組中所有程序。(關於孤兒程序參照:http://blog.csdn.net/hmsiwtv/article/details/7901711 )
結論:因此當網路斷開或終端視窗關閉後,也就是SSH斷開以後,控制程序收到SIGHUP訊號退出,會導致該會話期內其他程序退出。

簡而言之:就是ssh 開啟以後,bash等都是他的子程式,一旦ssh關閉,系統將所有相關程序殺掉!! 導致一旦ssh關閉,執行中的任務就取消了

例子:
我們來看一個例子。開啟兩個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

問題2   為什麼守護程式就算ssh 開啟的,就算關閉ssh也不會影響其執行?
因為他們的程式特殊,比如httpd –k start執行這個以後,他不屬於sshd這個程序組  而是單獨的程序組,所以就算關閉了ssh,和他也沒有任何關係!
[[email protected] ~]# pstree |grep http
     |-httpd
[[email protected] ~]# pstree |grep top
     |-sshd-+-sshd---bash---top


結論:守護程序的啟動命令本身就是特殊的,和一般命令不同的,比如mysqld_safe 這樣的命令 一旦使用了  就是守護程序執行。所以想把一般程式改造為守護程式是不可能,

問題3 使用後臺執行命令&  能否將程式擺脫ssh程序組控制呢  也就是ssh關閉,後臺程式繼續執行?
我們做一個試驗:  find / -name ‘*http*’&
利用ctrl+d 登出以後 再進入系統  會不會看見這個命令再執行?
答案是  :命令被中止了!!

因為他依然屬於這個ssh程序組 就算加了&也無法擺脫!!
[[email protected] ~]# pstree |grep find
     |-sshd-+-sshd---bash---find

結論就是:只要是ssh 開啟執行的一般命令,不是守護程式,無論加不加&,一旦關閉ssh,系統就會用SIGHUP終止

問題4  nohup能解決的問題
但是為了能夠再登出以後 依然能後臺執行,那麼我們就可以使用nohup這個命令,我們現在開始查詢find / -name ‘*http*’&
,並且希望在後臺執行,
那麼就使用nohup:nohup find / -name "*httpd*"
此時預設地程式執行的輸出資訊放到當前資料夾的nohup.out 檔案中去
加不加&並不會影響這個命令   只是讓程式 前臺或者後臺執行而已

延伸:Linux命令nohup+screen命令

如果想在關閉ssh連線後剛才啟動的程式繼續執行怎麼辦,可以使用nohup。但是如果要求第二天來的時候,一開ssh,還能檢視到昨天執行的程式的狀態,然後繼續工作,這時nohup是不行了,需要使用screen來達到這個目的。



雖然nohup很容易使用,但還是比較“簡陋”的,對於簡單的命令能夠應付過來,對於複雜的需要人機互動的任務就麻煩了。
其實我們可以使用一個更為強大的實用程式screen。流行的Linux發行版(例如Red Hat Enterprise Linux 4)通常會自帶screen實用程式,如果沒有的話,可以從GNU screen的官方網站下載。

1)使用
執行screen , 按任意鍵進入子介面;
我用ping命令開始執行,如果下班了,但是想關閉ssh以後ping繼續執行,那麼按ctrl+a   再按d   這樣暫停了子介面,會顯示[detached]的字樣,這時候 我回到了父介面;
用screen –ls檢視目前子介面的狀態screen -ls
There is a screen on: 22292.pts-3.free (Detached)
1 Socket in /tmp/screens/S-root,這裡的22292其實是子介面的pid號;

如果回到子介面 用screen –r 22292,一下子彈到了ping 的子介面;

2)更多幫助
可以通過C-a(ctrl+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 [
進入拷貝/回滾模式

其他常用選項:

-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,但刪掉那些無法連線的會話