最近在尝试使用child_process来跑一些子任务时,调用了yarn的命令来启动一个服务,但是发现怎么调接口都无法kill,子任务还在跑。后来灵光一闪,spawn在运行yarn命令的时候,是不是再在它的上面包了一层。顺藤摸瓜,发现网上也有同样的说法。原来,spawn在运行子命令的时候,首先要选一个shell来跑,默认是使用sh,也可以使用bash或cmd.exe,这个需要在调用spawn的时候传入它的第三个参数里面的配置来确定。这也就意味着,child.pid返回的是调用sh的进程,而在sh里面执行命令产生的子进程的pid不是默认返回的child.pid,而且,在sh里面,可能还会再起其他的子进程,也就是说,实际上,我们看似只跑了一个spawn,但是实际上它可能起了一堆子进程,而我们想要kill的目标进程只是这一堆里面的其中一个。
默认情况下,我们通过ctrl+c可以退出process,而nodejs会把SIGINI传递给所有子进程,进而关闭全部子进程。但是,如果我们自己手动调用child.kill来杀死子进程,就会导致只杀掉了一个,而且可能是最不重要的(因为跑的是sh)。
有了这个前置知识,那么问题就比较好解决了。我们只要封装一个自己的kill函数,传入child.pid,然后把其全部子进程杀掉即可。具体做法可以参考ps-tree里面的示例代码。在我们自己实现的时候,也可以直接引用ps-tree这个包来把所有子进程pid查出来,然后再通过exec遍历它们执行一个kill -9 {pid}即可。如此就可以真正把子进程杀掉了。