我正在使用共享的、强大的 pthread 互斥体来保护我的应用程序中的一些共享内存区域。由于我的程序是由 systemd 服务管理的,因此它可能会重新启动,在这种情况下,共享内存段已经存在,并且它将包含一个有效的初始化互斥体。强大的互斥体会检测锁定线程是否崩溃,但如果崩溃的线程没有持有它,我找不到有关双重初始化的任何内容:我应该如何初始化可能已经初始化的互斥体?
据我了解,在已初始化的互斥体上调用
pthread_mutex_init()
是 UB,仅当互斥体在崩溃时被保持时,pthread_mutex_lock()
才会返回错误 EOWNERDEAD,但如果互斥体尚未初始化,它也会返回 EINVAL。那么,我应该尝试在启动时锁定互斥锁,并检查它是否返回 EINVAL (仅当返回时,我才初始化它)?
编辑会简单地用
memset(ptr, 0, size)
重置所有内存,有效地破坏互斥锁,然后让我重新初始化它?还是会在内核中留下一些痕迹,泄漏资源?
编辑2 完整的场景是这样的:我有一个父进程和两个子进程,全部由 systemd 管理。父亲负责打开共享内存区域(每个孩子一个)并在每个区域中放置一个互斥体。由于这将在边缘设备上运行,因此在正常运行期间它将干净地退出并关闭机器,因此不会出现任何问题。但我需要保持它运行,既是出于可用性原因,也是因为父进程是检测何时应该关闭机器的进程(通过读取外部信号),因此在 systemd 服务中我设置为始终重新启动它。对于孩子们来说,这不是问题,因为他们不是拥有初始化互斥锁的代码的人,他们只是打开共享区域。如果父亲重新启动,它们也会重新启动(不是强制性的,但很有用)。然而,这样一来,如果父服务重新启动,内存区域仍然存在,并且带有互斥锁,父服务将执行尝试初始化互斥锁的代码。
我应该如何初始化可能已经初始化的互斥锁?
没有安全的方法可以做到这一点。 您需要知道是否初始化。
由于“父亲”独自负责设置共享内存和互斥锁,因此合理的方法是它每次启动时都从头开始执行此操作。 至少有两种好方法可以做到这一点:
如果父亲负责启动孩子本身,那么它可以使用匿名共享内存,该内存仅在至少一个进程保持打开状态时持续存在,并且对于父亲的独立运行不可见。 然而,它的子级将通过
fork
继承它,并且默认情况下,也会通过 exec
继承它。 通过这种方法,父亲不需要关心互斥体是否已经初始化,因为它总是需要创建一个新的。
父亲启动时可以检测共享内存是否已经存在,如果存在则也
这种方法存在潜在的时序问题,因为在父亲初始化互斥体之前,孩子们不得尝试访问互斥体。 但如果这是重新启动时的问题,那么它也是first启动时的问题。