新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > linux UART串口驱动开发文档

linux UART串口驱动开发文档

作者:时间:2012-09-06来源:网络收藏

另外写时,是向终端缓冲区当中写入,那么上层的写操作如何知道下层缓冲区中的的数据是否传送完成?用户空间的写进程处于什么样的状态?如果是写完缓冲区就睡眠以保证高效的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; i32; 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]. 软中断所依赖的执行机制.

linux操作系统文章专题:linux操作系统详解(linux不再难懂)

linux相关文章:linux教程




评论


相关推荐

技术专区

关闭