我正在使用命名的System V信号量来锁定OSX和Linux上所有应用程序的文件。任何定义都不是最好的API。
它似乎有效,但我无法弄清楚如何在每个人都完成之后正确地破坏信号量。
一般逻辑是这样的:
创建
[1]线程或进程尝试使用ftok()为文件创建的key_t打开信号量集。 Set包含2个信号量。 [2]如果信号量集不存在,则使用666权限创建。 [3]“Lock”(其中一个信号量)设置为释放状态(值1)。 [4]“引用计数”(同一组中的另一个信号量)递增。
锁定/解锁:
要锁定[5],线程将“锁定”信号量的值减1(使用撤消),因此等待它已经为零。要解锁[6],线程会将其递增1,从而允许其他人锁定它。
毁:
[7]尝试递减“引用计数”信号量(使用IPC_NOWAIT标志)。 [8]它的值被检查为0,如果它是[9],信号量集被破坏。
(还有一层基于线程本地存储的逻辑,使锁在一个线程内递归。)
问题是:
PS:虽然POSIX信号量具有更好的API,但我认为我不能在这里描述的sem_inlink()行为中存活:
调用sem_open()重新创建或 重新连接到信号量是指a sem_unlink()之后的新信号量是 调用。
所以我无法释放它们......
答案 0 :(得分:1)
几种方法:
首先,如果您的目标是锁定文件,那么 使用文件锁定调用 ,例如flock(2)
或fcntl(2)+F_SETLK
,而不是信号。恕我直言,这是最好的方法。
其次, 永远保持沉默 。你是对的,提案很生气,你的措辞表明新的sem客户可能随时出现。你需要一个单独的同步机制,比如一个单独的,长寿命的sem,来控制你真正关心的sem的创建/破坏。你可以变得异国情调并将其与一个专门的“等待零”(mysembuf.sem_op := 0
)驱逐舰结合起来,观察refcount sem并准备好IPC_RMID
。呸。最好只有一个持久的二进制信号量,而不需要用户提供的引用计数。
第三, 使用POSIX命名sems 。完成时忽略sem_unlink()
,而只是sem_close()
(当然sem_post()
解锁后!)。这在概念上与之前的方法类似 - 一个小的同步原语仍然存在 - 但正如您所说,一个更简单的API。您也不必处理SysV semaphores' fatal flaw。
答案 1 :(得分:0)
这是我最终做的事情(这是一个荣誉问题,我不会消失,直到我有正确的代码,无论手头的任务是否需要它)。)。
<强>创建强>
尝试用3 sems打开[1]现有sem set,如果失败,请尝试[2]创建一个。如果由于某人已经创建而无法创建,请返回[1]。这个循环最终会以打开或创建的sem退出,或者因为我无法处理的错误退出,在这种情况下我拿球并回家。 (我也有N次迭代的限制,以防万一:))。
3个sems中的一个是有效载荷,另一个是引用计数,第三个是引用计数的锁。在[2]锁定初始化为0后,锁定状态。
<强>护强>
如果sem set由[2]创建,则所有3个sems从0到1被semoped [3]。有效载荷被释放,ref count为1,锁定被释放(无撤消)。 如果由[1]打开,则获取锁定[4]( - 1),引用计数递增(+1)并释放锁定(+1)。如果此时锁定为零,则会阻止此操作。如果这个semop由于sem set在[6]被破坏而在我们等待时失败,那么保留失败并且我们一直回到[1]。这个循环也有有限的迭代次数。
<强>释放强>
获取锁[5](等待-1),ref计数递减(-1没有等待)。如果成功,那么如果ref count现在为零,则sem set将被销毁。否则[6]锁定被释放(+1)。如果锁定失败,因为sem set被破坏 - 什么都不做。
在保留和释放之间,有效载荷照常使用。
除了每组2个信号量的复杂性和开销之外,只有一个问题(现在我看到致命的缺陷:)) - 当创建者在[2]和[3]之间崩溃时。这将使所有客户死亡。我可以在linux上使用定时等待并杀死孤儿信号量,但OSX,通常是愚蠢的自我,没有定时操作,所以我有点搞砸......
* ...自行编写自己的内核...... *