Mach内核,锁的最佳使用方法

3

我希望得到关于如何设计Mac OS X网络内核扩展特定部分以实现最快和高效的建议或指导 - 使用C语言。

描述: 我有两组TAILQ列表。一组是A类型结构体,另一组是B类型结构体。 大多数情况下,我单独处理它们,因此为每个列表都有一个lock_mtx。有时,我需要修改A,然后是B,然后是同时修改两者。它看起来像这样:

Function1:
{
Modify List or Struct A
Modify List or Struct B
Modify List or Struct A & B
Modify List or Struct B
Modify List or Struct A
}

Function2:
{
Modify List or Struct B
Modify List or Struct A & B
Modify List or Struct A
Modify List or Struct A & B
}

我对于锁的使用不是很熟悉,所以我只看到两种选择:

  1. 使用一个锁来保护这两个列表。那样会浪费资源,因为它将阻止只修改A列表的函数在只修改B列表的函数执行时执行(反之亦然)。
  2. 连续获取并释放这两个锁。这将给我:

.

   Function1:
    {
    lock A
    Modify List or Struct A
    unlock A

    lock B
    Modify List or Struct B
    unlock B

    lock A
    lock B
    Modify List or Struct A & B
    unlock B
    unlock A

    lock B
    Modify List or Struct B
    unlock B

    lock A
    Modify List or Struct A
    unlock A
    }

我认为使用这么多锁会相当昂贵。有没有更好的方法来保护A和B的交叉修改?谢谢您的建议。

如果在持有每个锁的同时不执行任何CPU密集型任务,那么考虑将锁设计为函数级别的(在开头锁定A和B,在结尾处释放它们)。此外,您是否正在使用文件锁?为什么不使用互斥量/信号量?我相信它们会更快。 - dario_ramos
5
谢谢。我对锁并不是很熟悉。我使用lck_mtx_t类型的锁,以及lck_mtx_lock(...)和lck_mtx_unlock(…)。我可以确实地将它们应用于整个函数。至于性能分析,我想最有意义的度量应该是在整个内核扩展中等待和获取锁所花费的时间总和。如何测量这个值? - Jean
2
好的,这些看起来像是典型的内核级互斥锁,所以没问题。我从未在Mac上工作过,但在Linux上我使用gprof,在Windows上使用AQTime。无论如何,任何体面的分析工具都可以测量消耗最高CPU周期的函数。如果获取和释放锁的函数不在列表顶部,则瓶颈(如果有)存在于其他地方。 - dario_ramos
3
我会遵循@dario_ramos的建议:除非你在每个锁定块之间花费了大量的时间,或者你正在调用外部调用,否则最好只在整个函数中锁定两个数据结构。另一件需要注意的事情是,如果你锁定了多个互斥锁,你必须始终按照相同的顺序锁定它们,例如先锁定A再锁定B。混合A-B和B-A将在大多数情况下导致死锁。 - pmdj
1
感谢大家的回复。dario_ramos: 是的,我决定在阅读了广泛的锁机制文档后使用lck_mtx_t。所有人:但我以前从未有过在如此时间敏感的情况下使用它们的机会,我不希望我的kext成为内核网络堆栈的瓶颈。因此,“最好锁定整个函数而不是多次获取锁”这样的建议真的很有帮助。pmjordan: 实际上,我确实对bsd函数进行外部调用。它更像是:mod. A,各种调用,mod. B等...而且我不能改变调用的顺序。 - Jean
显示剩余3条评论
1个回答

1

起初,我正在寻求与上述问题相关的设计建议。

嗯,事实证明虽然使用内核级互斥量似乎是正确的选择,但没有明确的答案。过于依赖函数的确切结构以及花费在不需要在锁定环境中执行的副作用工作的时间。

在这种情况下,由dario_ramos和pmjordan暗示的最佳答案是对可行选项进行分析并做出选择。

感谢大家的帮助。


5
我猜测对内核扩展进行分析并不是易如反掌的事情。因此,我可以先总结所有实际花费在获取锁和从用户空间死锁加载网络堆栈的纳秒时间,并以可重现的方式进行。统计上最小化等待时间的配置获胜。 - Jean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接