在linux下写程序时,为了复用已经存在的程序或避免不稳定的第三方库导致主进程crash, 需要调用外部程序去实现相应的功能。
这时很多人会直接使用标准库里的函数,如C/C++中的system/popen。从源码可以看到,system或popen都是fork+exec这种linux经典的方式去调用外部程序。但是如果没有合理地使用fork+exec,可能会产生一些意想不到问题,或者满足不了需求。下面是具体的例子和相应的解决方案。
1. 程序在启动时端口被调用的子进程占用了
在重启程序时,可能会有报端口被占用了,导致进程起不来,这时可以用netstat -nlp | grep 看这个端口是被哪个程序占用了。有时会发现是端口是被自己之前调用的程序给占用了,原因是fork出来的子进程会默认继承父进程的文件描述符,即使运行exec之后新进程也还会占用这些文件描述符,父进程退出时子进程还在运行,文件描述符(包含端口、打开文件句柄等)没有释放。所以如果没有特殊要求,fork出来的子进程都应该把继承的文件描述符给关了,或者在打开文件描述符的时候加上O_CLOEXEC参数,这样调用exec运行程序就不会继承父进程的文件描述符啦。
2. 被调用的进程可能会变成僵尸进程
其实僵尸进程并没有名字那么可怕,僵尸进程是进程生命周期的一部分。每一个进程退出后,还会在内核进程表时保存有包含进程ID、占用资源等信息的记录,交给父进程来处理,这就是僵尸进程。这时用ps看进程的后面会有的标识。如果僵尸进程没有得到及时清理,占用的资源(如进程ID)就不会释放,可能会造成因资源耗尽机器死机的后果。所以在用fork调用外部命令时,需要在父进程调用wait或waitpid等待子进程退出。如果父进程在wait