⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 linux 2_4_x-+

📁 linux内核学习笔记 希望想看的人可以很快下载到
💻
📖 第 1 页 / 共 3 页
字号:
<tr><td><a href="/developerWorks/cn/linux/kernel/sync/index.shtml">Linux 2.4.x内核同步机制</a></td></tr>
<tr><td><a href="/developerWorks/cn/linux/kernel/startup/index.shtml">Linux启动过程综述</a></td></tr></table></td></tr></table>
<!-- End TOC-->
<!-- Start Related Content Area-->
<table border="0" cellpadding="0" cellspacing="0" width="160">
<tr><td bgcolor="#000000" height="1" width="160"><img alt="" height="1" src="/developerWorks/cn/i/c.gif" width="160" /></td></tr>
<tr><td align="center" background="/developerWorks/cn/i/bg-gold.gif" height="5">
<a class="nav" href="/developerWorks/cn/linux/index.shtml"><b>在 Linux 专区还有:</b></a></td></tr>
<tr><td bgcolor="#666666" height="1" width="160"><img alt="" height="1" src="/developerWorks/cn/i/c.gif" width="160" /></td></tr>
<tr><td align="right">
<table border="0" cellpadding="3" cellspacing="0" width="98%">
<tr><td>
<a href="/developerWorks/cn/cnedu.nsf/linux-onlinecourse-bytitle?OpenView&amp;Count=500">教程</a></td></tr>
<tr><td>
<a href="/developerWorks/cn/cntools.nsf/dw/linux-codelib-byname?OpenDocument&amp;Count=500">
工具与产品</a></td></tr>
<tr><td>
<a href="/developerWorks/cn/cntools.nsf/dw/linux-projects-byname?OpenDocument&amp;Count=500">
代码与组件</a></td></tr>
<tr><td>
<a href="/developerWorks/cn/cnpapers.nsf/linux-papers-bynewest?OpenView&amp;Count=500">
文章</a></td></tr></table></td></tr></table>
<!-- End Related dW Content Area-->
<table border="0" cellpadding="0" cellspacing="0" width="160">
<tr><td bgcolor="#000000" colspan="2" height="2" width="150"><img alt="" height="2" src="/developerWorks/cn/i/c.gif" width="160" /></td></tr>
<tr><td bgcolor="#FFFFFF" colspan="2" height="2" width="150"><img alt="" height="2" src="/developerWorks/cn/i/c.gif" width="160" /></td></tr></table>
<!-- END STANDARD SIDEBAR AREA--></td></tr></table>
<!-- START SUBTITLE AND CONTENT-->

<p><a href="#author1">杨沙洲</a> (<a href="mailto:pubb@163.net">pubb@163.net</a>)
<br /> 国防科技大学计算机学院<br /> 2002 年 6 月</p>
<blockquote>本文从Linux内核几种软中断机制相互关系和发展沿革入手,分析了这些机制的实现方法,给出了它们的基本用法。</blockquote>

<p><a name="1"><span class="atitle2">一. 软中断概况</span></a></p>
<p>软中断是利用硬件中断的概念,用软件方式进行模拟,实现宏观上的异步执行效果。很多情况下,软中断和"信号"有些类似,同时,软中断又是和硬中断相对应的,"硬中断是外部设备对CPU的中断","软中断通常是硬中断服务程序对内核的中断","信号则是由内核(或其他进程)对某个进程的中断"(《Linux内核源代码情景分析》第三章)。软中断的一种典型应用就是所谓的"下半部"(bottom half),它的得名来自于将硬件中断处理分离成"上半部"和"下半部"两个阶段的机制:上半部在屏蔽中断的上下文中运行,用于完成关键性的处理动作;而下半部则相对来说并不是非常紧急的,通常还是比较耗时的,因此由系统自行安排运行时机,不在中断服务上下文中执行。bottom half的应用也是激励内核发展出目前的软中断机制的原因,因此,我们先从bottom half的实现开始。</p>

<p><a name="2"><span class="atitle2">二. bottom half</span></a></p>
    <p>在Linux内核中,bottom half通常用"bh"表示,最初用于在特权级较低的上下文中完成中断服务的非关键耗时动作,现在也用于一切可在低优先级的上下文中执行的异步动作。最早的bottom half实现是借用中断向量表的方式,在目前的2.4.x内核中仍然可以看到:</p>
