Bash
執行前台程序,直到後台程序退出 shell
我在
-daemonize
模式下執行 QEMU 虛擬機,然後生成一個任意前台(可能是互動式)程序,用於與 QEMU 實例進行互動。通常,一旦前台程序完成,我會通過 pidfile 清理 QEMU 實例:qemu-system ... -pidfile ./qemu.pid -daemonize /my/custom/interactive/process pkill -F ./qemu.pid
但是在某些情況下,QEMU 可以獨立退出並且我的前台程序繼續執行。但我想阻止它以防萬一。所以我的自定義互動過程的行為應該是這樣的:
tail -f --pid=./qemu.pid /dev/null
我怎樣才能很好地做到這一點?也許有某種類似超時的包裝器,所以我可以執行類似的東西:
trackpid ./qemu.pid /my/custom/interactive/process
最後我得到了以下程式碼:
qemu-system ... -pidfile ./qemu.pid -daemonize { tail -f --pidfile="$(cat ./qemu.pid)" /dev/null kill -INT 0 } & /my/custom/interactive/process kill $! pkill -F ./qemu.pid
花括號中的腳本實際上是在後台執行的 pidfile 監視器。一旦 pid 消失,監控終止目前程序組。我使用
kill -INT 0
它是因為它為我提供了最可靠和最乾淨的結果。其他選擇是:
kill -- 0
(使用 TERM 信號終止,不會正確終止互動過程)
kill -INT $$
(僅殺死 shell 程序,不會正確終止互動程序)
kill -- -$$
(殺死由 shell 的 pid 表示的程序組,並不總是正常工作,我假設由於呼叫sudo
作為程序組負責人)
pkill -P $$
(僅殺死子程序,實際上可以,但我更喜歡使用內置的 shell 並依賴 Ctrl-C 處理行為)。另一點是,如果我的互動過程已自行完成,我必須終止監視程序以避免進一步推斷退出和清理腳本。
您可以輪詢 qemu 程序是否消失,並在它消失時提前終止。這是經過快速測試的程式碼,尤其是未使用
qemu-system
.它也有很多。您可以刪除這些
#DEBUG
行,但如果您有興趣了解它們是如何連接在一起的,請取消註釋並將程序輸出與程式碼進行比較。#!/bin/bash InvokeQemu() { local i pid pidFile=qemu.pid # Start the qemu process, and return the PID if possible # ( # qemu-system ... -pidFile "$pidFile" -daemonize ( sleep 30 & sleep 0.5 && echo $! >"$pidFile" ) # FAKE IT for half a minute ) >/dev/null 2>&1 </dev/null #echo "InvokeQemu: checking for successful daemonisation" >&2 #DEBUG for i in 1 2 3 do # Does the PID file exist yet #echo "InvokeQemu: attempt $i" >&2 #DEBUG if [[ -s "$pidFile" ]] && pid=$(cat "$pidFile") && [[ -n "$pid" ]] then printf "%s\n" $pid #echo "InvokeQemu: pid=$pid" >&2 #DEBUG return 0 fi # Pause a moment or so before trying again sleep 2 done return 1 } MonitorPIDs() { local pid for pid in "$@" do #echo "MonitorPIDs: checking pid $pid" >&2 #DEBUG if err=$(kill -0 "$pid" 2>&1) || [[ "$err" == *permitted* || "$err" == *denied* ]] then # Process still exists : #echo "MonitorPIDs: pid $pid still alive" >&2 #DEBUG else #echo "MonitorPIDs: pid $pid has died" >&2 #DEBUG echo "$pid" return 1 fi done #echo "MonitorPIDs: all good" >&2 #DEBUG return 0 } ######################################################################## # Go myPid=$$ # Start the qemu emulator echo "Starting qemu emulator" qemuPid=$(InvokeQemu) if [[ -z "$qemuPid" ]] then echo "Could not start qemu" >&2 exit 1 fi # Start the monitor process # # Once any of them is no longer running it will fire SIGTERM to its # remaining PIDs and then exit echo "Starting monitor process" ( while MonitorPIDs $qemuPid $myPid >/dev/null do #echo "(Monitor): all good" >&2 #DEBUG sleep 2 done kill $qemuPid $myPid 2>/dev/null ) & # Start your interactive foreground process # # You will receive SIGTERM within a few seconds of the emulator exiting, # so you may want to trap that echo "Starting interactive process" while read -p "What do you want to do? " x do echo "OK" sleep 1 done exit 0