execvp调用后进程PID异常,父进程无法终止子进程求助
首先,咱们先拆解你遇到的两个核心问题:为什么execvp后脚本里的PID和子进程PID不一致,以及为什么kill(SIGKILL, pid)无效。
为什么PID会不一样?
你说的完全没错——execvp调用后进程PID确实不会改变,但这里的关键是:你用execvp启动的是csh,而csh在运行你的a.sh脚本时,又额外fork了一个新进程来执行它!
因为a.sh是bash脚本(你用到了$BASHPID这个bash专属变量),csh识别到脚本类型后,会启动一个bash进程来解析运行脚本内容。所以你在脚本里打印的$BASHPID是这个bash子进程的PID,而非execvp启动的csh进程PID(也就是你fork出来的子进程PID)。
为什么kill(SIGKILL, pid)无效?
你用kill终止的是csh的PID,但实际在后台跑无限循环的是那个bash子进程。当你杀掉csh后,这个bash进程会变成孤儿进程,被系统的init/systemd进程接管,继续运行,所以看起来父进程的kill操作没起作用。
可行的解决办法
这里有几个方案,你可以根据需求选择:
方案1:直接用bash运行脚本,跳过csh中转
既然你的脚本是bash脚本,没必要通过csh跳转,直接让子进程execvp调用bash来运行a.sh:
// 替换原来的execvp调用逻辑 execvp("bash", (char*[]){"bash", "a.sh", NULL});
这样execvp启动的bash进程就是执行脚本的进程,PID和你fork出来的子进程PID完全一致,此时父进程用kill(SIGKILL, pid)就能直接终止脚本进程。
方案2:让csh替换自身为脚本进程,不fork新进程
如果必须用csh触发脚本,可以让csh用exec命令运行脚本,这样csh会直接替换自身为bash进程,不会产生新PID:
// 用csh的-c选项执行exec命令 execvp("csh", (char*[]){"csh", "-c", "exec ./a.sh", NULL});
这样execvp启动的csh会立刻替换成bash进程,PID保持不变,父进程kill这个PID就能终止脚本。
方案3:终止整个进程组
如果上面两种方式都不适合,你可以直接向整个进程组发送信号——csh和它fork出来的bash进程属于同一个进程组,只需要把kill的PID参数改成负数(代表进程组ID,这里进程组ID就是csh的PID):
kill(-pid, SIGKILL); // pid是你fork出来的子进程PID(即csh的PID)
这样就能一次性杀掉csh和它所有的子进程,包括运行脚本的bash进程。
内容的提问来源于stack exchange,提问作者k.p

