我們經常會碰到這樣的問題,用 telnet/ssh 登錄了遠程的 Linux 服務器,運行了一些耗時較長的任務, 結果卻由於網絡的不穩定導致任務中途失敗。
解決辦法:
當用戶注銷(logout)或者網絡斷開時,終端會收到 HUP(hangup)信號從而關閉其所有子進程。因此,我們的解決辦法就有兩種:要么讓進程忽略 HUP 信號,要么讓進程運行在新的會話里從而成為不屬於此終端的子進程。
模擬一個斷開的會話,看一下前台進程是否還會正常運行:
[root@node1 ~]# cat b.sh
#!/bin/bash
for((;;)) do
sleep 1
echo "no" >> /tmp/b.txt
done
第一個會話:
[root@node1 ~]# bash b.sh #執行腳本,會占據整個當前會話頁面
新打開一個會話:
[root@node1 ~]# ps -ef | grep b.sh
root 34693 28170 0 11:32 pts/0 00:00:00 bash b.sh
...
[root@node1 ~]# kill -1 34693 #模擬第一個會話斷開的情況,發送SIGHUP信號
再次切換至第一個會話:
[root@node1 ~]# bash b.sh
Hangup #已經斷線
[root@node1 ~]#
nohup
nohup - run a command immune to hangups, with output to a non-tty
運行一個忽略hangups(SIGHUP)影響的命令,輸出到一個非tty
格式:
nohup COMMAND [ARG]...
示例:
[root@node1 ~]# nohup bash b.sh #占據當前會話,使得當前會話不可執行其它命令
nohup: ignoring input and appending output to ‘nohup.out’ #如不指定日志輸出位置,默認輸出至當前目錄下的nohup.out文件中。
重新打開一個會話:
[root@node1 ~]# ps -ef | grep b.sh
root 25973 16490 0 09:56 pts/1 00:00:00 bash b.sh
[root@node1 ~]# kill -1 25973 #向進程發送SIGHUP信號,nohup進程還在運行。關閉上一個session會話,效果和kill -1效果一樣
[root@node1 ~]#
nohup將標准輸出和標准錯誤缺省會被重定向到nohup.out文件中。一般我們可在結尾加上&來將命令同時放入后台運行,也可用>xxx.log 2>&1(將標准輸出和標准錯誤輸出至指定的文件中)來更改缺省的重定向文件名。
setsid
setsid - run a program in a new session
在一個新的會話中運行一個程序。
格式:
setsid program [arg...]
示例:
[root@node1 ~]# setsid bash b.sh #使用setsid來代替nohup
[root@node1 ~]# jobs #后台無法查看
[root@node1 ~]# ps -ef | grep c.sh #ps查找其進程,可以看到PID是43019,但PPID確為1(systemd)
root 43593 1 0 14:15 ? 00:00:00 bash b.sh
root 44473 43385 0 14:22 pts/1 00:00:00 grep --color=auto b.sh
[root@node1 ~]# pstree -H 43593 -p
systemd(1)─┬─NetworkManager(3403)─┬─{NetworkManager}(3543)
│ └─{NetworkManager}(3551)
├─agetty(3528)
├─auditd(2802)───{auditd}(2806)
├─bash(43593)───sleep(44564) #因為父進程是1,不屬於某個終端的子進程,所以也就無法接收到HUP信號。但是不能使用專門指定這個此進程發送HUP信號,這樣是會中斷的。
├─chronyd(3472)
├─crond(3490)
...
├─sshd(4154)───sshd(16488)─┬─bash(36542)───less(40656)
│ ├─bash(42726)
│ └─bash(43385)───pstree(44565)
...
同樣的,在shell也有一種可以使用進程的PPID改成1的方法,使用`()`括起來並加上`&`,也和`setsid`效果一樣。 ``` [root@node1 ~]# (bash b.sh &) #后台執行命令 [root@node1 ~]# jobs #jobs無法查看 [root@node1 ~]# ps -ef | grep b.sh root 45631 1 0 14:30 pts/1 00:00:00 bash b.sh #PPID為1 root 45706 43385 0 14:31 pts/1 00:00:00 grep --color=auto b.sh [root@node1 ~]# kill -1 45631 #向進程發送HUP信號 [root@node1 ~]# ps -ef | grep b.sh #進程被中斷 root 45784 43385 0 14:31 pts/1 00:00:00 grep --color=auto b.sh [root@node1 ~]#
<br />
##jobs:
> 顯示當前會話下后台任務列表。也就是說,如果當命令切換至后台和想要顯示后台有哪些進程不是同一個會話,那么將查不出來有哪些后台進程。
**格式:**
`jobs [option]`
**常用選項:**
* -l:列出進程ID及其它信息
* -p:僅列出PID
* -n:僅列出自從上次輸出了狀態變化提示。run -> stop
* -r:顯示運行中的進程
* -s:顯示停止的進程
<br />
##fg:
> 將命令從后台作業提到前台終端運行。使用格式`fg 任務號`。任務號是由jobs命令獲取的第一列。
<br />
##bg:
> 使用格式`bg 任務號`。想要使用bg命令,首先對前台命令`ctrl+z`下放置后台成stop模式后,在對其使用bg命令才能像一開始在命令后添加`&`一樣后台運行。需要注意的是,如果掛起會影響到當前進程執行的結果,請慎用`ctrl+z`在`bg`。
<br />
**示例:**
[root@node1 ~]# bash b.sh #ctrl+z掛起進程至后台
^Z
[1]+ Stopped bash b.sh
[root@node1 ~]# jobs
[1]+ Stopped bash b.sh
[root@node1 ~]# bg 1 #后台繼續運行
[1]+ bash b.sh &
[root@node1 ~]# jobs #已經運行
[1]+ Running bash b.sh &
[root@node1 ~]#
[root@node1 ~]# bash #重新打開一個bash
[root@node1 ~]# jobs #查詢后台進程,查詢為空,所以jobs只能查詢當前會話的后台進程。
[root@node1 ~]#
[root@node1 ~]# exit #退出新的bash
exit
[root@node1 ~]# jobs #查看jobs
[1]+ Running bash b.sh &
[root@node1 ~]# fg 1 #調至前台運行
bash b.sh
<br />
**總結:**
`nohup`是使用進程本身忽略HUP信號從而達到可以后台運行不被意外中斷。`setsid`和shell中的`( &)`是將進程的父進程改為1,從而達到不受sshd連接的各種情況影響。
`jobs`,`bg`,`fg`三個命令只能在當前的會話終端上執行,一但會話終端意外剝離,就無法在操作。
<br />
參考資料:[https://www.ibm.com/developerworks/cn/linux/l-cn-nohup/index.html](https://www.ibm.com/developerworks/cn/linux/l-cn-nohup/index.html)
