來思考幾種場景:
1、某個腳本需要執行時間比較長,無人值守,可能執行過程中因ssh會話超時而中斷?
2、某次測試一段代碼,需要臨時放入后台運行?
3、放入后台運行的腳本,需要在一段時間后重新調到前台?
4、已經調起作用后,忽然發現沒有將作業放入后台執行,如何補救?
5、需要在后台運行大量的腳本,如何管理?
一、問題分析與思路
當終端關閉或網絡斷開時,當前終端中運行的進程就會收到SIGHUP(終止信號),終端關閉,終端進程之下的所有子進程也會關閉。
為此,我們可以考慮:
(1)有沒有方法讓運行中的進程不再收到SIGHUP信號
(2)有沒有方法讓運行中的進程獨立存在,不屬於當前終端的子進程
二、shell/命令/作業后台執行方法
1、nohup
nohup的用途就是讓提交的命令忽略hangup信號。語法如下:
# nohup --help
用法:nohup 命令 [參數]...
或:nohup 選項
忽略掛起信號運行指定的命令。
(1)使用
nohup的使用比較簡單,只需在要處理的命令前加上 nohup 即可,標准輸出和標准錯誤缺省會被重定向到nohup.out文件中。
使用時,常見的有以下幾種方式:
(1)一般我們可在結尾加上"&"來將命令同時放入后台運行;
(2)">filename 2>&1"來更改缺省的重定向文件名;
(3)直接">/dev/null 2>&1 &"重定向到/dev/null,不記錄日志;
注意:
(1)如果不將 nohup 命令的輸出重定向,輸出將附加到當前目錄的 nohup.out 文件中。
(2)如果當前目錄的 nohup.out 文件不可寫,輸出重定向到 $HOME/nohup.out 文件中。
(3)如果沒有文件能創建或打開以用於追加,那么 COMMAND參數指定的命令不可調用。
(4)如果標准錯誤是一個終端,那么把指定的命令寫給標准錯誤的所有輸出作為標准輸出重定向到相同的文件描述符。
(2)示例
示例1:直接放入后台運行
[root@vnx ~]# cat a.sh ping www.baidu.com [root@vnx ~]# nohup sh a.sh & ##直接放入后台運行 [1] 31159 ##進程號 [root@vnx ~]# nohup: 忽略輸入並把輸出追加到"nohup.out" [root@vnx ~]# ps -ef | grep 31159 root 31159 31051 0 22:49 pts/0 00:00:00 sh a.sh root 31160 31159 0 22:49 pts/0 00:00:00 ping www.baidu.com root 31280 31134 0 23:06 pts/1 00:00:00 grep 31159 [root@vnx ~]# ps -ef | grep ping root 31160 31159 0 22:49 pts/0 00:00:00 ping www.baidu.com root 31282 31134 0 23:06 pts/1 00:00:00 grep ping [root@vnx ~]# kill -9 31159 [root@vnx ~]# ps aux | grep 31159 root 31331 0.0 0.0 103256 848 pts/1 S+ 23:11 0:00 grep 31159 [root@vnx ~]# ps -ef | grep ping root 31160 1 0 22:49 pts/0 00:00:00 ping www.baidu.com root 31333 31134 0 23:11 pts/1 00:00:00 grep ping [root@vnx ~]# tail -f nohup.out 64 bytes from 61.135.169.121: icmp_seq=976 ttl=128 time=1.56 ms 64 bytes from 61.135.169.121: icmp_seq=977 ttl=128 time=2.02 ms
示例2:重定向到文件
[root@vnx ~]# nohup sh a.sh >tmp.log 2>&1 & [1] 31392 [root@vnx ~]# ps -ef | grep 31392 root 31392 31134 0 23:21 pts/1 00:00:00 sh a.sh root 31393 31392 0 23:21 pts/1 00:00:00 ping www.baidu.com root 31395 31134 0 23:21 pts/1 00:00:00 grep 31392 [root@vnx ~]# jobs ##查看作業,jobs可以查看nohup提交到后台的作業 [1]+ Running nohup sh a.sh > tmp.log 2>&1 & [root@vnx ~]# fg %1 #將jobs查到的作業調入前台 nohup sh a.sh > tmp.log 2>&1
不記錄日志方式與此類似,只是將/dev/null作為重定向的目標。
說明:
(1)ctrl + z:可以將一個正在前台執行的命令放到后台,並且暫停 (2)jobs:查看當前有多少在后台運行的命令 jobs 列出當前shell環境中已啟動的任務狀態,若未指定jobsid,則顯示所有活動的任務狀態信息;如果報告了一個任務的終止(即任務的狀態被標記為Terminated),shell 從當前的shell環境已知的列表中刪除任務的進程標識 (3)fg:將后台中的命令調至前台繼續運行 如果后台中有多個命令,可以用 fg %jobnumber將選中的命令調出,%jobnumber是通過jobs命令查到的后台正在執行的命令的序號(不是pid) (4)bg:將一個在后台暫停的命令,變成繼續執行 如果后台中有多個命令,可以用bg %jobnumber將選中的命令調出,%jobnumber是通過jobs命令查到的后台正在執行的命令的序號(不是pid)
2、setid
setsid能新建一個session,語法如下:
NAME setsid - run a program in a new session SYNOPSIS setsid program [arg...]
示例
[root@vnx ~]# setsid ping www.baidu.com [root@vnx ~]# PING www.a.shifen.com (220.181.111.188) 56(84) bytes of data. 64 bytes from 220.181.111.188: icmp_seq=1 ttl=128 time=3.63 ms 64 bytes from 220.181.111.188: icmp_seq=2 ttl=128 time=4.12 ms 64 bytes from 220.181.111.188: icmp_seq=3 ttl=128 time=4.18 ms [root@vnx ~]# ps -ef |grep ping root 31758 1 0 23:55 ? 00:00:00 ping www.baidu.com ##注意與nohup的區分,這里的父進程號是1(init進程) root 31760 31727 0 23:55 pts/1 00:00:00 grep ping [root@vnx ~]# jobs #這里使用jobs是無法查到相關的作業信息的
注意:setsid與nohup的區分,這里的進程 ID(PID)為31758,而它的父ID(PPID)為1(即為 init進程ID)並不是當前終端的進程ID
說明:當新建一個session后,相當創建了init進程的一個子進程,與當前終端的session便不再關聯,自然,關閉終端,也不會對其產生影響。
3、()和&
我們知道在Shell中有這樣一個知識點:
(cmd1;cmd2;cmd3)
命令組:在括號中的命令列表,將會作為一個子shell來運行。
(注:在括號中的變量,由於是在子shell中,所以對於腳本剩下的部分是不可用的。父進程,也就是腳本本身,將不能夠讀取在子進程中創建的變量, 也就是在子shell中創建的變量。)
示例:
[root@vnx ~]# (ping www.baidu.com ) ##會話1 PING www.a.shifen.com (220.181.111.188) 56(84) bytes of data. 64 bytes from 220.181.111.188: icmp_seq=1 ttl=128 time=5.11 ms 64 bytes from 220.181.111.188: icmp_seq=2 ttl=128 time=3.94 ms ^C --- www.a.shifen.com ping statistics --- 19 packets transmitted, 19 received, 0% packet loss, time 18817ms rtt min/avg/max/mdev = 3.408/4.026/5.480/0.539 ms [root@vnx ~]# ##直接用()包圍命令,在會話退出后子命令也是會退出的 [root@vnx ~]# ps -ef | grep www.baidu.com ##會話2 root 54495 31051 0 14:38 pts/0 00:00:00 ping www.baidu.com ##發現子命令的父命令進程是31051 root 54497 31727 0 14:39 pts/1 00:00:00 grep www.baidu.com [root@vnx ~]# jobs ##jobs作業列表里面沒有作業與之對應
[root@vnx ~]# [root@vnx ~]# ps -ef | grep www.baidu.com ##會話1中終止后,會話2中就看不到相關子命令的進程了 root 54514 31727 0 14:40 pts/1 00:00:00 grep www.baidu.com
上面我們知道,&可以將命令放入后台執行,我們嘗試將&也放入括號里面:
[root@vnx ~]# (ping www.baidu.com &) ##會話1 [root@vnx ~]# PING www.a.shifen.com (61.135.169.125) 56(84) bytes of data. 64 bytes from 61.135.169.125: icmp_seq=1 ttl=128 time=6.73 ms 64 bytes from 61.135.169.125: icmp_seq=2 ttl=128 time=9.75 ms ^C ##Ctrl + C不能終止子命令 [root@vnx ~]# 64 bytes from 61.135.169.125: icmp_seq=3 ttl=128 time=3.17 ms 64 bytes from 61.135.169.125: icmp_seq=4 ttl=128 time=3.44 ms 64 bytes from 61.135.169.125: icmp_seq=5 ttl=128 time=1.88 ms [root@vnx ~]# ps -ef | grep www.baidu.com ##查看該命令的進程,發現其父進程是1,即init初始進程,故當前終端的hub信號是無法影響到它的 root 54535 1 0 14:44 pts/0 00:00:00 ping www.baidu.com root 54537 31727 0 14:45 pts/1 00:00:00 grep www.baidu.com [root@vnx ~]# jobs ##jobs作業列表里面是查詢不到的
[root@vnx ~]#
從上面我們可以看到:(cmd1;cmd2;cmd3 &)也可以將命令放入后台執行,效果與setsid類似。
4、disown
若某次操作中,作業已經提交后發現沒有放入后台執行,如何補救才能避免被hup信號影響呢?
這種情況下,使用setsid、()+&是為時已晚的,只能使用作業調度和disown來解決這個問題。disown命令的語法如下:
disown jobID
disown jobID1 jobID2 ... jobIDN
disown [options] jobID1 jobID2 ... jobIDN
disown的可選參數如下:
-a Delete all jobs if jobID is not supplied. -h Mark each jobID so that SIGHUP is not sent to the job if the shell receives a SIGHUP.(使某個作業忽略hup信號) -r Delete only running jobs.
man手冊說明如下:
disown [-ar] [-h] [jobspec ...] Without options, each jobspec is removed from the table of active jobs. If jobspec is not present, and neither -a nor -r is supplied, the shell’s notion of the current job is used. If the -h option is given, each jobspec is not removed from the table, but is marked so that SIGHUP is not sent to the job if the
shell receives a SIGHUP. If no jobspec is present, and neither the -a nor the -r option is supplied, the current job is used. If no jobspec is supplied,the -a option means to remove or mark all jobs;the -r option without a jobspec argument restricts operation to running jobs. The return value is 0 unless a jobspec does not specify a valid job.
下面通過例子進行說明:
(1)沒有任何選項的情況下,移除不活動作業
(2)所有的作業都忽略hup信號
[root@vnx ~]# jobs -l [1]+ 73743 Running nohup ping www.baidu.com & [root@vnx ~]# [root@vnx ~]# disown -a [root@vnx ~]# jobs -l
(3)只有正在運行的作業忽略hup信號
[root@vnx ~]# jobs -l [1]- 83039 Running nohup ping www.baidu.com & [2]+ 83187 停止 ping www.baidu.com [root@vnx ~]# disown -r [root@vnx ~]# jobs -l [2] 83187 停止 ping www.baidu.com
(4)當退出會話時,如何保持作業在后台繼續運行
方式一:指定作業ID
# disown -h jobID
# disown -h %n ##n為作業號,%%代表當前job
方式二:不指定作業ID
## Step 1: update system ## apt-get upgrade &> /root/system.update.log & ## Step 2: Mark apt-get so that SIGHUP is not sent when you exit and go for tea ## disown -h ## Step 3: exit from root shell ## exit
(5)組合使用方式
用disown -h jobspec來使某個作業忽略HUP信號 用disown -ah 來使所有的作業都忽略HUP信號 用disown -rh 來使正在運行的作業忽略HUP信號
注意:(1)當使用過 disown 之后,會將把目標作業從作業列表中移除,我們將不能再使用jobs來查看它,但是依然能夠用ps -ef查找到它。
(2)這種方法的操作對象是作業,如果我們在運行命令時在結尾加了"&"來使它成為一個作業並在后台運行,那么就萬事大吉了,我們可以通過jobs命令來得到所有作業的列表。但是如果並沒有把當前命令作為作業來運行,如何才能得到它的作業號呢?答案就是用 CTRL-z(按住Ctrl鍵的同時按住z鍵)了!
CTRL-z 的用途就是將當前進程掛起(Suspend),然后我們就可以用jobs命令來查詢它的作業號,再用bg jobspec來將它放入后台並繼續運行。需要注意的是,如果掛起會影響當前進程的運行結果,請慎用此方法。示例如下:
(6)若提交命令時,已經使用&建命令放入后台執行,則可直接使用disown:
[root@vnx ~]# cp -r /opt/hadoop-2.7.3 /root/ & [1] 62042 [root@vnx ~]# jobs [1]+ Running cp -i -r /opt/hadoop-2.7.3 /root/ & [root@vnx ~]# disown -h %1 [root@vnx ~]# jobs [1]+ Running cp -i -r /opt/hadoop-2.7.3 /root/ & [root@vnx ~]# ps -ef | grep hadoop root 62042 46781 10 18:51 pts/0 00:00:01 cp -i -r /opt/hadoop-2.7.3 /root/ root 62140 46781 0 18:51 pts/0 00:00:00 grep hadoop
(7)若提交命令時,未將命令放入后台執行,則先用ctrl + z和bg將命令放入后台執行,然后使用disown:
[root@vnx ~]# cp -r /opt/hadoop-2.7.3/ /root/ ^Z [1]+ Stopped cp -i -r /opt/hadoop-2.7.3/ /root/ [root@vnx ~]# bg %1 [1]+ cp -i -r /opt/hadoop-2.7.3/ /root/ & [root@vnx ~]# jobs [1]+ Running cp -i -r /opt/hadoop-2.7.3/ /root/ & [root@vnx ~]# disown -h %1 [root@vnx ~]# jobs [1]+ Running cp -i -r /opt/hadoop-2.7.3/ /root/ & [root@vnx ~]# ps -ef | grep hadoop root 63368 58621 6 18:55 pts/3 00:00:01 cp -i -r /opt/hadoop-2.7.3/ /root/ root 63514 58621 0 18:55 pts/3 00:00:00 grep hadoop
5、screen
screen是建立一個新的全屏虛擬會話終端,這個會話只有在手動輸入exit的時候才會退出,在這個會話里執行的命令不用擔心HUP信號會對我們的進程造成影響,因此也不用給每個命令前都加上“nohup”或“setsid”了,非常適合我們有規划的執行大量的后台任務,可以非常方便的讓我們對這些后台任務進行管理。
使用方法:
screen ##立即創建並進入一個會話。 screen -dmS {name} ##建立一個處於掛起模式下的會話,並根據我們的需要指定其會話名稱。 screen -dmS {name} {script} ##在建立會話時同時執行指定的命令或腳本 screen -list ##列出所有會話。 screen -r {name} ##以獨占方式進入指定會話。 screen -x {name} ##以並行方式進入指定會話。 ctrl +ad ##輸入快捷鍵ctrl +a和d,可暫時退出當前會話。 exit ##進入指定會話后執行exit即可關閉該會話。
注:若有興趣,可嘗試用pstree命令查看一下使用screen過程中的進程樹,這樣會更容易理解!!!
通過以上幾種說明,可總結幾種情況的使用情境:
nohup/setsid 適用於作業量比較少的情況下; ()+& 適用於測試或寫腳本過程中,臨時使用; disown 適用於事后補救當前已經在運行的作業和命令的情況; screen 最適合有大批量操作時的情況。
為方便理解,可閱讀以下參考文檔:
Linux 技巧:讓進程在后台可靠運行的幾種方法(nohup等) http://blog.csdn.net/zstu_zlj/article/details/51783937
fg bg ctrl + z jobs & 等命令 http://www.cnblogs.com/xlmeng1988/archive/2012/06/04/jobs.html
linux中的(),(()),[],[[]],{}的作用 http://blog.csdn.net/damotiansheng/article/details/44196769
Linux / Unix: disown Command Examples – nixCraft https://www.cyberciti.biz/faq/unix-linux-disown-command-examples-usage-syntax/
Linux運行與控制后台進程的方法:nohup, setsid, &, disown, screen « Hey! Linux. http://www.heylinux.com/archives/1282.html
本文原始鏈接:http://www.cnblogs.com/chinas/p/7130378.html,轉載請注明出處,謝謝!!!!