避免共享对象中的内部线程
Avoiding internal threads in a shared object
我们有一个加载共享对象的产品,它使用 'dll injection' 和 'instrumentation' 技术监视随机进程(我们称之为 myDb)内的内部函数调用。
目前,我们的共享对象有一个内部线程,它从我们的主进程(一个外部实体)接收消息。
一切正常,直到 myDb fork()s 并创建一个相同的子进程,该子进程不会以 execve() 结束。这个相同的子进程主线程在调用 fork() 的父线程的上下文中启动,所有其他父线程不再存在于子线程中。这可能会破坏我们的共享对象内部状态,因为我们的内部线程可能会在任何执行点消失(请参阅 http://www.linuxprogrammingblog.com/threads-and-fork-think-twice-before-using-them 中有关混合线程与分叉的更多信息)。
MyDb 可以在不询问我们的情况下使用 fork,因此,如果我错了,请纠正我,看来我们别无选择,只能让我们的代码在没有内部线程的情况下工作。
我能想到的唯一选择是异步i/o。
根据 http://man7.org/linux/man-pages/man7/aio.7.html,来自异步例程的通知是使用 sigevent 完成的。 sigevent 可以使用 SIGEV_SIGNAL 来使用信号或使用 SIGEV_THREAD 来接收通知,根据手册页,它在下面使用了一个实际的线程。
因此,在我看来,我们唯一的选择是使用带有 SIGEV_SIGNAL 选项的异步 i/o。但这也有很多限制,因为在信号处理程序中处理消息是危险的,因为只能调用异步信号安全函数。
我很乐意在这里对我的问题提出任何建议。
谢谢。
我找到的最佳解决方案是使用 pthread_atfork()
注册处理程序。
这 API 可以帮助将 fork()
与驻留在进程中的其他线程同步,以避免内存不一致。
更多内容在 https://linux.die.net/man/3/pthread_atfork
不幸的是,使用 pthread_atfork()
解决这个问题并不总是那么容易,因为进程的每个线程都必须合作,这是一个严格的假设。除此之外,它有点破坏了我们的设计。
我们决定改变态度,使用 jobs 而不是 threads。我来解释一下:
通常,在创建线程时,它们大部分时间都会阻塞某些调用(read()
、sleep()
等),在这些情况下,线程性并不是真正需要的,改为使用作业可能更有意义。
我们开发了一个 JobScheduler() class,它将作业分配给注册到它的作业(每次超时、fd 就绪读取等)。
现在,在这个 class 中我们定义了一个 fork-safe-zone ,这是我们唯一允许分叉发生的地方,如果在正在进行的作业中发生分叉,则分叉将延迟到作业结束。
这引入了一个限制,即作业执行必须相对较快并且绝不阻塞线程。
这个fork-safe-zone使我们能够在子进程中强制执行内存space的一致性(使用pthread_atfork()
),此外,我们还可以re-create这个child-post-fork 回调中的工作线程,这将使 fork 限制不可见。
我们有一个加载共享对象的产品,它使用 'dll injection' 和 'instrumentation' 技术监视随机进程(我们称之为 myDb)内的内部函数调用。
目前,我们的共享对象有一个内部线程,它从我们的主进程(一个外部实体)接收消息。
一切正常,直到 myDb fork()s 并创建一个相同的子进程,该子进程不会以 execve() 结束。这个相同的子进程主线程在调用 fork() 的父线程的上下文中启动,所有其他父线程不再存在于子线程中。这可能会破坏我们的共享对象内部状态,因为我们的内部线程可能会在任何执行点消失(请参阅 http://www.linuxprogrammingblog.com/threads-and-fork-think-twice-before-using-them 中有关混合线程与分叉的更多信息)。
MyDb 可以在不询问我们的情况下使用 fork,因此,如果我错了,请纠正我,看来我们别无选择,只能让我们的代码在没有内部线程的情况下工作。
我能想到的唯一选择是异步i/o。 根据 http://man7.org/linux/man-pages/man7/aio.7.html,来自异步例程的通知是使用 sigevent 完成的。 sigevent 可以使用 SIGEV_SIGNAL 来使用信号或使用 SIGEV_THREAD 来接收通知,根据手册页,它在下面使用了一个实际的线程。 因此,在我看来,我们唯一的选择是使用带有 SIGEV_SIGNAL 选项的异步 i/o。但这也有很多限制,因为在信号处理程序中处理消息是危险的,因为只能调用异步信号安全函数。
我很乐意在这里对我的问题提出任何建议。
谢谢。
我找到的最佳解决方案是使用 pthread_atfork()
注册处理程序。
这 API 可以帮助将 fork()
与驻留在进程中的其他线程同步,以避免内存不一致。
更多内容在 https://linux.die.net/man/3/pthread_atfork
不幸的是,使用 pthread_atfork()
解决这个问题并不总是那么容易,因为进程的每个线程都必须合作,这是一个严格的假设。除此之外,它有点破坏了我们的设计。
我们决定改变态度,使用 jobs 而不是 threads。我来解释一下:
通常,在创建线程时,它们大部分时间都会阻塞某些调用(read()
、sleep()
等),在这些情况下,线程性并不是真正需要的,改为使用作业可能更有意义。
我们开发了一个 JobScheduler() class,它将作业分配给注册到它的作业(每次超时、fd 就绪读取等)。 现在,在这个 class 中我们定义了一个 fork-safe-zone ,这是我们唯一允许分叉发生的地方,如果在正在进行的作业中发生分叉,则分叉将延迟到作业结束。 这引入了一个限制,即作业执行必须相对较快并且绝不阻塞线程。
这个fork-safe-zone使我们能够在子进程中强制执行内存space的一致性(使用pthread_atfork()
),此外,我们还可以re-create这个child-post-fork 回调中的工作线程,这将使 fork 限制不可见。