在2010年关于Automatically release mutex on crashes in Unix问题的评论中,jilles声称:
glibc强大的互斥量之所以如此之快,是因为glibc采用了危险的捷径。当内核将互斥锁标记为“将导致EOWNERDEAD”时,不能保证互斥锁仍然存在。如果互斥锁被销毁,内存被替换为内存映射文件,该文件恰好在正确的位置包含最后一个拥有线程的ID,并且最后一个拥有线程在写入锁字之后(但在从拥有互斥锁的列表中完全删除互斥锁之前)终止,则该文件将被损坏。Solaris和will-be-FreeBSD9健壮的互斥锁速度较慢,因为它们不想冒这个风险。
我不能理解这种说法,因为销毁互斥是不合法的,除非它是解锁的(因此不在任何线程的健壮列表中)。我也找不到任何搜索这样的bug/问题的参考资料。这种说法仅仅是错误的吗?
我这么问的原因和我感兴趣的原因是,这与我自己的实现的正确性有关,这些实现是基于相同的Linux robust-mutex原语构建的。
发布于 2012-08-18 06:45:24
FreeBSD pthread开发人员David Xu对比赛的描述:http://lists.freebsd.org/pipermail/svn-src-user/2010-November/003668.html
我不认为munmap/mmap周期对于比赛是严格要求的。这块共享内存也可能被用于不同的用途。这并不常见,但却是有效的。
正如该消息中提到的,如果具有不同权限的线程访问共同的健壮互斥锁,则会产生更多的“乐趣”。因为所拥有的健壮互斥锁列表的节点在互斥锁本身中,所以具有低权限的线程可能会破坏高权限线程的列表。这很容易被利用来使高特权线程崩溃,在极少数情况下,这可能会导致高特权线程的内存被破坏。显然,Linux的强大互斥锁只为具有相同特权的线程而设计。通过将健壮的列表完全设置为线程内存中的数组,而不是链表,可以很容易地避免这种情况。
发布于 2012-08-18 02:34:38
我想我找到了这场比赛,它确实非常丑陋。它是这样的:
线程A持有健壮的互斥锁并将其解锁。基本步骤是:
问题是,在第3步和第4步之间,同一进程中的另一个线程可以获取互斥锁,然后解锁它,并(正确地)认为自己是互斥锁的最终用户,销毁并释放/munmap它。在此之后,如果进程中的任何线程创建了文件、设备或共享内存的共享映射,并且恰好分配了相同的地址,并且该位置的值恰好与仍在解锁步骤3和4之间的线程的pid匹配,那么就会出现这样的情况:如果进程被终止,内核将通过设置它认为是互斥锁所有者id的32位整数的高位来破坏映射的文件。
解决方案是在上面的步骤2和4之间保持对mmap/munmap的全局锁定,这与我对此问题的回答中描述的屏障问题的解决方案完全相同:
Can a correct fail-safe process-shared barrier be implemented on Linux?
https://stackoverflow.com/questions/11945429
复制相似问题