Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3463
  • 博文数量: 1
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 20
  • 用 户 组: 普通用户
  • 注册时间: 2016-03-23 16:55
个人简介

热衷于嵌入式linux软件开发

文章分类
文章存档

2016年(1)

我的朋友
最近访客

分类: 嵌入式

2016-04-13 18:53:26


   linux内核的中断机制是一个非常重要的知识点,本文将针对该知识点做一个详细的介绍,分析:一、为什么要有中断;二、中断的硬件触发和连接;三、中断的处理流程;四、中断的软件编程;五、linux内核中断编程;六、linux内核对于中断的要求; 七、linux内核提出的顶半部和底半部机制;八、底半部机制之tasklet;九、底半部机制之工作队列;十、底半部机制之软中断这十个方面。并给出参考代码。
一、为什么要有中断

外设的处理速度远远慢于CPU的处理速度

如果采用轮询方式(CPU一直等待)处理外设,会降低CPU的利用率

如果采用中断方式处理外设,会大大提高CPU的利用率

    以cpu读取串口信息为例:

为了防止串口数据的丢失,不采用中断,采用轮询,CPU只能不停读取串口的信息:

如果采用中断,cpu当发现串口数据不可读时,cpu可以做其他事情,一旦串口数据准备就绪,串口会给CPU发送一个中断信号,告诉cpu,数据准备就绪,请来处理,cpu处理完毕以后,再接着执行原先被打断的任务!

 

二、中断的硬件触发和连接

外设的中断信号并不是直接输送给CPU,而是先给中断控制器,经过中断控制器的判断以后,再决定是否给CPU发送中断信号。

中断控制器的作用:

1.能够屏蔽或者使能某个中断信号

2.能够设置中断优先级

3.能够设置外设中断信号的有效触发方式

4.如果是多核,能够指定中断信号给哪个CPU发送

一旦CPU接收到了外设的中断信号之后,CPU开始进行中断处理

 

三、中断的处理流程

 

四、中断的软件编程

    不管是裸板还是带操作系统,中断的编程步骤一致:

    1. 编写异常向量表的代码

    2. 编写保护现场的代码

    3. 编写中断处理函数

    4. 编写恢复现场的代码

    不管是在裸板上(keil)还是在linux内核中,进行中断编程,1,2,4三步骤已经有内核实现,驱动开发者只需要向内核注册中断处理函数即可!

 

 

五、linux内核中断编程

内核中断编程,只需将中断处理函数注册到内核中即可,一旦中断触发,内核执行对应的中断处理函数!

向内核注册和卸载中断处理函数的方法为:

request_irq/free_irq

      函数原型:

    request_irq(unsigned int irq,

                     irq_handler_t handler,

                     unsigned long flags,

                     const char* name,

                     void *dev)

函数功能:

1.向内核申请硬件中断资源

2.然后注册硬件中断的中断处理函数到内核

参数:

irq:  硬件中断对应的软件编号,又称中断号(类似于硬件中断的身份证

号),从32开始,0~31内核保留!中断号由芯片厂家在内核源

码定义!

例如:

    第一个硬件中断XEINT0,对应的软件编号为IRQ_EINT(0);

    第十个硬件中断XEINT10,对应的软件编号为IRQ_EINT(10);

    在//arch/arm/mach-s5pv210/include/mach/irqs.h

handler: 中断处理函数

flags: 中断处理标志,flags要指定有效的中断触发方式

    IRQF_TRIGGER_FALLING

    IRQF_TRIGGER_RISING

    IRQF_TRIGGER_HIGH

    IRQF_TRIGGER_LOW
         可以做位或运算

   对于内部中断,flags0

 

name: 中断名称,将来出现在cat /proc/interrupts

dev:  给中断处理函数传递参数,不传递给NULL


中断处理函数说明:

原型:typedef irqreturn_t (* irq_handler_t)(int, void *);

返回值类型定义:typedef enum irqreturn irqreturn_t;

  

    释放中断资源和删除中断处理函数

        void free_irq(int irq,void *dev)

        irq:释放中断号资源

        dev:给中断处理函数传递的参数,切记一定要和注册是传递的参数一样

 

六、linux内核对于中断的要求

      内核要求中断处理函数执行的速度要快,不能做休眠操作以避免中断长时间占用CPU资源,导致系统的并发和响应能力受到影响

linux内核,“任务”包括进程,硬件中断,软中断

优先级的划分如下:

硬件中断>软件中断>进程

硬件中断无优先级

软中断、进程有优先级

linux内核中,中断不隶属于任何进程,不参与进程的调度

 

七、 linux内核对于以上问题提出了顶半部和底半部机制加以优化

采用顶半部和底半部机制的特点是:

(1)避免中断长时间占用CPU资源,导致系统的并发和响应能力受到影响

(2)cpu的资源能够在多个任务中共享

 

顶半部的特点:

1.本质还是中断处理函数

2.执行原先中断函数中紧急,耗时短的任务

3.不可被中断

4.登记底半部的内容,以便将来CPU去执行

 

底半部的特点:

1.本质就是延后执行的一种手段,不一定非要和顶半部配合使用

例如将来让某个事情延后执行,可以单独采用底半部机制

2.如果有中断,执行原先中断处理函数中不紧急,耗时较长的任务

3.可被别的任务中断

4.CPU在适当的时候去执行底半部

5.至于在何时何地去登记,随便!一旦被登记,CPU的资源也能够获取,底半部的

  内容立马得到执行!

6.底半部的实现方法三种:tasklet、工作队列、软中断

 

八、底半部机制之tasklet

astlet的本质仅仅是延后执行

特点:

1.tasklet本身就是基于软中断实现的;、

