正确销毁命名的System V信号量

时间:2009-08-07 01:02:26

标签: multithreading ipc semaphore file-locking

我正在使用命名的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],信号量集被破坏。

(还有一层基于线程本地存储的逻辑,使锁在一个线程内递归。)

问题是:

  • 如何同步步骤[1]和[2]? (如果信号量集不存在,但是当我们计算星星时,它是由其他人创建的,所以现在创建也会失败)
  • 如何将步骤[4]与[8]同步,以便[9]不会过早地杀死我?
  • 还有其他种族条件吗?

PS:虽然POSIX信号量具有更好的API,但我认为我不能在这里描述的sem_inlink()行为中存活:

  

调用sem_open()重新创建或   重新连接到信号量是指a   sem_unlink()之后的新信号量是   调用。

所以我无法释放它们......

2 个答案:

答案 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,通常是愚蠢的自我,没有定时操作,所以我有点搞砸......

* ...自行编写自己的内核...... *

相关问题