您当前的位置:五五电子网电子知识单片机-工控设备嵌入式系统-技术Linux下的串口驱动的设计层次及接口 正文
Linux下的串口驱动的设计层次及接口

Linux下的串口驱动的设计层次及接口

点击数:7246 次   录入时间:03-04 11:49:30   整理:http://www.55dianzi.com   嵌入式系统-技术

    由上即可知,安装共享中断时,只须指定安装的中断标志位flag为SA_SHIRQ, 进入分析安装中断的处理可知,在安装时,会检测已经安装的中断是否支持共享中断,如果不支持,则新的中断安装动作失败;如果已经安装的中断支持共享中断,则还必须检测将要安装的新中断是否支持中断共享,如果不支持则安装还是会失败,如果支持则将此新的中断处理链接到此中断向量对应的中断动作处理链表当中.

    在产生中断时,共享中断向量中对应的中断处理程序链表中的每一个都会被调用,依据链表的次序来,这样处理虽然会有影响到效率,但是一般情况下中断传到用户的中断处理服务程序中时,由用户根据硬件的状态来决定是否处理中断,所以能常情况下都是立即就返回了,效率的影响不会是大的问题.

    三. Linux的软中断机制.

    前面已经简单讲过了LINUX下的硬中断处理机制,其实硬中断的处理都由LINUX底层代码具体完成了,使用者一般在处理硬中断时是相当简单的,只须要用request_irq()简单的挂上中断即可,这里我们进一步介绍一下LINUX下的软中断机制,软中断机制相比起硬中断机制稍微复杂一些,而且在LINUX内核本身应用非常的广, 它作为一种软性的异步执行机制,只有深入理解了它才能灵活的运用.

    之所以提到内核的SOFtirq机制,主要是因为在串口中断也使用了这些机制,理解了这些机制就能更加明白串口驱动一些问题, 现在先提出几个问题如下:

    前面提供到中断接收后数据,先放到flip缓冲区当中,这样让人很容易进一步想知道,中断处理的缓冲区的数据,用户进程读取串口时如何读到的?很明显中断处于内核空间,用户读取串口输入进程是在用户空间,中断缓冲区中的数据如何被处理到终端缓冲区中,供用户读取的?



www.55dianzi.com

    另外写串口时,是向终端缓冲区当中写入,那么上层的写操作如何知道下层缓冲区中的的数据是否传送完成?用户空间的写串口进程处于什么样的状态?如果是写完缓冲区就睡眠以保证高效的CPU使用率,那么何时才应该醒过来? 由谁负责醒过来?

    1. 往tq_timer任务队列中添加一项任务.

    根据以上这两个问题,我们来深入代码分析,首先看接收缓冲区中的数据如何上传, 前面已经提到过,接收中断处理完成后,会调用tty_flip_buffer_push(),这个函数完成的功能就是往一系统定义的任务队列当中加入一个任务,下面我们将详细的分析加入的任务最终是如何执行起来的.[任务:这里所讲的任务可以直接理解成为一个相应的回调函数,LINUX下术语称作tasklet]

    void tty_flip_buffer_push(struct tty_struct *tty)

    {

    if (tty->low_latency)

    flush_to_ldisc((void *) tty);

    else

    queue_task(&tty->flip.tqueue, &tq_timer);

    }

    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)

上一页  [1] [2] [3] [4] [5]  下一页


本文关键字:接口  Linux  嵌入式系统-技术单片机-工控设备 - 嵌入式系统-技术