编写init.d脚本时保存forked进程PID出现不一致问题
为什么sudo启动的进程$!捕获的PID和实际java进程PID不一致?
问题描述
我正在编写init.d脚本,遇到了保存forked进程PID的问题。以下是我的脚本片段:
sudo -u $USER nohup -- nice -n "0" java Test >> /dev/null 2>&1 < /dev/null & NEWPID="$!" echo $NEWPID
运行该脚本后输出的PID为4595,但执行ps | grep java得到的java进程PID是4596,二者不一致。而直接执行java Test & NEWPID="$!" echo $NEWPID时,输出的PID与ps查询结果一致。请问第一种写法存在什么问题?
问题原因
你碰到的核心问题是**$!捕获的是sudo进程的PID,而不是最终java进程的PID**,整个命令的执行链是这样的:
- 当你在命令末尾加
&时,后台启动的是最外层的sudo进程(也就是你看到的4595) sudo会创建新进程切换到指定用户,然后启动nohupnohup接着执行nice,最后nice启动目标java进程(PID 4596)
而直接运行java Test &时,没有中间的sudo/nohup/nice这些包装进程,$!直接对应java进程本身的PID,所以和ps结果完全匹配。
解决办法
要正确捕获java进程的PID,你需要让sudo执行一个子shell,在子shell内部处理后台启动逻辑,这样$!就能拿到真正的java相关PID:
直接输出PID
sudo -u $USER bash -c 'nohup nice -n 0 java Test >> /dev/null 2>&1 < /dev/null & echo $!'
保存PID到文件(init.d脚本常用方式)
sudo -u $USER bash -c 'nohup nice -n 0 java Test >> /dev/null 2>&1 < /dev/null & echo $! > /var/run/test.pid'
这里的关键逻辑是:
bash -c创建的子shell会在目标用户环境下执行命令,$!在子shell中捕获的是nohup进程的PID- 而
nohup和nice都是通过exec调用后续命令的,它们不会保留自身进程,最终这个PID会直接对应到java进程
另外补充一点:你脚本里的nohup --是多余的,因为nice没有需要用--分隔的参数,去掉也不会影响功能。
内容的提问来源于stack exchange,提问作者Some Name
相关产品推荐
相关产品推荐

