0

我正在使用命名的 System V 信号量来锁定我在 OSX 和 linux 上的所有应用程序中的文件。无论如何定义都不是最漂亮的 API。

它似乎有效,但我不太清楚在每个人都完成后如何正确销毁信号量。

一般逻辑是这样的:

创建:

[1] 线程或进程尝试使用 ftok() 为文件创建的 key_t 打开一个信号量集。Set 包含 2 个信号量。[2] 如果信号量集不存在,则使用 666 权限创建。[3] “锁定”(信号量之一)设置为释放状态(值 1)。[4] “引用计数”(同一集合中的另一个信号量)递增。

锁定/解锁:

要锁定 [5],线程将“Lock”信号量的值减 1(撤消),因此如果它已经为零,则等待。为了解锁 [6],线程将其加一,从而允许其他人锁定它。

销毁:

[7] 尝试减少“引用计数”信号量(使用 IPC_NOWAIT 标志)。[8] 检查其值为0,如果为[9],则销毁信号量集。

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

问题是:

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

PS:虽然 POSIX 信号量有更好的 API,但我认为我无法在此处描述的 sem_inlink() 行为中幸存下来:

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

所以我没有办法释放他们......

4

2 回答 2

1

几种方法:

首先,如果您的目标是锁定文件,请使用类似or的文件锁定调用,而不是信号量。恕我直言,这是最好的方法。flock(2)fcntl(2)+F_SETLK

其次,永远保持一个sem。你是对的,这个提议很活泼,而且你的措辞表明新的 sem 客户可能随时出现。您需要一个单独的同步机制,例如一个单独的、长期存在的 sem,来控制您真正关心的 sem 的创建/销毁。您可以变得奇特,并将其与专用的“等待零”(mysembuf.sem_op := 0)驱逐舰结合起来,观察 refcount sem 并准备好IPC_RMID。呸。最好只拥有一个持久的二进制信号量,而无需用户提供的引用计数。

第三,使用名为 sems 的 POSIX。忽略sem_unlink()而不是简单地sem_close()完成后(sem_post()当然是在解锁之后!)。这在概念上类似于以前的方法——一个小的同步原语仍然存在——但是,正如你所说,一个更简单的 API。此外,您不必处理SysV 信号量的致命缺陷

于 2009-08-07T04:08:37.620 回答
0

这就是我最终要做的事情(在这一点上这是一个荣誉问题,我不会离开,直到我有正确的代码,无论手头的任务是否需要它:))。

创造

尝试用 3 个 sem 打开 [1] 现有的 sem 集,如果失败尝试 [2] 创建一个。如果因为有人已经创建而无法创建,请返回 [1]。这个循环最终会退出,要么打开或创建 sem,要么因为我无法处理的错误,在这种情况下我拿球回家。(我也有 N 次迭代的限制,以防万一:))。

3 个 sem 之一是有效负载,另一个是引用计数,第三个是引用计数的锁。[2] lock 初始化为0后,锁定状态。

保留

如果 sem 集是由 [2] 创建的,则所有 3 个 sem 都从 0 到 1 semoped [3]。有效负载被释放,引用计数为 1,锁定被释放(无撤消)。如果它被 [1] 打开,则获取锁 [4] (-1),引用计数递增 (+1) 并释放锁 (+1)。如果此时 lock 为零,这将阻塞。如果此 semop 因 sem 集在我们等待时在 [6] 处被破坏而失败,则保留失败,我们将一直返回到 [1]。该循环的迭代次数也有限。

释放

获得锁 [5](-1 等待),引用计数递减(-1 无等待)。如果这成功,那么如果 ref 计数现在为零,则 sem 集被破坏。否则 [6] 锁被释放 (+1)。如果由于 sem 集被破坏而导致锁定失败 - 什么也不做。

在保留和释放之间,有效载荷照常使用。

除了每组 2 个信号量的复杂性和开销之外,只有一个问题(现在我看到了致命缺陷:))——当创建者在 [2] 和 [3] 之间崩溃时。这将使所有客户端挂起。我可以在 linux 上使用定时等待并杀死孤立的信号量,但是 OSX,通常是愚蠢的自我,没有定时操作,所以我有点搞砸了......

*...去写自己的内核什么的...*

于 2009-08-08T05:27:15.797 回答