<pre>static void (*bh_base[32])(void);	/* kernel/softirq.c */</pre>    <p>系统如此定义了一个函数指针数组,共有32个函数指针,采用数组索引来访问,与此相对应的是一套函数:</p>
<pre>void init_bh(int nr,void (*routine)(void));</pre>
    <p>为第nr个函数指针赋值为routine。</p>
<pre>void remove_bh(int nr);</pre>
    <p>动作与init_bh()相反,卸下nr函数指针。</p>
<pre>void mark_bh(int nr);</pre>
    <p>标志第nr个bottom half可执行了。</p>
    <p>由于历史的原因,bh_base各个函数指针位置大多有了预定义的意义,在v2.4.2内核里有这样一个枚举:</p>
	<pre>
enum {
         TIMER_BH = 0,
         TQUEUE_BH,
         DIGI_BH,
         SERIAL_BH,
         RISCOM8_BH,
         SPECIALIX_BH,
         AURORA_BH,
         ESP_BH,
         SCSI_BH,
         IMMEDIATE_BH,
         CYCLADES_BH,
         CM206_BH,
         JS_BH,
         MACSERIAL_BH,
         ISICOM_BH
 };
 </pre>
    <p>并约定某个驱动使用某个bottom half位置,比如串口中断就约定使用SERIAL_BH,现在我们用得多的主要是TIMER_BH、TQUEUE_BH和IMMEDIATE_BH,但语义已经很不一样了,因为整个bottom half的使用方式已经很不一样了,这三个函数仅仅是在接口上保持了向下兼容,在实现上一直都在随着内核的软中断机制在变。现在,在2.4.x内核里,它用的是tasklet机制。</p>

<p><a name="3"><span class="atitle2">三. task queue</span></a></p>
    <p>在介绍tasklet之前,有必要先看看出现得更早一些的task queue机制。显而易见,原始的bottom half机制有几个很大的局限,最重要的一个就是个数限制在32个以内,随着系统硬件越来越多,软中断的应用范围越来越大,这个数目显然是不够用的,而且,每个bottom half上只能挂接一个函数,也是不够用的。因此,在2.0.x内核里,已经在用task queue(任务队列)的办法对其进行了扩充,这里使用的是2.4.2中的实现。</p>
    <p>task queue是在系统队列数据结构的基础上建成的,以下即为task queue的数据结构,定义在include/linux/tqueue.h中:</p>
	<pre>
struct tq_struct {
        struct list_head list;          /* 链表结构 */
        unsigned long sync;             /* 初识为0,入队时原子的置1,以避免重复入队 */
        void (*routine)(void *);        /* 激活时调用的函数 */
        void *data;                     /* routine(data) */
};

typedef struct list_head task_queue;
</pre>
    <p>在使用时,按照下列步骤进行:</p>
	<ol class="n01">
	<li>DECLARE_TASK_QUEUE(my_tqueue);	/* 定义一个my_tqueue,实际上就是一个以tq_struct为元素的list_head队列 */</li>
	<li>说明并定义一个tq_struct变量my_task;</li>
	<li>queue_task(&amp;my_task,&amp;my_tqueue);	/* 将my_task注册到my_tqueue中 */</li>
	<li>run_task_queue(&amp;my_tqueue);		/* 在适当的时候手工启动my_tqueue */</li>
</ol>

    <p>大多数情况下,都没有必要调用DECLARE_TASK_QUEUE()定义自己的task queue,因为系统已经预定义了三个task queue:</p>
	<ol class="n01">
	<li>tq_timer,由时钟中断服务程序启动;</li>
	<li>tq_immediate,在中断返回前以及schedule()函数中启动;</li>
	<li>tq_disk,内存管理模块内部使用。</li>
</ol>

   <p>一般使用tq_immediate就可以完成大多数异步任务了。</p>