2.延后执行的内容都是放在tasklet的一个函数中,此函数又称延后处理函数

里面可以放不紧急,耗时较长的内容

3.tasklet的延后处理函数工作在中断上下文中,所以不能进行休眠操作

补充:中断上下文: 中断处理的过程(进入中断向量函数,保护现场,执行中断函

  数,恢复现场)

  进程上下文: 进程的创建,调度,切换,状态修改,进程抢占,休眠,销毁

 

九、底半部机制之工作队列

    特点:

1.工作队列 = 工作 +队列 = 工作的队列 = 队列中的每一个节点就是一个工作

“工作”:延后执行的任务

2.工作队列也是延后执行的一种手段

3.工作队列中每个节点代表着延后执行的某个事,这个事要放在一个函数中,此函数又称为工作节点的延后处理函数

4.工作队列中每一个节点对应的延后处理函数工作在进程上下文中,可以进行休眠操作。

5.工作队列就是为了解决tasklet的延后处理函数不能休眠的问题

将来如果发现延后处理函数中需要做休眠操作,只能采用工作队列,而不能 采用 tasklet

6.工作队列的延后处理函数的效率不如tasklet的延后处理函数,工作队列基于进程,而tasklet基于软中断,tasklet不可休眠,工作队列可以休眠

7.工作队列可以使用默认的

 

十、底半部机制之软中断

软中断的特点:

1.软中断本质也是延后执行的一种手段

2.软中断的延后处理函数可以运行在多个CPU之上

3.由于第二点,内核要求软中断的延后处理函数要具有可重入性

函数可重入性:

1.尽量避免访问全局变量

2.如果要访问全局变量,记得要进行互斥访问,缺点是大大降低了代码的执行效率

4. tasklet虽然是基于软中断实现,但是其延后处理函数只能运行在一个CPU

5. 通过第二和第四这两个特点对比,软中断的效率要比tasklet要高

6. 软中断的软件实现,不能采用insmodrmmod形式的动态加载和卸载,只能静态编译到zImage,无形加大了软中断代码实现的复杂度,就是因为麻烦,所以给你提供了tasklet机制

 

关于底半部机制的总结:

底半部机制的本质就是延后执行的手段。

tasklet基于软中断,效率高,不可休眠;

工作队列基于进程,效率不如tasklet的延后处理函数,可以休眠

 

 附例程:

点击(此处)折叠或打开

  1. /*按键中断,芯片:S5PV210,采用底半部机制之工作队列实现*/
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/irq.h>
  5. #include <linux/interrupt.h>
  6. //include/linux/input.h
  7. #include <linux/input.h> //标准按键值
  8. #include <asm/gpio.h>
  9. #include <plat/gpio-cfg.h>

  10. //声明描述按键的硬件数据结构
  11. struct btn_resource {
  12.     int gpio; //对应的GPIO编号
  13.     int irq; //对应的中断号
  14.     char *name; //按键名称
  15.     int code; //按键值
  16. };

  17. //定义初始化按键的信息
  18. static struct btn_resource btn_info[] = {
  19.     [0] = {
  20.         .gpio = S5PV210_GPH0(0),
  21.         .irq = IRQ_EINT(0),
  22.         .name = "KEY_UP",
  23.         .code = KEY_UP
  24.     },
  25.     [1] = {
  26.         .gpio = S5PV210_GPH0(1),
  27.         .irq = IRQ_EINT(1),
  28.         .name = "KEY_DOWN",
  29.         .code = KEY_DOWN
  30.     }
  31. };

  32. //延后处理函数
  33. //work指针就指向自己对应的工作节点btn_work
  34. static void btn_work_function(struct work_struct *work)
  35. {
  36.     printk("%s\n", __func__);
  37. }

  38. //定义工作节点对象
  39. static struct work_struct btn_work;

  40. //中断处理函数
  41. static irqreturn_t button_isr_handler(int irq,
  42.                                     void *dev)
  43. {
  44.     int state = 0;
  45.     //1.获取按键对应的硬件信息
  46.     struct btn_resource *pdata = dev;
  47.     //2.获取按键的状态
  48.     state = gpio_get_value(pdata->gpio);
  49.    
  50.     //登记底半部工作的延后处理函数
  51.     schedule_work(&btn_work);
  52.     
  53.     //3.打印按键的信息
  54.     printk("按键%s的状态为%s,按键值为%d\n",
  55.             pdata->name,
  56.             state?"松开":"按下",
  57.             pdata->code);
  58.     return 0;
  59. }

  60. static int btn_init(void)
  61. {
  62.     int i;
  63.     //1.申请中断资源和注册中断处理函数
  64.     for (i = 0; i < ARRAY_SIZE(btn_info); i++) {
  65.         gpio_request(btn_info[i].gpio,
  66.                         btn_info[i].name);
  67.         request_irq(btn_info[i].irq,
  68.                 button_isr_handler,
  69.         IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
  70.                 btn_info[i].name,
  71.                 &btn_info[i]);
  72.     }
  73.     //2.初始化工作节点对象
  74.     INIT_WORK(&btn_work, btn_work_function);
  75.     return 0;
  76. }

  77. static void btn_exit(void)
  78. {
  79.     int i;
  80.     //1.释放资源
  81.     for (i = 0; i < ARRAY_SIZE(btn_info); i++) {
  82.         gpio_free(btn_info[i].gpio);
  83.         free_irq(btn_info[i].irq, &btn_info[i]);
  84.     }
  85. }

  86. module_init(btn_init);
  87. module_exit(btn_exit);
  88. MODULE_LICENSE("GPL");



阅读(655) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:没有了

给主人留下些什么吧!~~