我在模式下运行 QEMU 虚拟机-daemonize
,然后生成一个任意前台(可能是交互式)进程,旨在与 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
答案1
您可以轮询 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
答案2
最后我得到了以下代码:
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 处理行为)。
另一点是,如果我的交互式进程已自行完成,我必须终止监视器进程,以避免进一步推断退出和清理脚本。