背景
我正在用C编写一个与LD_PRELOAD
动态链接的共享库,用于拦截和覆盖来自预加载应用程序的网络调用,如socket()
、connect()
、recv()
、send()
等。
在库的init中,我用pthread_create()
启动了一个线程。这个线程轮询一些内核内存,这些内存被映射到用户空间。
该库的通用性足以处理(但不限于)网络基准应用程序,如netperf、iperf和sockperf。
我的问题
一切都很好,生活在大多数情况下都是甜蜜的,只有一种例外。停用的应用程序。例如,如果我作为deamon启动netserver(netperf基准测试应用程序的服务器端),即在没有-D
参数的情况下,应用程序首先要做的事情之一就是调用fork()
。在fork上,父级使用exit(EXIT_SUCCESS)
关闭,子级侦听连接。父级退出的事实扼杀了我的轮询线程。
因此,我想实现的是,如果父母已经离开,让孩子产生一个新的轮询线程。我可以拦截并覆盖fork()
调用,但从根本上讲,我如何让孩子知道父母是否已经离开?我不能接受任何假设,因为库必须是通用的。
谢谢。
您可以定期轮询getppid()函数。一旦它开始返回"1"(init进程的id),您的父级就死了。
更新
摘录自"man pthread_create":
新线程以以下方式之一终止:。。。
- 进程中的任何线程调用exit(3)或主线程从main()执行返回。这导致所有进程中的线程
因此,如果您的线程是由调用exit的netserver进程创建的,那么这个线程将被终止
您可以使用ptread_atfork(3)
在子线程中启动线程。
大约一年后,我自己的问题得到了答案,那就是"一个子进程如何知道它的父进程已经死了?">
那么,孩子可以使用getppid()
询问其父母的PID是什么。如果父PID为1,则意味着子进程已重定为init
进程。因此,这位家长已经走了。
例如:
void child_fork(void)
{
int parent_pid;
usleep(1);
parent_pid = (int)getppid();
if (parent_pid == 1)
run_my_polling_thread();
}
注意:当进程分叉时,父进程将进入睡眠状态。首先运行子进程,然后唤醒父进程(对于写时复制优化,我不在这里详细介绍)。usleep(1)
调用是为了让孩子立即进入睡眠状态,这样父母就有时间醒来并终止。在孩子醒来之前,它将被重新分配到init
。
现在,在lib初始化时,我只需要调用:
__register_atfork(NULL, NULL, child_fork, NULL)
但是,这种解决方案并不适用于所有情况。例如,如果父母有多个孩子怎么办?我真的希望他们都启动一个轮询线程吗?
现在这对我来说是一个可以接受的解决方案,尽管并不理想。