微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > Linux内核开发之中断与时钟(一)

Linux内核开发之中断与时钟(一)

时间:12-05 来源:互联网 点击:

“小王,醒醒,开始上课了,今天咱们开始讲中断,这可是高级东西,错过不补哈”我使劲推着睡梦中的小王。

“嗯?感情好啊,快点,快点”小王一听有新东西讲,像打了鸡血似的兴奋,连我都怀疑起她是不是性格中喜新厌旧。

不管那么多了,我讲我的,她厌她的…

啥叫中断?就是指cpu在执行过程中,出现了某些突发事件时CPU必须暂停执行当前的程序,转去处理突发事件,处理完毕后CPU有返回原程序被中断的位置并继续执行。

中断的分法不懂,分类就不同,向什么内外部中断,可/不可屏蔽中断…等等乱七八糟一大堆,我这里要说明的一点是按照中断入口跳转方法的不同,可分为向量中断和非向量中断。采用向量中断的CPU通常为不同的中断分配不同的中断号,当检测到某中断号的中断到来后,就自动跳转到与该中断号对应的地址执行。不同的中断号有不同的中断地址(即入口)。而非向量中断的多个中断共享一个入口地址。进入后根据软件判断中断标志来识别具体是哪个中断。也就是说,向量中断是由硬件提供中断服务程序入口地址,非向量中断由软件提供中断服务程序入口地址。

我们在后边会说到一个时钟定时器,它也是通过中断来实现的。它的原理很简单,嵌入式微处理器它接入一个时钟输入,当时钟脉冲到来时,就将目前的计数器值加1并和预先设置的计数值比较,若相等,证明计数周期满,产生定时器中断并复位目前计数器值。

  

Linux中断处理架构

设备的中断会打断内核中进程的正常调度和运行,会影响系统的性能。为了在中断执行时间尽可能短和中断处理需完成大量工作之间找到一个平衡点,Linux将中断处理程序分解成两个半部:顶半部和底半部。其中顶半部尽可能完成尽可能少的比较紧急的功能。而底半部几乎做了中断处理程序所有的事情,而且可以被新的中断打断。

在linux设备驱动中,提供了一系列函数来帮助设备实现中断的相关操作:

1)设备申请中断

int request_irq(unsigned int irq, //irq是要申请的中断号

void (*handler)(int irq, void *dev_id, struct pt_regs * *regs),//回调函数,中断发生时,系统会调用该函数,

unsigned long irqflags,

const char *devname,

void *dev_id);

其中irqflags是中断处理的属性,若设置为SA_INTERRUPT,则表示中断处理程序是快速处理程序,它被调用时屏蔽所有中断。若设置为SA_SHIRQ,则表示多个设备共享中断,dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL.

该函数返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回EBUSY表示中断已经被占用且不能共享。

2)释放中断

free_irq(unsigned int irq, void *dev_id);

3)使能和屏蔽中断

void disable_irq(int irq); //这个会立即返回

void disable_irq_nosync(int irq);//等待目前的中断处理完成再返回。

void enable_irq(int irq);

上述三个函数作用于可编程中断处理器,因此对系统内所有的CPU都生效。

void local_irq_save(unsigned long flags);//会将目前的中断状态保留在flags中

void local_irq_disable(void);//直接中断

这两个将屏蔽本CPU内的所有中断。对应的上边两个中断的方法如下

void local_irq_restore(unsigned long flags);

void local_irq_enable(void);

我们两边说了Linux系统中中断是分为顶半部和底半部的,那么在系统实现方面是具体怎样实现的呢,这主要有tasklet,工作队列,软中断:

1)tasklet:使用比较简单,如下:

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

DECLARE_TASKLET(my_tasklet, my_tasklet_function, data); //定义了一个名叫my_tasklet的tasklet并将其与处理函数绑定,而传入参数为data

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

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

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

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

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

INIT_WORK(&my_wq, (void (*)(void *))my_wq_func, NULL); //初始化工作队列并将其与处理函数绑定

同样

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top