📄 linux 2_4_x-+
字号:
<pre>
struct tasklet_struct bh_task_vec[32];</pre>
<p>在软中断子系统初始化时,这组tasklet的动作被初始化为bh_action(nr),而bh_action(nr)就会去调用bh_base[nr]的函数指针,从而与bottom half的语义挂钩。mark_bh(nr)被实现为调用tasklet_hi_schedule(bh_tasklet_vec+nr),在这个函数中,bh_tasklet_vec[nr]将被挂接在tasklet_hi_vec[cpu]链上(其中cpu为当前cpu编号,也就是说哪个cpu提出了bottom half的请求,则在哪个cpu上执行该请求),然后激发HI_SOFTIRQ软中断信号,从而在HI_SOFTIRQ的中断响应中启动运行。</p>
<p>tasklet_schedule(&my_tasklet)将把my_tasklet挂接到tasklet_vec[cpu]上,激发TASKLET_SOFTIRQ,在TASKLET_SOFTIRQ的中断响应中执行。HI_SOFTIRQ和TASKLET_SOFTIRQ是softirq子系统中的术语,下一节将对它做介绍。</p>
<p><a name="5"><span class="atitle2">五. softirq</span></a></p>
<p>从前面的讨论可以看出,task queue基于bottom half,bottom half基于tasklet,而tasklet则基于softirq。</p>
<p>可以这么说,softirq沿用的是最早的bottom half思想,但在这个"bottom half"机制之上,已经实现了一个更加庞大和复杂的软中断子系统。</p>
<pre>
struct softirq_action
{
void (*action)(struct softirq_action *);
void *data;
};
static struct softirq_action softirq_vec[32] __cacheline_aligned;</pre>
<p>这个softirq_vec[]仅比bh_base[]增加了action()函数的参数,在执行上,softirq比bottom half的限制更少。</p>
<p> 和bottom half类似,系统也预定义了几个softirq_vec[]结构的用途,通过以下枚举表示:</p>
<pre>
enum
{
HI_SOFTIRQ=0,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
TASKLET_SOFTIRQ
};</pre>
<p> HI_SOFTIRQ被用于实现bottom half,TASKLET_SOFTIRQ用于公共的tasklet使用,NET_TX_SOFTIRQ和NET_RX_SOFTIRQ用于网络子系统的报文收发。在软中断子系统初始化(softirq_init())时,调用了open_softirq()对HI_SOFTIRQ和TASKLET_SOFTIRQ做了初始化:</p><pre>
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
</pre> <p>open_softirq()会填充softirq_vec[nr],将action和data设为传入的参数。TASKLET_SOFTIRQ填充为tasklet_action(NULL),HI_SOFTIRQ填充为tasklet_hi_action(NULL),在do_softirq()函数中,这两个函数会被调用,分别启动tasklet_vec[cpu]和tasklet_hi_vec[cpu]链上的tasklet运行。</p>
<pre>
static inline void __cpu_raise_softirq(int cpu, int nr)
</pre> <p>这个函数用来激活软中断,实际上就是第cpu号CPU的第nr号软中断的active位置1。在do_softirq()中将判断这个active位。tasklet_schedule()和tasklet_hi_schedule()都会调用这个函数。</p>
<p>do_softirq()有4个执行时机,分别是:从系统调用中返回(arch/i386/kernel/entry.S::ENTRY(ret_from_sys_call))、从异常中返回(arch/i386/kernel/entry.S::ret_from_exception标号)、调度程序中(kernel/sched.c::schedule()),以及处理完硬件中断之后(kernel/irq.c::do_IRQ())。它将遍历所有的softirq_vec,依次启动其中的action()。需要注意的是,软中断服务程序,不允许在硬中断服务程序中执行,也不允许在软中断服务程序中嵌套执行,但允许多个软中断服务程序同时在多个CPU上并发。</p>
<p><a name="6"><span class="atitle2">六. 使用示例</span></a></p>
<p>softirq作为一种底层机制,很少由内核程序员直接使用,因此,这里的使用范例仅对其余几种软中断机制。</p>
<p><span class="atitle3">1.bottom half</span></p>
<p> 原有的bottom half用法在drivers/char/serial.c中还能看到,包括三个步骤:</p><pre>
init_bh(SERIAL_BH,do_serial_bh); //在串口设备的初始化函数rs_init()中,do_serial_bh()是处理函数
mark_bh(SERIAL_BH); //在rs_sched_event()中,这个函数由中断处理例程调用
remove_bh(SERIAL_BH); //在串口设备的结束函数rs_fini()中调用
</pre>
<p> 尽管逻辑上还是这么三步,但在do_serial_bh()函数中的动作却是启动一个task queue:run_task_queue(&tq_serial),而在rs_sched_event()中,mark_bh()之前调用的则是queue_task(...,&tq_serial),也就是说串口bottom half已经结合task queue使用了。而那些更通用一些的bottom half,比如IMMEDIATE_BH,更是必须要与task queue结合使用,而且一般情况下,task queue也很少独立使用,而是与bottom half结合,这在下一节task queue使用示例中可以清楚地看到。</p>
<p><span class="atitle3">2.task queue</span></p>
<p>一般来说,程序员很少自己定义task queue,而是结合bottom half,直接使用系统预定义的tq_immediate等,尤以tq_immediate使用最频繁。看以下代码段,节选自drivers/block/floppy.c:</p>
<pre>
static struct tq_struct floppy_tq; //定义一个tq_struct结构变量floppy_tq,不需要作其他初始化动作
static void schedule_bh( void (*handler)(void*) )
{
floppy_tq.routine = (void *)(void *) handler;
//指定floppy_tq的调用函数为handler,不需要考虑floppy_tq中的其他域
queue_task(&floppy_tq, &tq_immediate);
//将floppy_tq加入到tq_immediate中
mark_bh(IMMEDIATE_BH);
//激活IMMEDIATE_BH,由上所述可知,
这实际上将引发一个软中断来执行tq_immediate中挂接的各个函数
}
</pre>
<p> 当然,我们还是可以定义并使用自己的task queue,而不用tq_immediate,在drivers/char/serial.c中提到的tq_serial就是串口驱动自己定义的:</p>
<pre>
static DECLARE_TASK_QUEUE(tq_serial);
</pre>
<p>此时就需要自行调用run_task_queue(&tq_serial)来启动其中的函数了,因此并不常用。</p>
<p><span class="atitle3">3.tasklet</span></p>
<p> 这是比task queue和bottom half更加强大的一套软中断机制,使用上也相对简单,见下面代码段:</p>
<pre>
1: void foo_tasklet_action(unsigned long t);
2: unsigned long stop_tasklet;
3: DECLARE_TASKLET(foo_tasklet, foo_tasklet_action, 0);
4: void foo_tasklet_action(unsigned long t)
5: {
6: //do something
7:
8: //reschedule
9: if(!stop_tasklet)
10: tasklet_schedule(&foo_tasklet);
11: }
12: void foo_init(void)
13: {
14: stop_tasklet=0;
15: tasklet_schedule(&foo_tasklet);
16: }
17: void foo_clean(void)
18: {
19: stop_tasklet=1;
20: tasklet_kill(&foo_tasklet);
21: }
</pre> <p>这个比较完整的代码段利用一个反复执行的tasklet来完成一定的工作,首先在第3行定义foo_tasklet,与相应的动作函数foo_tasklet_action相关联,并指定foo_tasklet_action()的参数为0。虽然此处以0为参数,但也同样可以指定有意义的其他参数值,但需要注意的是,这个参数值在定义的时候必须是有固定值的变量或常数(如上例),也就是说可以定义一个全局变量,将其地址作为参数传给foo_tasklet_action(),例如:</p>
<pre>
int flags;
DECLARE_TASKLET(foo_tasklet,foo_tasklet_action,&flags);
void foo_tasklet_action(unsigned long t)
{
int flags=*(int *)t;
...
}
</pre>
<p>这样就可以通过改变flags的值将信息带入tasklet中。直接在DECLARE_TASKLET处填写flags,gcc会报"initializer element is not constant"错。</p>
<p>第9、10行是一种RESCHEDULE的技术。我们知道,一个tasklet执行结束后,它就从执行队列里删除了,要想重新让它转入运行,必须重新调用tasklet_schedule(),调用的时机可以是某个事件发生的时候,也可以是像这样在tasklet动作中。而这种reschedule技术将导致tasklet永远运行,因此在子系统退出时,应该有办法停止tasklet。stop_tasklet变量和tasklet_kill()就是干这个的。</p>
<p><a name="resources"><span class="atitle2">参考资料:</span></a></p>
<ol class="n01">
<li>《Linux内核源代码情景分析》,毛德操、胡希明著,2001年9月浙江大学出版社</li>
<li>《Linux内核2.4版源代码分析大全》,李善平等著,2002年1月机械工业出版社</li>
<li> 《Linux Device Drivers》,Alessandro Rubini & Jonathan Corbet,2001年8月 O'Reilly </li>
</ol>
<br/>
<!-- AUTHOR BIOS-->
<!-- Make author heading singular or plural as needed-->
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr><td><a name="author1"><span class="atitle2">作者简介</span></a><br/>杨沙洲,现为国防科技大学计算机学院博士生,主要研究领域为操作系统技术。 </td></tr></table>
<!-- END PAPER BODY--></td><td width="10"><img alt="" border="0" height="1" src="/developerWorks/cn/i/c.gif" width="10" /></td></tr></table><br clear="all" /><img alt="" border="0" height="10" src="/developerWorks/cn/i/c.gif" width="100" /><br />
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr valign="top"><td align="right" width="100%"><a href="#top">到页首</a></td><td width="5"><img alt="" border="0" height="1" src="/developerWorks/cn/i/c.gif" width="5" /></td></tr>
<tr valign="top"><td bgcolor="#000000" colspan="2"><img alt="" border="0" height="1" src="/developerWorks/cn/i/c.gif" width="100" /></td></tr>
<tr valign="top"><td bgcolor="#FFFFFF" colspan="2"><img alt="" border="0" height="8" src="/developerWorks/cn/i/c.gif" width="100" /></td></tr></table>
<table border="0" cellpadding="10" cellspacing="0" width="100%">
<tr valign="top"><td><form action="/developerWorks/cn/cnratings.nsf/RateArticle?CreateDocument" method="POST"><input name="ArticleTitle" type="HIDDEN" value="Linux 2.4.x内核软中断机制" /> <input name="Zone" type="HIDDEN" value="linux" /> <input name="RedirectURL" type="HIDDEN" value="/developerWorks/cn/thankyou/feedback-linux.html" /> <a name="rating"><b>您对这篇文章的看法如何?</b></a>
<table border="0" cellpadding="0" cellspacing="0" width="600">
<tr><td colspan="5"><img alt="" border="0" height="8" src="/developerWorks/cn/i/c.gif" width="100" /></td></tr>
<tr valign="top"><td width="16%"><input name="Rating" type="RADIO" value="5" />真棒!(5)</td><td width="20%"><input name="Rating" type="RADIO" value="4" />好材料 (4)</td><td width="24%"><input name="Rating" type="RADIO" value="3" />一般;尚可 (3)</td><td width="22%"><input name="Rating" type="RADIO" value="2" />需提高 (2)</td><td width="18%"><input name="Rating" type="RADIO" value="1" />太差! (1)</td></tr></table><br /><b>建议?</b><br /><textarea cols="60" name="Comments" rows="5" wrap="virtual"></textarea><br /><br /><input type="SUBMIT" value="提交反馈意见" /></form></td></tr>
<tr valign="top"><td bgcolor="#FFFFFF"><img alt="" border="0" height="8" src="/developerWorks/cn/i/c.gif" width="100" /></td></tr></table>
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tr><td align="right">(c) Copyright IBM Corp. 2001, (c) Copyright IBM China 2001, All Right Reserved</td></tr>
<tr valign="top">
<td height="21" class="bbg"> <a href="/developerWorks/cn/cgi-bin/click.cgi?url=www-900.ibm.com/cn/ibm/index.shtml&origin=dwhead" class="mainlink">关于 IBM</a><span class="divider"> | </span><a href="/developerWorks/cn/cgi-bin/click.cgi?url=www-900.ibm.com/cn/ibm/privacy/index.shtml&origin=dwhead" class="mainlink">隐私条约</a><span class="divider"> | </span><a href="/developerWorks/cn/cgi-bin/click.cgi?url=www-900.ibm.com/cn/ibm/legal/index.shtml&origin=dwhead" class="mainlink">法律条款</a><span class="divider"> | </span><a href="/developerWorks/cn/cgi-bin/click.cgi?url=www-900.ibm.com/cn/ibm/contact/index.shtml&origin=dwhead" class="mainlink">联系 IBM</a></td>
</tr></table>
<script type="text/javascript" language="JavaScript1.2" src="//www.ibm.com/common/stats/stats.js"></script>
<noscript><img src="//i.ihost.com/i/c.gif" width="1" height="1" alt="" border="0" /></noscript></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -