
我有一個在 Ubuntu 上運行的服務,配置如下:
#/etc/init/my_service.conf
start on (local-filesystems and net-device-up IFACE=eth1)
respawn
exec python -u /opt/XYZ/my_prog.py 2>&1 \
| logger -t my_prog.py
使用 停止服務時sudo service my_service stop
,python 進程不會被終止。殺死父進程也kill
不會殺死 python 進程。
如何完全停止服務(即殺死所有子進程)?理想情況下,我不想修改上面的設定檔。
答案1
理想情況下,我不想修改上面的設定檔。
艱難的!這是正確的做法。
您需要將其變更exec
為script
,並停止在作為管道一部分的分叉子進程中執行該 python 程式。 這個伺服器故障答案解釋如何在嵌入式 shell 腳本中執行此操作。我只需對最後一行中給出的腳本進行一個更改:
exec python -u /opt/XYZ/my_prog.py 2>&1
畢竟,沒有充分的理由不記錄標準錯誤。
應對分叉的複雜迴轉,從expect daemon
到 切換到systemd
,忽略了正確的做法是阻止守護程式分叉。如果說當前的混亂能帶來一件好事的話,那就是 IBM 在 1995 年所寫和推薦的內容多年來一直被不斷證實是正確的。
習慣這個想法鍊式裝載守護進程。有很多工具集可以讓這些事情變得簡單。也要習慣不使用 shell 腳本的想法。有許多專為這項工作設計的工具集,可以消除 shell 的開銷(這是 Ubuntu 世界中眾所周知的好主意)。
例如:ServerFault 答案中的 shell 命令可以替換為使用以下命令的腳本洛朗·貝爾科特的execline
工具它們被設計為能夠在沒有子 shell 和未連結的 FIFO 的情況下執行此操作:
#!/command/execlineb -PW
pipeline -w {
logger -t my_prog.py
}
fdmove -c 2 1
python -u /opt/XYZ/my_prog.py
然後你會簡單地
exec /foo/this_execlineb_script
和我的nosh
工具集,它同樣是一個包含以下內容的腳本:
#!/usr/local/bin/nosh
pipe
fdmove -c 2 1
python -u /opt/XYZ/my_prog.py | logger -t my_prog.py
或者,可以直接在 Upstart 作業定義中使用此節(使用一種技巧來避免 shell 元字符,以便 Upstart 不會產生 shell):
exec /usr/local/bin/exec pipe --separator SPLIT fdmove -c 2 1 python -u /opt/XYZ/my_prog.py SPLIT logger -t my_prog.py
進一步閱讀
- 拉斯·奧爾伯里 (2013-12-18)。 Bug#727708:Debian 中新貴提出的政策。 debian-ctte。
- 喬納森·德博因·波拉德 (2001)。 」不要為了「將守護程式置於背景」而使用 fork()。」。 設計 Unix 守護程式時要避免的錯誤。經常給的答案。
答案2
在 GNU/Linux 上,通常無法停止服務及其產生的所有子進程,因為子進程可以變更其 PPID(父進程 ID)。唯一知道的方法是痕跡系統呼叫生成進程,並保留這些進程的清單。
Ubuntu init 系統upstart
不會執行此操作。所以你的問題的答案是不可能-- 在 Ubuntu 上 -- 沒有:
- 修改該腳本;
- 確切地知道該進程產生了哪些進程 ID;
- 手動追蹤這些進程 ID;
- 單獨殺死他們。
這就是為什麼你應該運行一個可以運行的 Linux 發行版系統。如您所見,systemd追蹤所有子進程並且可以用一個命令殺死他們中的每一個。這就是 GNU/Linux 系統管理的方式應該,但因為 systemd 太新了,而且因為它是「Not Invented Here」(意思是 Canonical 沒有發明它),Ubuntu 不想使用它。
答案3
你已經跳過了預計節。這新貴食譜指定:
警告
這節非常重要:請仔細閱讀本節!
Upstart 將追蹤它認為屬於某個作業的進程 ID。如果作業指定了實例節,Upstart 將追蹤該作業的每個唯一實例的 PID。
如果您不指定expect 節,Upstart 將追蹤它在exec 或script 節中執行的第一個PID 的生命週期。然而,大多數 Unix 服務將“守護進程”,這意味著它們將創建一個新進程(使用 fork(2)),該進程是初始進程的子進程。服務通常會“雙分叉”,以確保它們與初始流程沒有任何關聯。 (請注意,沒有服務最初會分叉兩次以上,因為這樣做沒有額外的好處)。
在這種情況下,Upstart 必須有一種方法來追蹤它,因此您可以使用expect fork,或者expect 守護進程,它允許Upstart 使用ptrace(2) 來「計數fork」。
為了讓 Upstart 確定作業的最終進程 ID,它需要知道該進程會呼叫 fork(2) 多少次。 Upstart 本身無法知道這個問題的答案,因為一旦守護進程運行,它就可以分叉許多「工作」進程,這些進程本身可以分叉任意次數。在這種情況下,不能期望 Upstart 知道哪個 PID 是“master”,因為它根本不知道是否會創建工作進程,更不用說進程最初會分叉多少次。因此,有必要告訴 Upstart 哪個 PID 是「主」或父 PID。這是透過使用expect 節來實現的。
|您需要這個,因為您正在使用的管道正在建立子進程。你可以在書中找到進階Linux編程對此進行簡要介紹,其中指出:
例如,此 shell 指令使 shell 產生兩個子進程,一個用於LS和一個用於較少的:
$ ls | less
我不知道這是否意味著一個或兩個分叉,所以我會嘗試修改該行重生在你的程式碼中
expect fork
respawn
或者
expect daemon
respawn
我不相信這可以實現僅有的和系統,儘管我的標誌清楚地表明我是系統。