Board logo

标题: linux UART串口驱动开发文档 04 [打印本页]

作者: samwalton    时间: 2013-8-16 10:08     标题: linux UART串口驱动开发文档 04

2. tq_timer的执行路径分析。
    tq_timer是一个双链表结构任务队列,每项任务包含一个函数指针成员, 它通过run_task_queue每次将当中的所有任务(其实是一些函数指针)全部调用一次,然后清空队列, 最终的执行tq_timer的是在中断底半的tqueue_bh 中执行,如下:
    void tqueue_bh(void)
    {
    run_task_queue(&tq_timer);
    }
    在void __init sched_init(void)当中初始化底半的向量如, tqueue_bh初始化在bh_base的TIMER_BH位置,bh_base为一结构很简单的数组,在什么位置调用什么样的了函数基本已经形成默认的习惯:
    init_bh(TIMER_BH, timer_bh);
    init_bh(TQUEUE_BH, tqueue_bh);
    init_bh(IMMEDIATE_BH, immediate_bh);
    看看init_bh相当于初始底半的服务程序,非常简单:
    void init_bh(int nr, void (*routine)(void))
    {
    bh_base[nr] = routine;
    mb();
    }
    最终真正的执行bh_base中保存的函数指针的,在bh_action()当中:
    static void bh_action(unsigned long nr)
    {
    …
    if (bh_base[nr])
    bh_base[nr]();
    …
    }
    关于这里所指出的bh_base, 我们在后面就直接称作bh,意即中断底半所做的事。
    3. tq_timer实现所依赖的tasklet.
    那么bh_action在什么时候执行呢?bh_action被初始化成bh_task_vec这32个tasklet调用的任务, 因此它的依赖机制是tasklet机制,后面将进行简单介绍。
    void __init softirq_init()
    {
    int i;
    for (i=0; i<32; i++)
    tasklet_init(bh_task_vec+i, bh_action, i);
    …
    }
    至此已经把任务队列的执行流程及原理分析完成,tasklet是须要激活的,这里我们先指出任务队列是如何激活的,在时钟中断的do_timer()当中会调用mark_bh(TIMER_BH), 来激时钟底半所依赖运行的tasklet,其中bh_task_vec的所有成员的函数指针全部指向bh_action.
    static inline void mark_bh(int nr)
    {
    tasklet_hi_schedule(bh_task_vec+nr);
    }
    tasklet_hi_schedule的功能就是往tasklet当中加入一个新的tasklet.
    4. tasklet的机制简单分析。
    讲到tasklet,我们才与我们真正要讲的softirq最近了,因为目前在软中断当中有主要的应用就是tasklet,而且在所有32个软中断中仅有限的几个软中断如下:
    enum{
    HI_SOFTIRQ=0,
    NET_TX_SOFTIRQ,
    NET_RX_SOFTIRQ,
    TASKLET_SOFTIRQ
    };
    struct softirq_action{
    void (*action)(struct softirq_action *);
    void *data;
    };
    static struct softirq_action softirq_vec[32] __cacheline_aligned; //软中断的中断向量表,实为数组。
    [1]. 初始化软中断向量。
    我们这里所要讲的,就是HI_SOFTIRQ / TASKLET_SOFTIRQ 两项,据我理解这两项根本在实现机制上一样的,之所以分开两个名字叫主要是为了将不同的功能分开,就类似于虽然同是软中断,但是各处所完成的功能不一样,所以分在两个软中断完成, 后面我们仅取其中用于执行时钟底半的任务队列HI_SOFTIRQ为例进行讲解, 而且我们不讲及多个CPU情况下的tasklet相关机制, 这两项软中断的实始化如下:
    void __init softirq_init()
    {
    …
    open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
    open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
    }
    open_softirq下所做的事相当简单, 即往软中断向量中赋值, 相当于硬中断当中的request_irq挂硬件中断:
    void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
    {
    softirq_vec[nr].data = data;
    softirq_vec[nr].action = action;
    }
    [2]. 软中断中断服务程序
    对于HI_SOFTIRQ , 相应的中断服务程序为tasklet_hi_action , 由上文所讲的初始化过程给出,这个函数目前完成的功能相当简单,它的任务就是遍历执行此中断所对应一个tasklet链表,
    NR_CPUS= 1.
    struct tasklet_head tasklet_hi_vec[NR_CPUS] __cacheline_aligned;
    [3]. 往软中断对应的tasklet链表中加入新的tasklet, 加在尾部。
    void __tasklet_hi_schedule(struct tasklet_struct *t)
    {
    …
    t->next = tasklet_hi_vec[cpu].list;
    tasklet_hi_vec[cpu].list = t;
    cpu_raise_softirq(cpu, HI_SOFTIRQ);
    …
    }
    最重要的一点是,在安装了新的tasklet后,还必须将软中断设置为激活,告诉系统有软中断须要执行了,下面一点即提到系统如何检测是否有软中断须要处理:
    #define __cpu_raise_softirq(cpu, nr) do { softirq_pending(cpu) |= 1UL 《 (nr); } while (0)
    [4]. 软中断所依赖的执行机制。
    讲到最后还没有指出软中断是如何触发执行的,其实很简单:
    在系统处理所有硬中断信号时,他们的入口是统一的,在这个入口函数当中除了执行do_IRQ()完成硬件中断的处理之外,还会执行do_softirq()来检测是否有软中断须要执行,所以软中断所依赖的是硬件中断机制;
    另外还有一个专门处理软中断内核线程ksoftirqd(),这个线程处理软中断级别是比较低的,他是一个无限LOOP不停的检测是否有软中断须要处理,如果没有则进行任务调度。
    在do_softirq()中有如下的判断,以决定是否有软中断须要执行,如果没有就直接退出,在[3]中提到的激活软中断时,要将相应软中断位置1, 软中断有32个,因此一个整型数即可以表示32个软中断,即可判断有什么样的软中断须要处理,代码如下:
    pending = softirq_pending(cpu);
    if (pending) {
    }
    …
    do { //检测32个软中断位标志中是否有为1的…
    if (pending & 1)
    h->action(h);
    h++;
    pending 》= 1;
    } while (pending);
    [4]. 软中断所依赖的执行时期问题。
    之所以将这个问题单独列开来讲,是因为他特别的重要,上面我已经讲过了软中断是依赖硬中断触发执行的,但是产生如下疑问:
    是不是一有硬中断发生就会触发软中断的执行?
    软中断的执行会不会影响到系统的性能?
    会不会影响到硬中断的处理效率?也就是说会不会导致在处理软中断时而引起硬中断无法及时响应呢?
    再看do_softirq的代码当中有如下判断:
    if (in_interrupt())
    return;
    这个条件就是能否进行软中断处理的关键条件,因此由此也可以了解到软中断是一种优先级低于硬中断的软性机制,具体来看看这个判断条件是什么:
    /*Are we in an interrupt context? Either doing bottom half
    * or hardware interrupt processing?*/
    #define in_interrupt() ({ const int __cpu = smp_processor_id();
    (local_irq_count(__cpu) + local_bh_count(__cpu) != 0); })
    /* softirq.h is sensitive to the offsets of these fields */
    typedef struct {
    unsigned int __softirq_pending;
    unsigned int __local_irq_count;
    unsigned int __local_bh_count;
    unsigned int __syscall_count;
    struct task_struct * __ksoftirqd_task; /* waitqueue is too large */
    } ____cacheline_aligned irq_cpustat_t;
    #define irq_enter(cpu,irq) (local_irq_count(cpu)++)
    #define irq_exit(cpu,irq) (local_irq_count(cpu)--)




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0