本文主要來自https://fukun.org/archives/06281192.html 和 http://blog.csdn.net/wangjun_1218/article/details/6835800
外加一點自己的實踐。
原文如下:
https://fukun.org/archives/06281192.html
在Linux中,如果要讓進程在後臺運行,一般情況下,我們在命令後面加上&即可,實際上,這樣是將命令放入到一個作業隊列中了:
1
2
3
4
5
|
$
. /test .sh
& [1]
17208 $
jobs -l [1]+
17208 Running . /test .sh
& |
對於已經在前臺執行的命令,也可以重新放到後臺執行,首先按ctrl+z暫停已經運行的進程,然後使用bg命令將停止的作業放到後臺運行:
1
2
3
4
5
6
7
8
|
$
. /test .sh [1]+
Stopped . /test .sh $
bg
%1 [1]+
. /test .sh
& $
jobs -l [1]+
22794 Running . /test .sh
& |
但是如上方到後臺執行的進程,其父進程還是當前終端shell的進程,而一旦父進程退出,則會發送hangup信號給所有子進程,子進程收到hangup以後也會退出。如果我們要在退出shell的時候繼續運行進程,則需要使用nohup忽略hangup信號,或者setsid將將父進程設爲init進程(進程號爲1)
1
2
3
4
5
6
7
8
9
|
$
echo
$$ 21734 $
nohup
. /test .sh
& [1]
29016 $
ps
-ef | grep
test 515
29710 21734 0 11:47 pts /12
00:00:00 /bin/sh
. /test .sh 515
29713 21734 0 11:47 pts /12
00:00:00 grep
test |
1
2
3
4
5
6
|
$
setsid . /test .sh
& [1]
409 $
ps
-ef | grep
test 515
410 1 0 11:49 ? 00:00:00 /bin/sh
. /test .sh 515
413 21734 0 11:49 pts /12
00:00:00 grep
test |
上面的試驗演示了使用nohup/setsid加上&使進程在後臺運行,同時不受當前shell退出的影響。那麼對於已經在後臺運行的進程,該怎麼辦呢?可以使用disown命令:
1
2
3
4
5
6
7
8
9
10
11
|
$
. /test .sh
& [1]
2539 $
jobs -l [1]+
2539 Running . /test .sh
& $
disown -h %1 $
ps
-ef | grep
test 515
410 1 0 11:49 ? 00:00:00 /bin/sh
. /test .sh 515
2542 21734 0 11:52 pts /12
00:00:00 grep
test |
另外還有一種方法,即使將進程在一個subshell中執行,其實這和setsid異曲同工。方法很簡單,將命令用括號() 括起來即可:
1
2
3
4
5
|
$
(. /test .sh
&) $
ps
-ef | grep
test 515
410 1 0 11:49 ? 00:00:00 /bin/sh
. /test .sh 515
12483 21734 0 11:59 pts /12
00:00:00 grep
test |
注:本文試驗環境爲Red Hat Enterprise Linux AS release 4 (Nahant Update 5),shell爲/bin/bash,不同的OS和shell可能命令有些不一樣。例如AIX的ksh,沒有disown,但是可以使用nohup -p PID來獲得disown同樣的效果。
還有一種更加強大的方式是使用screen,首先創建一個斷開模式的虛擬終端,然後用-r選項重新連接這個虛擬終端,在其中執行的任何命令,都能達到nohup的效果,這在有多個命令需要在後臺連續執行的時候比較方便:
1
2
3
4
5
6
7
8
|
$
screen
-dmS screen_test $
screen
-list There
is a screen
on: 27963.screen_test
(Detached) 1
Socket in
/tmp/uscreens/S-jiangfeng . $
screen
-r screen_test |
echo $$
nohup ./tcp_demo &
ps -ef |grep tcp_demo
說明:tcp_demo是要運行的程序名稱我們知道,當用戶註銷(logout)或者網絡斷開時,終端會收到 HUP(hangup)信號從而關閉其所有子進程。因此,我們的解決辦法就有兩種途徑:要麼讓進程忽略 HUP 信號,要麼讓進程運行在新的會話裏從而成爲不屬於此終端的子進程。
1. nohup
顯而易見,nohup命令的功能是使進程忽略hangup信號,從而持續執行。nohup 的使用是十分方便的,只需在要處理的命令前加上 nohup 即可,標準輸出和標準錯誤缺省會被重定向到 nohup.out 文件中。一般我們可在結尾加上"&"來將命令同時放入後臺運行,也可用">filename 2>&1"來更改缺省的重定向文件名。
如:nohup myUbuntuSourceSyncCmd.pl >./sources/ubuntu/sync.log 2>&1 &
這樣起到了三個效果:進程後臺執行;忽略hangup信號;輸出重定向。
2. setsid
setsid中的sid指的是session id,意指以該命令運行的進程是一個新的session,因此其父進程id不屬於當前終端。實際上,setsid運行的進程,其父進程id(PPID)爲1(init 進程的 PID)。因此,setsid解決問題用的是第二種途徑。
如:setsid myUbuntuSourceSyncCmd.pl >./sources/ubuntu/sync.log 2>&1 &
格式與nohup相仿,後臺運行也需加上&,但輸出重定向必須手動設置。
3. 括號()與&
&代表後臺運行(注意輸出並沒有被重定向);此外,我們知道,將一個或多個命名包含在“()”中就能讓這些命令在子 shell 中運行中,從而擴展出很多有趣的功能,我們現在要討論的就是其中之一。
當我們將"&"也放入“()”內之後,我們就會發現所提交的作業並不在作業列表中,也就是說,是無法通過jobs來查看的。
如:(myUbuntuSourceSyncCmd.pl >./sources/ubuntu/sync.log 2>&1 &)
以這種方式運行程序,新提交的進程的父 ID爲1,並不是當前終端的進程 ID。因此並不屬於當前終端的子進程,從而也就不會受到當前終端的 HUP 信號的影響了。
4. disown
如果我們未加任何處理就已經提交了命令,這時想加 nohup 或者 setsid 已經爲時已晚,只能通過作業調度和 disown 來解決這個問題了。讓我們來看一下 disown 的幫助信息:
disown [-ar] [-h] [jobspec ...]
Without options, each jobspec is removed from the table of
active jobs. 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.
可以看出,我們可以用如下方式來達成我們的目的。
a. 用disown -h jobspec 來使某個作業忽略HUP信號。
b. 用disown -ah 來使所有的作業都忽略HUP信號。
c. 用disown -rh 來使正在運行的作業忽略HUP信號。
需要注意的是,當使用過 disown 之後,會將把目標作業從作業列表中移除,我們將不能再使用jobs來查看它,但是依然能夠用ps -ef查找到它。
但是還有一個問題,這種方法的操作對象是作業,如果我們在運行命令時在結尾加了"&"來使它成爲一個作業並在後臺運行,那麼就萬事大吉了,我們可以通過jobs命令來得到所有作業的列表。但是如果並沒有把當前命令作爲作業來運行,如何才能得到它的作業號呢?答案就是用 CTRL-z(按住Ctrl鍵的同時按住z鍵)了!
CTRL-z 的用途就是將當前進程掛起(Suspend),然後我們就可以用jobs命令來查詢它的作業號,再用bg jobspec 來將它放入後臺並繼續運行。需要注意的是,如果掛起會影響當前進程的運行結果,慎用此方法。
示例:
root@ubuntu-server:/mnt/ftp# jobs
[1]+ Running ./sync_mirror.sh &
root@ubuntu-server:/mnt/ftp# disown -h %1
root@ubuntu-server:/mnt/ftp# ps -ef|grep sync_mirror.sh
root 8650 22271 0 15:08 pts/0 00:00:00 grep sync_mirror.sh
root 16748 22271 0 Mar31 pts/0 00:00:00 /bin/sh -e ./sync_mirror.sh
另外還有一個命令screen。screen 提供了 ANSI/VT100 的終端模擬器,使它能夠在一個真實終端下運行多個全屏的僞終端。screen 的參數很多,具有很強大的功能。本篇不做介紹。