gpt4 book ai didi

c - 如何使用 pthread_atfork() 和 pthread_once() 重新初始化子进程中的互斥量

转载 作者:太空狗 更新时间:2023-10-29 16:28:36 24 4
gpt4 key购买 nike

我们有一个使用 ZeroC's Ice 的 C++ 共享库RPC 的库,除非我们关闭 Ice 的运行时,否则我们会观察到子进程卡在随机互斥体上。 Ice 运行时启动线程,有许多内部互斥体,并为服务器保持打开的文件描述符。

此外,我们有一些自己的互斥锁来保护我们的内部状态。

我们的共享库被数百个内部应用程序使用,因此我们无法控制进程何时调用 fork(),因此我们需要一种方法来安全地关闭 Ice 并在进程派生时锁定我们的互斥量。

pthread_atfork() 上阅读 POSIX 标准关于处理互斥锁和内部状态:

Alternatively, some libraries might have been able to supply just a child routine that reinitializes the mutexes in the library and all associated states to some known value (for example, what it was when the image was originally executed). This approach is not possible, though, because implementations are allowed to fail *_init() and *_destroy() calls for mutexes and locks if the mutex or lock is still locked. In this case, the child routine is not able to reinitialize the mutexes and locks.

在 Linux 上,this test C program从子 pthread_atfork() 处理程序中的 pthread_mutex_unlock() 返回 EPERM。 Linux 需要将 _NP 添加到 PTHREAD_MUTEX_ERRORCHECK 宏才能编译。

此程序链接自 good thread .

鉴于解锁或销毁子进程中的互斥量在技术上是不安全或不合法的,我认为最好有指向互斥量的指针,然后让子进程在堆上创建新的 pthread_mutex_t 并单独保留父进程的互斥量,从而有一个小的内存泄漏。

唯一的问题是如何重新初始化库的状态,我正在考虑重置 pthread_once_t。可能是因为 POSIX 有一个 pthread_once_t 的初始值设定项,它可以重置为初始状态。

#include <pthread.h>
#include <stdlib.h>
#include <string.h>

static pthread_once_t once_control = PTHREAD_ONCE_INIT;

static pthread_mutex_t *mutex_ptr = 0;

static void
setup_new_mutex()
{
mutex_ptr = malloc(sizeof(*mutex_ptr));
pthread_mutex_init(mutex_ptr, 0);
}

static void
prepare()
{
pthread_mutex_lock(mutex_ptr);
}

static void
parent()
{
pthread_mutex_unlock(mutex_ptr);
}

static void
child()
{
// Reset the once control.
pthread_once_t once = PTHREAD_ONCE_INIT;
memcpy(&once_control, &once, sizeof(once_control));
}

static void
init()
{
setup_new_mutex();
pthread_atfork(&prepare, &parent, &child);
}

int
my_library_call(int arg)
{
pthread_once(&once_control, &init);

pthread_mutex_lock(mutex_ptr);
// Do something here that requires the lock.
int result = 2*arg;
pthread_mutex_unlock(mutex_ptr);

return result;
}

在上面的 child() 示例中,我只通过复制一个用 PTHREAD_ONCE_INIT 初始化的新 pthread_once_t 来重置 pthread_once_t。只有在子进程中调用库函数时才会创建新的 pthread_mutex_t。

这很棘手,但也许是处理这种绕过标准的最佳方式。如果 pthread_once_t 包含互斥锁,那么系统必须有一种方法可以从 PTHREAD_ONCE_INIT 状态对其进行初始化。如果它包含一个指向堆上分配的互斥量的指针,那么它将被迫分配一个新的互斥量并在 pthread_once_t 中设置地址。我希望它不会将 pthread_once_t 的地址用于任何会打败它的特殊内容。

正在搜索 comp.programming.threads group for pthread_atfork()展示了很多很好的讨论,而 POSIX 标准真正提供的解决这个问题的能力是多么的少。

还有一个问题是,人们应该只从 pthread_atfork() 处理程序调用异步信号安全函数,并且出现了 most important one is the child handler。 ,其中只完成了一个 memcpy()。

这个有用吗?有没有更好的方法来处理我们共享库的需求?

最佳答案

恭喜,您发现了标准中的缺陷。 pthread_atfork 从根本上无法解决它为使用互斥体而创建的问题,因为不允许子进程中的处理程序对它们执行任何操作:

  • 它无法解锁它们,因为调用者将是新创建的子进程中的新主线程,而这与获得锁的线程(在父进程中)不是同一线程
  • 它无法摧毁它们,因为它们被锁定了。
  • 它不能重新初始化它们,因为它们还没有被销毁。

一个可能的解决方法是在这里使用 POSIX 信号量代替互斥量。信号量没有所有者,因此如果父进程锁定它 (sem_wait),则父进程和子进程都可以解锁 (sem_post) 各自的副本,而无需调用任何未定义的行为。

顺便说一句,sem_post 是异步信号安全的,因此 child 使用绝对合法。

关于c - 如何使用 pthread_atfork() 和 pthread_once() 重新初始化子进程中的互斥量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2620313/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com