中断处理程序架构
<p>对于上半部分和下半部分之间的划分没有严格的规则,靠驱动程序开发人员自己的编程习惯来划分,不过还是有一些习惯供参考:</p>
<ul>
<li>
<p>如果该任务对时间比较敏感,将其放在上半部中执行。</p>
</li>
<li>
<p>如果该任务和硬件相关,一般放在上半部中执行。</p>
</li>
<li>
<p>如果该任务要保证不被其他中断打断,放在上半部中执行(因为这是系统关中断)。</p>
</li>
<li>其他不太紧急的任务, 一般考虑在下半部执行。</li>
</ul>
<p>通常下半部分在中断处理程序一返回就会马上运行,目前已经从最原始的BH(bottom half)衍生出BH(在2.5中去除)、软中断(softirq在2.3引入)、tasklet(在2.3引入)、工作队列(work queue在2.5引入)</p>
<h3>Linux中断编程</h3>
<ol>
<li>
<p>申请中断
在Linux设备驱动中,使用中断的设备需要先申请对应的中断</p>
<pre><code>int request_irq(unsigned int irq,
irqreturn_t (*handler)(int, void *, struct pt_regs *),
unsigned long flags,
const char *dev_name,
void *dev_id);</code></pre>
<ul>
<li>irq:请求的中断号</li>
</ul>
</li>
</ol>
<ul>
<li>
<p>handler:中断处理函数指针,中断发生时系统调用该函数。</p>
</li>
<li>
<p>flags:一个与中断管理相关的选项,如果设置成SA_INTERRUPT表示一个"快速"中断处理, 快速中断被处理时屏蔽当前处理器上的所有中断。如果设置成SA_SHIRQ表示中断可以在设备间共享。</p>
</li>
<li>
<p>dev_name:这个字串用在 /proc/interrupts来显示中断的拥有者。</p>
</li>
<li>dev_id:用作共享中断的指针。如果中断没有被共享, dev_id 可以设置为 NULL,或者指向设备的设备结构体。</li>
</ul>
<p><code>request_irq()</code>函数返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。</p>
<ol>
<li>
<p>屏蔽单个中断</p>
<pre><code>void disable_irq(int irq);
void disable_irq_nosync(int irq);</code></pre>
</li>
<li>
<p>使能单个中断
<code>void enable_irq(int irq)函数重新使能被禁止的中断</code></p>
</li>
<li>
<p>禁止所有中断</p>
<pre><code>void local_irq_save(unsigned long flags);
void local_irq_disable(void);
local_irq_save函数禁止当前处理器上所有中断, 并保存当前状态到 flags;
local_irq_disable关闭本地中断而不保存状态。</code></pre>
</li>
<li>
<p>使能所有中断</p>
<pre><code>void local_irq_restore(unsigned long flags);
void local_irq_enable(void);</code></pre>
<h3>tasklet机制</h3>
<ul>
<li>软件中断的派生,调度时机和软中断一样</li>
<li>内核中需要延迟执行的多数任务都可以用tasklet来完成</li>
<li>在中断期间运行,即使被调度多次,tasklet也只运行一次。</li>
<li>asklet把任务延迟到安全时间执行的一种方式</li>
</ul>
</li>
</ol>
<blockquote>
<p>软中断和tasklet都是运行在中断上下文中,它们与任一进程无关,没有支持的进程完成重新调度。所以软中断和tasklet不能睡眠、不能阻塞,它们的代码中不能含有导致睡眠的动作,如减少信号量、从用户空间拷贝数据或手工分配内存等。</p>
</blockquote>
<h3>工作队列机制</h3>
<ul>
<li><strong>它可以把工作推后,交由一个内核线程去执行</strong></li>
<li>该工作队列总是会在进程上下文执行</li>
<li>工作队列允许重新调度甚至是睡眠</li>
</ul>
<h3>软中断处理时机</h3>
<ol>
<li>硬中断处理完成,do_IRQ即将退出时处理</li>
<li>softirqd内核线程被唤醒后处理</li>
</ol>