Mik*_*ney 2 linux scheduler linux-kernel
我有一个关于3.10内核中printk()实现的问题.我把它称之为 local_irq_save开头.我看到它然后打电话 raw_spin_lock(&logbuf_lock).如果在此之前已经禁用了中断,那么logbuf_lock的目的是什么?是因为即使在当前CPU上禁用了中断,其他CPU仍然可以调用printk,因此需要停止写入日志缓冲区吗?
基本上我有三个问题:
我看到printk获取了logbuf_lock并写入日志缓冲区,然后尝试获取控制台信号量并释放logbuf_lock.然后在console_unlock循环内部获取logbuf_lock并禁用中断,然后释放logbuf_lock并调用控制台驱动程序,然后恢复中断.这种锁定/禁用中断序列的目的是什么?
我在printk()中看到有关日志缓冲区可能再次填满的注释,因此缓冲区可能必须再次刷新到控制台.考虑到我在上面#1中提到的所有锁定,这种情况会怎样?
如果在任何给定时刻只有1个CPU上的代码要调用printk(),那么中断是否仍然可以在SMP系统中的其他内核上处理?我也试图了解printk对中断延迟的影响.
谢谢.
一些后续行动:
你能澄清一下吗:
local_irq_save()防止本地CPU上的中断(并且还避免在使用cpu变量访问每个CPU数据时在另一个CPU上重新调度)
你的意思local_irq_save()是,只有在访问每个CPU数据时,调用才会阻止当前线程在另一个CPU上重新调度,或者它是否阻止当前线程在另一个CPU周期重新调度?在这里printk()的情况下local_irq_save()的目的是什么?我记得在LMKL上读过一个线程,说明中断的禁用是为了确保日志缓冲区中的条目顺序反映了printk()调用发生的实际顺序.
如果在此之前已经禁用了中断,那么logbuf_lock的目的是什么?是因为即使在当前CPU上禁用了中断,其他CPU仍然可以调用printk,因此需要停止写入日志缓冲区吗?
是.local_irq_save()防止本地CPU上的中断(并且还避免在使用cpu变量访问每个CPU数据时在另一个CPU上重新安排),而自旋锁防止其他CPU.
如果在任何给定时刻只有1个CPU上的代码要调用printk(),那么中断是否仍然可以在SMP系统中的其他内核上处理?
是.
我看到printk获取了logbuf_lock并写入日志缓冲区,然后尝试获取控制台信号量并释放logbuf_lock.然后在循环内部的console_unlock中获取logbuf_lock并禁用中断,然后释放logbuf_lock并调用控制台驱动程序,然后恢复中断.这种锁定/禁用中断序列的目的是什么?
要保护两件事:日志缓冲区和控制台驱动程序.logbuf_lock保护日志缓冲区,同时console_sem保护对控制台驱动程序列表和实际控制台本身的访问.
打印内核消息是一个两步过程.首先将消息放入日志缓冲区,然后将日志缓冲区发送到控制台中console_unlock().这两个步骤不需要在同一个调用中发生printk().此外,在注册/启动/恢复/ ...控制台时,可能会将日志缓冲区刷新到控制台.
将消息放入日志缓冲区后,printk()尝试获取console_sem.它甚至可以在中断上下文中执行此操作,因为down_trylock()它不会休眠.如果它获取了信号量,它可以继续将日志缓冲区内容发送到控制台.如果它没有获取信号量,控制台信号量持有者的责任就是将日志缓冲区内容发送到控制台上console_unlock().在console_unlock()发送到控制台时,可以有其他CPU呼叫printk().所以console_unlock()循环,直到没有更多的东西发送到控制台.在每个循环中,它获取指向日志缓冲区部分的指针,以发送到控制台,在其下logbuf_lock,然后,不再在其下logbuf_lock,将输出发送到控制台.在向控制台发送内容时,由于logbuf_lock没有采用,其他CPU可以继续向日志缓冲区添加内容.
我在printk()中看到有关日志缓冲区可能再次填满的注释,因此缓冲区可能必须再次刷新到控制台.考虑到我在上面#1中提到的所有锁定,这种情况会怎样?
该缓冲区可能释放后已经满了logbuf_lock,但之前up()荷兰国际集团console_sem.和logbuf_lock之前发布的up()荷兰国际集团console_sem,因为up()可能会导致唤醒,这需要采取运行队列锁,这可能会产生对优先级反转的问题printk()所采取的运行队列锁调用(提交0b5e1c5255).
有一些补丁来改变这个锁定方案:Jan Kara [PATCH 0/8 v4] printk:清理和软件锁定避免,其中包括试图无限期地避免CPU循环console_unlock()(它发生在大型系统上,记录大量的通过慢速串行控制台启动事件),将该工作移交给其他CPU; 并尝试最小化禁用中断的时间printk().
你的意思是调用local_irq_save()会阻止当前线程只在它访问每个CPU数据时在另一个CPU上重新调度,或者它是否阻止当前线程在另一个CPU周期重新调度?
后者.local_irq_save()防止在本地CPU上处理中断,这可能schedule()在从ISR返回时最终调用.schedule()也可以从其他地方调用,但是因为printk()应该可以从中断上下文中使用,所以它调用的任何东西都应该最终调用schedule()(例如:down_trylock()使用而不是down()出于这个原因).
在这里printk()的情况下local_irq_save()的目的是什么?
Jan Kara的[PATCH 3/8] printk:在调用console_trylock_for_printk()之前启用中断尝试最小化禁用中断的时间.在该补丁之前,至少出于以下原因禁用中断:
logbuf_lock保护的数据免受中断.这种情况通常使用自旋锁的IRQ变体(例如:)来解决raw_spin_lock_irqsave(),但是这里有更多的事情要在禁用中断的情况下运行.can_use_console(),来自console_trylock_for_printk(),问:我们可以在这个cpu上实际使用控制台吗?,注意:控制台驱动程序可能会假定已分配每个CPU资源.转移到另一个CPU可能会让人感到困惑.console_sem采取了down_trylock(),因此应该不会有什么问题应该中断也尝试着printk(),被抢占按住console_sem()将防止他人打印到控制台.因此,上面的补丁不再为后两种情况禁用中断,而是将它们包装在preempt_disable()/中preempt_enable(),这允许中断,但不允许抢占.
我记得在LMKL上读过一个线程,说明中断的禁用是为了确保日志缓冲区中的条目顺序反映了printk()调用发生的实际顺序.
你能分享对该线程的引用吗?您可能会注意到3.10中有一个cont缓冲区,它包含上一个换行符中的所有字符.当换行到达时,或者当一个不同的任务时,它会被刷新到"真正的"日志缓冲区printk().
在那个帖子中,除了这个摘录之外,我没有找到任何关于日志条目排序的有效关注:
好吧,我相信有人得到了DDetetccctted ed 113223 HHzz CPUCPU
其中,AFAIK,完全是虚假的.对日志缓冲区的排序是保证的logbuf_lock,在写入日志缓冲区时已经禁用了中断vprintk_emit(),并且使用锁定变量来禁用中断(raw_spin_lock_irqsave())console_unlock().因此,访问日志缓冲区不会受到其他CPU或中断的干扰.
几种情况下,日志行被分成几个printk()调用,在较新的内核中,该情况由cont缓冲区覆盖,缓冲区保存部分行,并在另一个CPU /中断干扰时刷新它们,因此日志行可能会分成几个行和它们之间有不相关的日志行,但没有日志行应该有混合输出.
剩下的另一个可能的腐败原因是,由于日志缓冲区是一个环形缓冲区,它理论上可能会溢出,这意味着会覆盖以前的消息.
该线程中的一个有效问题是及时输出日志缓冲区.这是通过console_unlock()在每次vprintk_emit()调用中尝试调用(调用控制台驱动程序)来实现的.如果无法获取控制台信号量,则消息已在日志缓冲区中,并且当前信号量所有者将其输出到控制台.
在该线程中提到的一个有趣的事情是,在提交a0f1ccfd8d之前:"lockdep:不要在printk中递归",(printk.c 之前和之后)中断在调用之前被重新启用release_console_sem()(这是之前的化身console_unlock()).显然,当启用lockdep(锁定验证器,能够检测可能的死锁和其他锁定问题以及打印诊断)时,它可能会在尝试printk时导致锁定printk().因此,调用spin_{,un}lock_irq{save,restore}()被分为禁用/启用中断和获取/释放锁,lockdep_on/off()在两者之间添加了调用,并且已经扩展了lockdep和中断的禁用以覆盖整个功能.
回到:
我看到printk获取了logbuf_lock并写入日志缓冲区,然后尝试获取控制台信号量并释放logbuf_lock.然后在循环内部的console_unlock中获取logbuf_lock并禁用中断,然后释放logbuf_lock并调用控制台驱动程序,然后恢复中断.这种锁定/禁用中断序列的目的是什么?
console_unlock()不仅可以调用vprintk_emit()它,还可以在注册新控制台时调用它,在恢复控制台时,在将具有挂起输出的CPU热插拔到控制台时,...这些位置通常启用中断.所以,console_unlock()必须考虑到这一点.
您似乎已经注意到,虽然在logbuf_lock打开console_unlock()(它调用raw_spin_lock_irqsave())时禁用了中断,但在释放lock(raw_spin_unlock())时它们(可能)不会被重新启用,它们之后才可能被重新启用(local_irq_console())call_console_drivers().我看到call_console_drivers()禁用中断调用的唯一原因是避免CPU在我们下面进行更改(控制台驱动程序可以访问每个CPU变量).
一个有趣的数据点是-rt(实时)补丁集在调用之前console_unlock()以及之前call_console_drivers()(在-rt受到保护migrate_disable()/migrate_enable(),不允许CPU迁移)之前重新启用中断console_unlock().这样做是为了最小化期间的中断延迟printk().PREEMPT_RT_FULL有利于低中断延迟.您可以在git.kernel.org的linux-stable-rt树中的printk.c上看到它,相关的补丁是printk-rt-aware.
| 归档时间: |
|
| 查看次数: |
2878 次 |
| 最近记录: |