我们有一个C ++共享库,它使用ZeroC的Ice库来实现RPC,除非我们关闭Ice的运行时,否则我们观察到子进程挂在随机互斥锁上。 Ice运行时启动线程,具有许多内部互斥锁并保持打开文件描述符到服务器。
另外,我们有一些我们自己的互斥体来保护我们的内部状态。
我们的共享库被数百个内部应用程序使用,因此我们无法控制进程何时调用fork(),因此我们需要一种方法来安全地关闭Ice并在进程分叉时锁定我们的互斥锁。
在pthread_atfork()上读取处理互斥锁和内部状态的POSIX标准:
或者,某些库可能只能提供一个子例程,它将库中的互斥锁和所有关联状态重新初始化为某个已知值(例如,最初执行映像时的状态)。 但是,这种方法是不可能的,因为如果互斥锁或锁仍然被锁定,则允许实现失败* _init()和* _destroy()调用互斥锁和锁。 在这种情况下,子例程无法重新初始化互斥锁和锁。
在Linux上,此测试C程序从子pthread_atfork()处理程序中的pthread_mutex_unlock()返回EPERM。 Linux需要将_NP添加到PTHREAD_MUTEX_ERRORCHECK宏以便进行编译。
这个程序是从这个好的线程链接的。
鉴于解锁或破坏子代中的互斥锁在技术上是不安全或合法的,我认为最好有指向互斥锁的指针,然后让孩子在堆上创建新的pthread_mutex_t并让父项的互斥量单独使用,从而拥有小内存泄漏。
唯一的问题是如何重新初始化库的状态,我正在考虑重新设置pthread_once_t。 也许是因为POSIX有一个pthread_once_t的初始化程序,它可以重置为初始状态。
#include #include #include 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。
这很hacky但也许是处理这个标准的最好方法。 如果pthread_once_t包含互斥锁,则系统必须具有从PSTREAD_ONCE_INIT状态初始化它的方法。 如果它包含指向堆上分配的互斥锁的指针,那么它将被强制分配一个新的并在pthread_once_t中设置地址。 我希望它不会使用pthread_once_t的地址来解决任何特殊问题。
在pthread_atfork()中搜索comp.programming.threads组显示了很多很好的讨论,以及POSIX标准为解决这个问题提供了多少帮助。
还有一个问题是,人们只应该从pthread_atfork()处理程序调用异步信号安全函数,并且看起来最重要的是子处理程序 ,其中只有memcpy()。
这有用吗? 有没有更好的方法来处理我们的共享库的要求?
恭喜,您发现标准存在缺陷。 pthread_atfork
从根本上无法解决用互斥体解决它所创建的问题,因为子进程中的处理程序不允许对它们执行任何操作:
一种可能的解决方法是在此处使用POSIX信号量代替互斥量。 信号量没有所有者,因此如果父进程将其锁定( sem_wait
),则父进程和子进程都可以解锁( sem_post
)其各自的副本,而不会调用任何未定义的行为。
作为一个好的方面, sem_post
是异步信号安全的,因此对孩子来说绝对合法。
我认为这是调用fork()的程序中的一个错误。 在multithreading进程中,子进程应仅调用异步信号安全函数。 如果程序想要在没有exec的情况下进行分叉,那么它应该在创建线程之前执行。
对于线程fork()/ pthread_atfork(),没有一个很好的解决方案。 它的一些块似乎可以工作,但这不是可移植的,并且可能会破坏OS版本。
需要了解更多c/c++开发分享如何使用pthread_atfork()和pthread_once()重新初始化子进程中的互斥锁,也可以关注C/ C++技术分享栏目—计算机技术网(www.ctvol.com)!
以上就是c/c++开发分享如何使用pthread_atfork()和pthread_once()重新初始化子进程中的互斥锁相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/980417.html