linux 中断与时钟

  中断是指CPU在运行的时候,出现了某些突发的事件,CPU必须暂停当前的程序,去处理突发事件,处理完成之后,CPU又重新回到原程序被中断的位置,并继续执行程序。

     根据中断的来源,可分为内部中断和外部中断:内部中断来自CPU内部(软件中断指令,溢出等),外部中断来自CPU外部,有外设提出请求。

      也可以分为可屏蔽中断和不可屏蔽中断:可屏蔽中断通过屏蔽字被屏蔽,屏蔽后不再得到相应。

       也可以分为向量中断和非向量中断:采用向量中断的CPU通常为不同的中断分配不同的中断号,当检测到中断到来之后,就自动跳到与该中断对应的地址执行,不同的中断号有不同的地址。非向量中断的多个中断共享一个入口地址,进入该入口地址后再通过软件判断中断标志来识别具体是哪个中断。即: 向量中断由硬件提供中断服务程序的入口地址,非向量中断由软件提供中断服务的程序入口地址。

非向量中断服务程序的典型结构:

irq_handler()

{

...

int int_src = read_int_status();   //读硬件的中断相关寄存器

switch(int_src){    //判断中断源

 case : DEV_A

    dev_a_handler();

     break;

case : DEV_A 

    dev_a_handler();

    break;

  ...

default:

   break;

}

...

}

 

linux中断处理程序架构

    设备的中断会打断内核中的进程的正常调度和运行。因此要求中断服务程序尽可能短小。但是往往中断程序不会是很小的,需要大量时间来处理。

       为了解决这个矛盾,linux将中断处理分解为两半部:顶半部(top half)和底半部(bottom half)。

顶半部完成紧急的功能,他往往简单的读取寄存器中的中断状态,清除中断标志位,“登记”中断。即:将底半部处理程序挂到该设备的底半部执行队列中。这样顶半部的速度会很快,可以服务更多的中断请求。 中断底半部完成中断绝大部分(几乎所有)的工作。而且中断底半部可以被新的中断打断。

 

        中断顶半部和底半部的区别是:中断顶半部不可以被新的中断打断,而 中断底半部可以被打断,中断顶半部非常紧急,底半部则不是那么紧急,不在硬件中断服务程序中执行。

 

注:中断顶半部和底半部的结合改变了系统的相应能力,但是不能认为linux设备驱动的中断程序一定分为两部分,如果中断要处理的工作很少,则完全可以直接在顶半部分完成。

 

linux中断编程

1 申请和释放中断

在linux设备驱动中,使用中断的设备需要申请和释放对应的中断,分别使用内核提供的request_irq()和free_irq()函数

int request_irq(unsigned int irq,    irq_handler_t handler,

             unsigned long irqflags,      const char *devname,     void *dev_id);

 

void free_irq(unsigned int irq,   void  *dev_id);

2 使能和屏蔽中断

下列函数可以屏蔽一个中断源

void disable_irq(int irq );

void disable_irq_nosync(int irq );

void enable_irq(int irq );

注:如果在N号中断的顶半部调用disable_irq(N),会引起系统的死锁,因此只能调用disable_irq_nosymc(N)

 

3 底半部机制

linux实现底半部的机制主要有tasklet,工作队列和软中断

 tasklet 

 tasklet使用很简单,我们只需要定义tasklet 及其处理函数并将两者关联,如:

void my_tasklet_func(unsigned long );//定义了一个处理函数

DECLARE_TASKLET(my_tasklet,my_tasklet_func,data) ;

//定义了一个tasklet结构my_tasklet,与my_tasklet_func(data)函数相关联

在需要调度tasklet的时候,引用一个tasklet_schedule()函数就能使系统在适当的时候进行调度运行。tasklet_schedule(&my_tasklet);

工作队列

工作队列的使用方法和tasklet相似,如下:

struct work_struct my_wq();//定义了一个工作队列

void my_wq_func(unsigned long );// 定义了一个处理函数

通过INIT_WORK()可以初始化这个工作队列并将工作队列与处理函数绑定:

