Multithreading 确定内核代码的关键部分

Multithreading 确定内核代码的关键部分,multithreading,concurrency,linux-kernel,kernel,Multithreading,Concurrency,Linux Kernel,Kernel,嗨,我正在写内核代码,打算做进程调度和多线程执行。我研究了锁定机制及其功能。对于关键部分中哪种类型的数据结构应该通过锁(互斥锁/信号量/自旋锁)进行保护,是否有一个经验法则 我知道,只要部分代码有可能并发,我们就需要锁。但是我们如何决定,如果我们错过了测试用例,而测试用例没有抓住它们呢。早些时候,我为系统调用和文件系统编写了代码,在这些系统中,我从来都不在乎锁 在访问任何共享数据的任何代码的持续时间内获取一个锁总是安全的,但这很慢,因为这意味着一次只有一个线程可以运行大量代码 不过,根据所讨论的

嗨,我正在写内核代码,打算做进程调度和多线程执行。我研究了锁定机制及其功能。对于关键部分中哪种类型的数据结构应该通过锁(互斥锁/信号量/自旋锁)进行保护,是否有一个经验法则

我知道,只要部分代码有可能并发,我们就需要锁。但是我们如何决定,如果我们错过了测试用例,而测试用例没有抓住它们呢。早些时候,我为系统调用和文件系统编写了代码,在这些系统中,我从来都不在乎锁

在访问任何共享数据的任何代码的持续时间内获取一个锁总是安全的,但这很慢,因为这意味着一次只有一个线程可以运行大量代码

不过,根据所讨论的数据,可能会有安全快速的快捷方式。如果它是一个简单的整数(整数是指CPU的本机字大小,即不是32位CPU上的64位),那么您可能不需要执行任何锁定:如果一个线程尝试写入整数,而另一个线程同时读取该整数,读取器将获得旧值或新值,而不是两者的混合。如果读者不关心他是否得到了旧值,那么就不需要锁

但是,如果您同时更新两个整数,并且读卡器无法获取其中一个的新值和另一个的旧值,那么您需要一个锁。另一个例子是,如果线程正在递增整数。这通常包括读取、添加和写入。如果一个线程读取旧值,那么另一个线程将读取、添加和写入新值,然后第一个线程添加和写入新值,这两个线程都认为变量已递增,但不是递增两次,而是只递增一次。这需要一个锁,或者使用原子增量原语来确保读/修改/写周期不会被中断。还有原子测试和设置原语,这样你可以读取一个值,对它做一些数学运算,然后尝试写回,但只有当它仍然保持原始值时,写操作才会成功。也就是说,如果另一个线程在您读取它之后更改了它,测试和设置将失败,那么您可以放弃新值,重新读取另一个线程集的值,然后尝试再次测试和设置它

指针实际上只是整数,因此,如果您设置了一个数据结构,然后将指向它的指针存储在另一个线程可以找到它的地方,那么只要在指针中存储其地址之前完全设置了结构,就不需要锁。另一个读取指针的线程(它需要确保只读取指针一次,即通过将指针存储在局部变量中,然后从那时起仅使用该变量来引用该结构)将看到新结构或旧结构,但不会看到中间状态。如果大多数线程仅通过指针读取结构,并且任何想要写入的线程都可以使用锁或原子测试和指针集来读取结构,那么这就足够了。但是,每当您想要修改结构的任何成员时,都必须将其复制到新成员,更改新成员,然后更新指针。这就是内核的RCU(读取、复制、更新)机制的工作原理

对于关键部分中哪种类型的数据结构应该通过锁定进行保护,是否有一个经验法则

任何对象(全局变量、结构对象的字段等),在一次访问为写访问时同时访问,都需要一些访问锁定规则

但是我们如何决定,如果我们错过了测试用例,而测试用例没有抓住它们呢

好的做法是对变量、结构或结构字段的每个声明进行适当的注释,这需要使用锁定规则进行访问。使用此变量的任何人都会读取此注释并编写相应的代码进行访问。内核核心和模块倾向于遵循这种策略


至于测试,普通测试很少揭示并发性问题,因为它们的概率很低。在测试内核模块时,我建议使用它,它试图证明并发内存访问的正确性,或者增加并发问题的概率并检查它们。

理想情况下,您必须在设计期间枚举系统中可用的所有资源、相关线程和通信、共享机制。确定每种资源的以下内容,并在进行更改时保持适当的检查表,会有很大帮助:

  • 资源繁忙的持续时间(资源利用率)和锁类型
  • 在特定资源(负载)和优先级上排队的任务量
  • 与资源相关的通信类型、共享机制
  • 与资源相关的错误条件
  • 如果可能,最好有一个描述资源、利用率、锁、负载、通信/共享机制和错误的流程图

    此过程可以帮助您确定缺失的场景/未知因素、关键部分以及瓶颈识别

    在上述过程的基础上,您可能还需要某些工具来帮助您进行测试/进一步分析,以排除隐藏的问题(如果有):

  • Helgrind
    -用于检测同步错误的Valgrind工具。 这有助于确定由于以下原因导致的数据争用/同步问题: 对于不正确的锁定,可能导致死锁和 此外,POSIX线程API使用不当可能会对以后产生影响。 参考:
  • 锁匠
    -用于确定在操作过程中可能出现的常见锁错误 运行时或可能导致死锁的
  • ThreadSanitarizer
    -用于检测竞争状况。应显示所有通道涉及的所有通道和锁
  • Sparse
    可以帮助您