确定内核代码的关键部分

时间:2015-11-14 16:45:51

标签: multithreading concurrency linux-kernel kernel

您好我正在编写内核代码,该代码旨在进行进程调度和多线程执行。我研究了锁定机制及其功能。关于临界区中哪种数据结构应该通过锁定(互斥/信号量/自旋锁)来保护,是否有拇指规则?

我知道在部分代码中存在并发机会的地方,我们需要锁定。但是我们如何决定,如果我们错过了测试用例并且没有抓住它们。之前我为系统调用和文件系统编写了代码,我从不关心锁定。

3 个答案:

答案 0 :(得分:1)

  

是否有关于应通过锁定保护关键部分中哪种数据结构的拇指规则?

任何对象(全局变量,结构对象的字段等),同时访问 一次访问是写访问时需要一些锁定访问纪律。

  

但是我们如何决定,如果我们错过了,测试用例怎么办?

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

对于测试,常见测试很少会发现并发问题,因为它们的可能性很低。在测试内核模块时,我建议使用Kernel Strider,它试图证明并发内存访问的正确性RaceHound,这会增加并发问题的可能性并检查它们。< / p>

答案 1 :(得分:0)

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

根据相关数据的不同,可能会有快捷快捷的快捷方式。如果它是一个简单的整数(并且整数我的意思是CPU的本机字大小,即32位cpu上不是64位),那么你可能不需要做任何锁定:如果一个线程试图写入整数,另一个同时读取它,读者将获得旧值或新值,而不是两者的混合。如果读者不关心他是否得到旧值,那么就不需要锁定。

但是,如果你要一起更新两个整数,并且读者获取一个的新值和另一个的旧值是不好的,那么你需要一个锁。另一个例子是线程是否递增整数。这通常涉及读取,添加和写入。如果一个人读取旧值,那么另一个人设法读取,添加和写入新值,然后第一个线程添加并写入新值,两者都认为他们已经增加了变量,但是不是增加两次,而是只增加一次。这需要锁定或使用原子增量原语来确保读取/修改/写入循环不会被中断。还有原子测试和设置原语,因此您可以读取一个值,对其进行一些数学运算,然后尝试将其写回,但只有当它仍然保持原始值时,写入才会成功。也就是说,如果另一个线程自您读取它以来就改变了它,那么测试和设置将失败,那么您可以丢弃新值并重新读取其他线程设置的值并尝试测试 - 并且 - 再次设置它。

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

答案 2 :(得分:0)

理想情况下,您必须在设计过程中枚举系统中可用的所有资源,相关的线程和通信,共享机制。确定每个资源的以下内容并在进行更改时维护正确的检查清单可能会有很大帮助:

  1. 资源繁忙的持续时间(资源利用率)&amp;锁类型
  2. 在该特定资源上排队的任务量(负载)&amp;优先
  3. 与资源相关的沟通类型,共享机制
  4. 与资源相关的错误条件
  5. 如果可能的话,最好有一个描述资源,利用率,锁,负载,通信/共享机制和错误的流程图。

    此过程可以帮助您确定缺失的情景/未知情况,关键部分以及识别瓶颈。

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

    1. Helgrind - 用于检测同步错误的Valgrind工具。 这有助于识别到期的数据竞争/同步问题 不正确的锁定,锁定顺序可能导致死锁和 也可能有以后影响的POSIX线程API使用不当。 请参阅:http://valgrind.org/docs/manual/hg-manual.html
    2. Locksmith - 用于确定可能出现的常见锁定错误 运行时或可能导致死锁。
    3. ThreadSanitizer - 用于检测种族歧视。应显示所有访问和&amp;锁定涉及所有访问。
    4. Sparse可以帮助列出函数获取和释放的锁,还可以识别诸如指向用户地址空间的指针和指向内核地址空间的指针之类的问题。
    5. Lockdep - 用于调试锁
    6. iotop - 用于通过监视内核输出的I / O使用信息来确定系统上进程或线程的当前I / O使用情况。
    7. LTTng - 用于追踪竞争条件并可能中断级联。 (LTT的继承者 - kprobes,tracepoint和perf功能的组合)
    8. Ftrace - 用于分析/调试延迟和性能相关问题的Linux内核跟踪器。
    9. lsoffuser可以很方便地确定具有锁定的进程和锁的类型。
    10. 分析可以帮助确定内核花费的确切时间。这可以使用perfOprofile等工具完成。 strace可以拦截/记录进程调用的系统调用以及进程接收的信号。它应显示事件的顺序和所有呼叫的返回/恢复路径。