INIT_WORK(&my_wq,(void (*)) my_func,NULL);

与tasklet_schedule() 函数对应的用于调度工作队列的函数为schedule_work()

schedule_work(&my_wq);

软中断

软中断是传统的底半部处理机制,他的执行时机通常是顶半部返回的时候,tasklet是基于软中断实现的,因此也运行与软中断的上下文。

在linux中,使用softirq 结构体表征一个软中断,使用open_softirq()函数可以注册中断对应的处理函数,而raise_softirq()函数可以触发一个软中断。

 

三种机制的关联和区别:

软中断和tasklet运行于软中断的上下文,仍然属于是原子上下文的一种,而工作队列则运行于进程的上下文。因此,软中断和tasklet 处理函数的时候,不能睡眠,而工作队列处理函数则可以睡眠。

硬中断,信号和软中断的区别:

硬中断是外部设备对CPU的中断,软中断是中断底半部的一种处理机制,而信号时由内核(或者其他的进程)对某个进程的中断。

 

内核定时器

内核定时器编程

软件意义上的定时器最终依赖硬件定时器来实现,内核在时钟中断发生后执行检测定时器是否到期,到期后的定时器处理函数将作为软中断在底半部运行。实质上,时钟中断处理程序会唤起TIMET_SOFTIRQ()软中断,运行当前处理器上到期的所有定时器。

linux内核所提供的用于操作定时器的数据结构和函数如下:

   timer_list()

在linux中,一个timer_list实例对应一个定时器,如下:

struct timer_list{

struct list_head entry ;//定时器列表

unsigned 龙expires  ; // 定时器到期时间

void (*function )(unsigned long );//定时器处理函数

unsigned long data ; //作为参数被传入定时器处理函数

struct timer_base_s *base ;

...

};

 

定义一个定时器:

struct timer_listmy_timer;

2   初始化定时器

void init_timer(struct timer_list *timer);

3 增加定时器

void add_timer(struct timer_list *timer);

4  删除定时器

int del_timer(struct timer_list *timer);

5 修改定时器的expire

 intmod_timer(struct timer_list *timer ,unsigned long expires );

 

 

内核延迟

 1 短延迟

linux内核提供了如下3个函数分别进行纳秒,微秒,毫秒级延时 (忙等待)

void ndelay(unsigned long nsecs);

void udelay(unsigned long usecs);

void mdelay(unsigned long msecs);

毫秒级的延时已经很大,在内核中,最好不要用mdelay()函数,这将是无谓的耗费CPU资源,对于毫秒级的延时,linux内核提供了如下的函数:

void msleep (unsigned long millisecs);

unsigned long  msleep_interruptible (unsigned int  millisecs);

void ssleep (unsigned long seconds);

2 长延时

内核中进行延时的一个很直观的方法是比较当前的jiffies 和目标jiffies ,直到未来的jiffies 达到目标jiffies 。

 

3 睡着延时

睡着延时是比忙等待更好的方式,睡着延时在等待的时间到来之间进程处于睡眠状态,CPU被其他进程使用。

schedule_timeout()可以使当前任务睡眠指定的jiffies之后被重新调度执行,msleep ()和 msleep_interruptible()在本质上都是依靠包含了schedule_timeout()的schedule_timeout_uninterruptible()和schedule_timeout_interruptible() 实现的。实际上,schedule_timeout() 的实现原理是向系统添加一个定时器,在定时器处理函数中唤醒参数对应的进程 。

 

 

总结:

          linux的中断处理分为两个半部,顶半部处理紧急的硬件操作,底半部处理不紧急的耗时的操作,tasklet和工作队列都是调度中断底半部的良好机制,tasklet基于软中断实现。内核定时器也是依靠软中断实现。

        内核的延时可以采用忙等待和睡眠等待,为了充分利用CPU资源,使系统有更好的吞吐性能,在对延迟时间的要求不是很精确的情况下,睡眠等待通常更好,而ndelay,udelay 等忙等待机制在驱动中通常是配合硬件上短时间的延时要求。

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值