公开学习文档

公开学习文档


其它

<h2>中断</h2> <ul> <li> <p>同步中断,由 CPU 本身产生,又称为内部中断,也称异常</p> <p>可屏蔽中断 不可屏蔽中断</p> </li> <li> <p>异步中断,由外部硬件设备产生,可以在任何时候产生,又称为中断</p> <p>故障 陷阱 终止</p> </li> </ul> <p>中断描述表,IDT(它是一个数组,下标与向量值一一对应),它包含 3 种类型的描述符:</p> <ul> <li>任务门</li> <li>中断门</li> <li>陷阱门</li> </ul> <p>linux 利用中断门处理中断,利用陷阱门处理异常。</p> <p>如果 CPL 与 DPL 不同,则需要切换栈,而且是先切换栈,再保存老的栈信息:ss 和 esp。然后再保存 eflags, cs, eip。然后跳转到中断处理程序那里。</p> <p>Page Fault 发生在内核态 ?</p> <p>中断处理程序可以抢占其它中断处理程序,也可以抢占异常处理程序;相反,异常处理程序从不抢占中断处理程序。在内核态能触发的唯一异常就是 Page Fault。</p> <p>与异常相关的内核控制路径,可以在一个 CPU 上开始,由于进程切换,而迁移到另一个 CPU 上执行。</p> <p>中断向量可以共享,比如向量 43 既分配给 USB 端口,也分配给声卡。那么共用同一个 IDT 函数吗,调用同一个 <code>do_IRQ()</code> 吗?并且 vector 还是相同的?那么怎么区分呢?</p> <h2>中断向量</h2> <p>物理 IRQ 可以分配给 32-238 范围内的任何向量(128 除外)。</p> <p>&gt; 说明:0-19 级非屏蔽中断和异常;20-31 保留</p> <p>注意:在 <code>cat /proc/interrupts</code> 看到的,是 <code>irq_desc[]</code> 中的内容,并非 IDT 对应的内容。</p> <h2>I/O APIC</h2> <p>一共有 24 条 IRQ 线,分别对应 24 项的中断重定向表,表项中是一个 64 bit 的空间,其中低 8 bit 表示 vector。设备的 IRQ 线连接到 I/O APIC上。</p> <p>中断亲和力,通过 I/O APIC 的中断重定向表来实现。</p> <p><img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=dfe2b802ff7b49f51329d189f3c4fc0f" alt="" /></p> <p>&gt; 参考文档:<a href="https://zhuanlan.zhihu.com/p/393195942">https://zhuanlan.zhihu.com/p/393195942</a></p> <h2>中断亲和性</h2> <p>&gt; 参考文档:<a href="https://blog.csdn.net/whut_gyx/article/details/8488768">https://blog.csdn.net/whut_gyx/article/details/8488768</a></p> <h2>kirqd</h2> <p>内核进程,纠正 CPU 对 IRQ 的自动分配。</p> <h2>CPL、DPL、RPL</h2> <p><a href="https://blog.csdn.net/better0332/article/details/3416749">https://blog.csdn.net/better0332/article/details/3416749</a></p> <h2>irq_desc</h2> <p>每个中断向量都有它自己的 irq_desc_t 描述符,所有这些描述符组织在一起形成 irq_desc 数组,如下图:</p> <p><img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=48332265aae5d3181eb28bb8bb2ac1ee" alt="" /></p> <p>结构中,action 是一个 list,这响应中断要调用的函数,如下图:</p> <p><img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=90d0ec166771e1d8d919314e34e48123" alt="" /></p> <p><img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=be4c94104616cbf06ee87c66e7d993f9" alt="" /></p> <p>在 <code>request_irq()</code> 中,初始化一个 <code>irqaction</code>,然后调用 <code>setup_irq()</code> 将其放到 <code>irq_desc[irq] -&amp;gt; action</code> 列表中,实现对同一个 irq 的共享。总的来看,主要是 <code>setup_irq()</code> 来负责注册 irq 的 action。</p> <h2>vector_irq</h2> <p><img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=7605e51c0770ff4244f927b34eb003fa" alt="" /></p> <p>&gt; 参考文档:<a href="https://blog.csdn.net/sinat_20184565/article/details/98095894">https://blog.csdn.net/sinat_20184565/article/details/98095894</a></p> <h2>其它</h2> <pre><code># 最多 256 个中断向量。本质上中断向量是 os 里面的统一的中断号 #define NR_VECTORS 256</code></pre> <p>发送 IPI CPU 间中断,是直接往 I/O 端口或寄存器写命令实现的。</p> <p>异常处理,是每个 vector 注册不同的处理函数;而中断处理,基本上每个 vector 都是相同的处理函数(调用 <code>do_IRQ()</code>),然后在这个统一的函数里,再根据 vector 进行不同的处理。</p> <p>触发中断门时,CPU 会自动清 eflags 的 IF 位,来禁用中断。</p> <p>init_IRQ() 初始化,从 0x20 号开始往后初始化,但避开了 0x80,因为之前已经初始化了,这里不能再覆盖:</p> <p><img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=b7c325599dd82b334e723a777dad0ac1" alt="" /></p> <h2>下半部</h2> <h2>软中断及 tasklet</h2> <ul> <li> <p>软中断 softirq(即使是同一种类型的软中断)可以并发地运行在多个 CPU 上,因此其是可重入函数。</p> </li> <li>而 tasklet 则不一样:同一种类型的是串行执行的。</li> </ul> <p>触发检查执行 softirq 的地方:</p> <ol> <li> <p>local_bh_enable</p> </li> <li> <p>do_IRQ() 退出时</p> </li> <li> <p>时钟中断 ?</p> </li> <li>完成 CALL_FUNCTION_VERCTOR 处理器间中断时,</li> </ol> <pre><code class="language-c">asmlinkage void do_softirq(void) { __u32 pending; unsigned long flags; if (in_interrupt()) // 不能在中断中执行,包括软中断。在下面 __do_softirq 中,会通过禁用中断,来增加软中断计数 return; local_irq_save(flags); // 注意:这里会保存 eflags,并且禁用 CPU 中断,对于 x86 是执行 cli 来实现禁用的 pending = local_softirq_pending(); if (pending) __do_softirq(); local_irq_restore(flags); }</code></pre> <pre><code class="language-c">asmlinkage void __do_softirq(void) { struct softirq_action *h; __u32 pending; int max_restart = MAX_SOFTIRQ_RESTART; int cpu; pending = local_softirq_pending(); local_bh_disable(); // 在 preempt 中增加软中断计数。如果不这么做,那么在下面执行 action 的时候,可能会触发新的中断,在退出时又会调用 do_softirq cpu = smp_processor_id(); restart: /* Reset the pending bitmask before enabling irqs */ local_softirq_pending() = 0; local_irq_enable(); // sti 允许 CPU 中断 h = softirq_vec; do { if (pending &amp;amp; 1) { h-&amp;gt;action(h); rcu_bh_qsctr_inc(cpu); } h++; pending &amp;gt;&amp;gt;= 1; } while (pending); local_irq_disable(); // cli 禁止中断 pending = local_softirq_pending(); if (pending &amp;amp;&amp;amp; --max_restart) // 控制每次处理的次数 goto restart; if (pending) wakeup_softirqd(); __local_bh_enable(); }</code></pre> <p>每个 CPU 都有自己的 ksoftirqd/n 内核线程,</p> <pre><code class="language-c">static int ksoftirqd(void * __bind_cpu) { set_user_nice(current, 19); current-&amp;gt;flags |= PF_NOFREEZE; set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { if (!local_softirq_pending()) schedule(); __set_current_state(TASK_RUNNING); // 激活运行后,从这里开始 while (local_softirq_pending()) { /* Preempt disable stops cpu going offline. If already offline, we'll be on wrong CPU: don't process */ preempt_disable(); // 防止抢占 if (cpu_is_offline((long)__bind_cpu)) goto wait_to_die; do_softirq(); preempt_enable(); // 允许抢占 cond_resched(); } set_current_state(TASK_INTERRUPTIBLE); } __set_current_state(TASK_RUNNING); return 0; wait_to_die: preempt_enable(); /* Wait for kthread_stop */ set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { schedule(); set_current_state(TASK_INTERRUPTIBLE); } __set_current_state(TASK_RUNNING); return 0; }</code></pre>

页面列表

ITEM_HTML