现象
在进程A的线程s中使用system执行了进程B(后台持续运行),在进程A退出时向守护进程D发送了socket断开信息,但是进程D没有收到消息。在shell中手动重新运行进程A时,进程A中打开设备失败。
调查步骤1-尝试
作为linux入门级选手,尝试在进程A退出后,在shell端kill掉进程B,发现守护进程D即刻收到socket消息。再次手动运行进程A,不会出现打开设备失败信息。
在进程A中不运行进程B,直接在shell中执行进程B。在进程A退出时,守护进程D即刻收到socket消息。进程A再次运行也不会出现打开设备失败信息。
怀疑进程A通过调用system执行进程B,使进程B继承了进程A的某些资源,造成进程A退出后,进程B不释放。在进程A运行期间,查看进程B的父进程为pid为1:init进程,进程A和进程B不属于父子进程关系。
想到解决方法为:在进程A退出时(正常或者异常),进程B都能跟随退出。基于此想法,进行解决方法搜索。
参照网址使用prctl API, 在父进程退出后,让子进程也退出 - cornsea - 博客园(使用prctl API, 在父进程退出后,让子进程也退出)中说法改写system函数,调用prctl(PR_SET_PDEATHSIG, SIGHUP),写测试程序,在PC上执行,发现kill掉主进程时,创建的子进程的确可以退出。交叉编译到装置里运行,主进程kill掉后,创建的子进程仍不能退出。
调查步骤2-尝试
步骤1中,使用prctl失败后,继续搜索。c - How to make child process die after parent exits? - Stack Overflow How to make child process die after parent exits?。其中提到:
int pipes[2];
pipe(pipes)
if (fork() == 0) {
close(pipes[1]); /* Close the writer end in the child*/
dup2(pipes[0], 0); /* Use reader end as stdin */
exec("sh -c 'set -o monitor; child_process & read dummy; kill %1'")
}
close(pipes[0]); /* Close the reader end in the parent */待去尝试
调查步骤3-解决
在进程A运行期间,使用netstat -anpl | grep -i 端口号 查看到进程A和进程D的相关信息。等进程A退出后,再次使用此命令查看,进程A的socket被进程B接管。父子进程中,Linux中,子进程与父进程的继承关系_gakki的二向箔-CSDN博客_linux子进程继承父进程哪些(Linux中,子进程与父进程的继承关系),子进程继承了父进程的文件描述符,socket和打开设备都属于文件描述符。system执行的进程虽然父进程id不是其执行进程的id,但其中也调用了fork函数(linux system 函数源代码分析-ShadowSrX-ChinaUnix博客),应该也符合子进程继承父进程相关资源。基于父子进程继承问题进行相关搜索。
C语言在linux如何让子进程不继承父进程的资源_百度知道 C语言在linux如何让子进程不继承父进程的资源
一个子进程继承父进程所有文件描述符的坑_anthonytao的博客-CSDN博客_子进程会继承父进程的文件描述符吗 一个子进程继承父进程所有文件描述符的坑
网址中给出了解决方法,在执行execl系列函数前关闭所有已经打开的文件描述符。因此,问题就转换成了,执行fork后,execl前,关闭所有已打开的文件描述符。
先用简单的方法进行测试,(守护进程)如何关闭所有已经打开的文件描述符-CSDN论坛。在进程A退出后,守护进程D可以收到socket消息,再次运行进程A,打开设备正常。
但每次关闭所有的文件描述符,效果还行,但效率感觉不是很好。网址中使用FD_CLOEXEC实现close-on-exec,关闭子进程无用文件描述符_牛晨光的博客-CSDN博客 (O_CLOEXEC)在打开设备时追加O_CLOEXEC标志(socket为SOCK_CLOEXEC),在执行exec时可自动关闭一打开的文件描述符。open时可以追加此标志,但fopen怎么加?
上述简单的方法关闭打开的文件描述符不友好,快速关闭所有打开的文件描述符-xiaosuo-ChinaUnix博客(快速关闭所有打开的文件描述符)提供了遍历/proc/进程id/fd目录下逐一关闭方法。待试。
后记
改写system,在fork后开始执行exe系列函数。例如将:system("xterm -hold -e ls")中参数直接表示成execl("/bin/sh", "sh", "-c", "xterm -hold -e ls"), (char *)0),将会创建两个xterm进程(可以用 top & 替换xterm整条命令),且其中一个为另一个的父进程,若改写成execlp("xterm","-hold","-e","ls",NULL),将不能正确执行。参考关于linux execl函数 - OSCHINA - 中文开源技术交流社区(关于linux execl函数),需改成execlp("xterm", "xterm","-hold","-e","ls",NULL);或者execl("/bin/sh", "xterm","-hold","-e","ls",NULL);
如何将exec函数传参封装成象system函数传参一样?
问题
在进程A退出时发送的socket消息,守护进程D并未收到,跑哪里了?待调查