<p>run_task_queue(task_queue *list)函数可用于启动list中挂接的所有task,可以手动调用,也可以挂接在上面提到的bottom half向量表中启动。以run_task_queue()作为bh_base[nr]的函数指针,实际上就是扩充了每个bottom half的函数句柄数,而对于系统预定义的tq_timer和tq_immediate的确是分别挂接在TQUEUE_BH和IMMEDIATE_BH上(注意,TIMER_BH没有如此使用,但TQUEUE_BH也是在do_timer()中启动的),从而可以用于扩充bottom half的个数。此时,不需要手工调用run_task_queue()(这原本就不合适),而只需调用mark_bh(IMMEDIATE_BH),让bottom half机制在合适的时候调度它。</p>

<p><a name="4"><span class="atitle2">四. tasklet</span></a></p>
    <p>由上看出,task queue以bottom half为基础;而bottom half在v2.4.x中则以新引入的tasklet为实现基础。</p>
    <p>之所以引入tasklet,最主要的考虑是为了更好的支持SMP,提高SMP多个CPU的利用率:不同的tasklet可以同时运行于不同的CPU上。在它的源码注释中还说明了几点特性,归结为一点,就是:同一个tasklet只会在一个CPU上运行。</p>
	<pre>
struct tasklet_struct
{
        struct tasklet_struct *next;	/* 队列指针 */
        unsigned long state;		/* tasklet的状态,按位操作,目前定义了两个位的含义:
		TASKLET_STATE_SCHED(第0位)或TASKLET_STATE_RUN(第1位) */
        atomic_t count;			/* 引用计数,通常用1表示disabled */
        void (*func)(unsigned long);	/* 函数指针 */
        unsigned long data;		/* func(data) */
};
</pre>
    <p>把上面的结构与tq_struct比较,可以看出,tasklet扩充了一点功能,主要是state属性,用于CPU间的同步。</p>
    <p>tasklet的使用相当简单:</p>
	<ol class="n01">
	<li>定义一个处理函数void my_tasklet_func(unsigned long);</li>
	<li>DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);	/*<br/> 定义一个tasklet结构my_tasklet,与my_tasklet_func(data)函数相关联,相当于DECLARE_TASK_QUEUE() */</li>
	<li>tasklet_schedule(&amp;my_tasklet);	/*<br/> 登记my_tasklet,允许系统在适当的时候进行调度运行,相当于queue_task(&amp;my_task,&amp;tq_immediate)和mark_bh(IMMEDIATE_BH) */</li>
</ol>

    <p>可见tasklet的使用比task queue更简单,而且,tasklet还能更好的支持SMP结构,因此,在新的2.4.x内核中,tasklet是建议的异步任务执行机制。除了以上提到的使用步骤外,tasklet机制还提供了另外一些调用接口:</p>
	<p>
DECLARE_TASKLET_DISABLED(name,function,data);		/*<br/> 和DECLARE_TASKLET()类似,不过即使被调度到也不会马上运行,必须等到enable */<br/>
tasklet_enable(struct tasklet_struct *);	/* tasklet使能 */<br/>
tasklet_disble(struct tasklet_struct *);	/* 禁用tasklet,只要tasklet还没运行,则会推迟到它被enable */<br/>
tasklet_init(struct tasklet_struct *,void (*func)(unsigned long),unsigned long);	/* 类似DECLARE_TASKLET() */<br/>
tasklet_kill(struct tasklet_struct *);		/* 清除指定tasklet的可调度位,即不允许调度该tasklet,但不做tasklet本身的清除 */</p>
    <p>前面提到过,在2.4.x内核中,bottom half是利用tasklet机制实现的,它表现在所有的bottom half动作都以一类tasklet的形式运行,这类tasklet与我们一般使用的tasklet不同。</p>
    <p>在2.4.x中,系统定义了两个tasklet队列的向量表,每个向量对应一个CPU(向量表大小为系统能支持的CPU最大个数,SMP方式下目前2.4.2为32)组织成一个tasklet链表:</p>
	<pre>
struct tasklet_head tasklet_vec[NR_CPUS] __cacheline_aligned;
struct tasklet_head tasklet_hi_vec[NR_CPUS] __cacheline_aligned;
</pre>
    <p>另外,对于32个bottom half,系统也定义了对应的32个tasklet结构:</p>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -