开发者社区> 桃子红了呐> 正文

linux中断申请之request_threaded_irq【转】

简介:
+关注继续查看

转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=21977330&id=3755609

在linux里,中断处理分为顶半(top half),底半(bottom half),在顶半里处理优先级比较高的事情,要求占用中断时间尽量的短,在处理完成后,就激活底半,有底半处理其余任务。底半的处理方式主要有soft_irq, tasklet, workqueue三种,他们在使用方式和适用情况上各有不同。soft_irq用在对底半执行时间要求比较紧急或者非常重要的场合,主要为一些subsystem用,一般driver基本上用不上。 tasklet和work queue在普通的driver里用的相对较多,主要区别是tasklet是在中断上下文执行,而work queue是在process上下文,因此可以执行可能sleep的操作。

request_threaded_irq()是Linux kernel 2.6.30 之后新加的irq handler API 

如何确定可以用到 request_threaded_irq() ? 
Linux kernel config 需要定义CONFIG_GENERIC_HARDIQS 
kernel config 才有支援threaded irq 

    Moving interrupts to threads 介绍request_threaded_irq() 的由来 
    http://lwn.net/Articles/302043/ 
    从realtime tree 移植而来,为了减少kernel 因为要等待每一个硬件中断处理的时间 
    ,就另外交给kernel thread 处理中断后续工作。 
    优点: 
        1 减少 kernel 延迟时间 
        2 避免处理中断时要分辨是在硬体中断或软体中断? 
        3 更容易为kernel 中断处理除错,可能可完全取代tasklet 
    原本的中断处理分上半部(硬体中断处理,必须关闭中断无法处理新的中断)跟下半部( 
    软体中断处理),因此上半部的硬体中断处理必须尽可能简短,让系统反应速度更快。 

    request_threaded_irq 是在将上半部的硬件中断处理缩短为只确定硬体中断来 
    自我们要处理的装置,唤醒kernel thread 执行后续中断任务。

    缺点: 
    对于非irq 中断的kernel threads ,需要在原本task_struct 新增struct 
    irqaction 多占 4/8 bytes 记忆体空间 
    linux kernel 2.6.29 之后(2.6.30)加入request_threaded_irq 


    跟传统top/bottom havles 的差异是threaded_irq 受Linux kernel system 
    的 process scheduling 控制,不会因为写错的bottom half 代码造成整个系统 
    延迟的问题。 

    也可以透过RT/non RT 跟nice 等工具调整各个thread 优先权,丢给使用率较低的 
    cpu 以及受惠于kernel 原本可以对threads 做的各种控制,包括但不限于sleep, 
    lock, allocate 新的记忆体区块。 

    受惠最大的是shared irq line 的多个中断处理。除了可以加速共享中断造成的延迟 
    ,threaded_irq 也可以降低在同一段程式码处理多个装置中断的复杂度。 

    threaded irq 在使用性上也比tasklet(接着top half 直接执行,无法sleep) 
    /workqueue(kernel context?) 等需要在top half 增加跟bottom half 连结与沟通 
    的麻烦。 

int request_threaded_irq(unsigned int irqirq_handler_t handlerirq_handler_t thread_fn, unsigned long irqflagsconst char *devname, void *dev_id

IRQF_SHARED 共享中断时,dev_id不能为空,因为释放irq时要区分哪个共享中断
irq:中断号
handler:发生中断时首先要执行的硬中断处理函数,这个函数可以通过返回 IRQ_WAKE_THREADED唤醒中断线程,也可
返回IRQ_HANDLE不执行中断线程
thread_fn : 中断线程,类似于中断下半部
后三个参数与request_irq中的一致

关于IRQF_ONESHOT, 直到线程函数执行完毕才会开启该中断

IRQF_ONESHOT:Interrupt is not reenabled after the hardirq handler finished.
    Used by threaded interrupts which need to keep the irq line disabled until
the threaded handler has been run. 这里linus在邮件列表里指明IRQF_ONESHOT 的原因
Making the IRQF_ONESHOT explicit does two things:
 - it makes people who read the code *aware* of things
 - if/when you have irq conflicts and two drivers want to attach to
the same interrupt, at least you can see directly from the source what
flags they used (and again, not have to even *think* about it).

IRQF_ONESHOT 与 IRQF_SHARED 不能同时使用
当多个设备共享中断时,由于IRQF_ONESHOT会关闭中断线程的中断,而线程一般执行时间会比较长,所以是不允许的
当hardirq函数为NULL时,必须声明IRQF_ONESHOT, 表示threadirq线程中关闭该中断,在某些情况下,这个标志会非常有用
例如:设备是低电平产生中断,而硬中断函数为NULL,如果不使用IRQF_ONESHOT,就会一直产生中断执行NULL函数,中断线程
得不到执行,声明IRQF_ONESHOT后,会执行完线程才使能该中断  

 

点击(此处)折叠或打开

  1. /* 
  2.  * gpio_irqTest.c 
  3.  * PB27 receive this signal as IRQ and make the LED linking on PB17 turn on or turn off 
  4.  * 
  5.  */ 
  6.   
  7. #include <linux/types.h> 
  8. #include <linux/kernel.h> 
  9. #include <linux/module.h> 
  10. #include <linux/init.h> 
  11. #include <linux/platform_device.h> 
  12. #include <linux/cdev.h> 
  13. #include <linux/ioctl.h> 
  14. #include <linux/fs.h> 
  15. #include <linux/gpio.h>
  16. #include <linux/delay.h>
  17. #include <linux/cdev.h> 
  18. #include <linux/interrupt.h>
  19. #include <asm/io.h> 
  20. #include <asm/io.h>
  21. #include <mach/gpio.h> 
  22. #include <mach/hardware.h> 
  23. #include <mach/board.h> 
  24. #include <mach/gpio.h> 
  25. #include <mach/at91_pio.h> 
  26. #include <mach/at91_aic.h> 
  27. #include <mach/at91_pmc.h> 
  28. void led_on() 
  29. // at91_set_gpio_output(AT91_PIN_PB17,1);
  30.     printk("led on\n");
  31.   
  32. void led_off() 
  33. // at91_set_gpio_output(AT91_PIN_PB17 ,0);
  34.     printk("led off.\n");
  35.   
  36. struct light_dev *light_devp; 
  37. int light_major = 200; 
  38.   
  39. struct light_dev 
  40.     struct cdev cdev; 
  41.     unsigned char value; 
  42. }; 
  43.   
  44.   
  45. static void io_init(void) 
  46. {
  47.     at91_set_GPIO_periph(AT91_PIN_PB27, 0);
  48.     at91_set_gpio_input(AT91_PIN_PB27, 1); 
  49.     at91_set_deglitch(AT91_PIN_PB27, 1); 
  50.   
  51.   
  52. struct gpio_irq_desc 
  53. {
  54.     int pin;
  55.     int irq; 
  56.     unsigned long flags; 
  57.     char *name; 
  58. }; 
  59.   
  60. static struct gpio_irq_desc gpio_irq={AT91_PIN_PB27, AT91_PIN_PB27,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING|IRQF_ONESHOT,"PB27"};
  61. static irqreturn_t gpio_irqhandler(int irq, void *dev_id)
  62. {
  63.     printk(KERN_INFO "In hard irq handler.\n");
  64.     return IRQ_WAKE_THREAD;
  65. }
  66. static irqreturn_t gpio_threadhandler(int irq, void *dev_id) 
  67. {
  68.     int rst;
  69.     rst = at91_get_gpio_value(gpio_irq.pin);
  70.     printk(KERN_INFO "gpio stat: %d\n", rst);
  71.     if(rst == 0){
  72.         led_on();
  73.     }else{
  74.         led_off();
  75.     }
  76.     printk(KERN_INFO "sleep 3000ms\n");
  77.     msleep(3000);
  78.     printk(KERN_INFO "awake after sleep\n");
  79.     return IRQ_HANDLED;
  80. }
  81.   
  82. int light_open(struct inode *inode,struct file *filp) 
  83.     int err; 
  84.     struct light_dev *dev;
  85.     dev = container_of(inode->i_cdev,struct light_dev,cdev); 
  86.     filp->private_data = dev;
  87.     printk(KERN_DEBUG "%s", __FUNCTION__);
  88.     io_init(); 
  89. // err = request_threaded_irq(gpio_irq.irq,gpio_irqhandler,gpio_threadhandler,gpio_irq.flags,gpio_irq.name,(void*)0);
  90.     err = request_threaded_irq(gpio_irq.irq,NULL,gpio_threadhandler,gpio_irq.flags,gpio_irq.name,(void*)0);
  91.     if(err) 
  92.     { 
  93. // free_irq(gpio_irq.irq,(void*)0);
  94.         printk(KERN_DEBUG "request irq failed.\n");
  95.         return -EBUSY; 
  96.     } 
  97.       
  98.     return 0; 
  99.   
  100. int light_release(struct inode *inode,struct file *filp) 
  101.     free_irq(gpio_irq.irq,(void*)0); 
  102.     return 0; 
  103.   
  104. int light_ioctl(struct inode *inode,struct file *filp,unsigned int cmd, unsigned long arg)
  105.     struct light_dev *dev = filp->private_data; 
  106.   
  107.     switch(cmd) 
  108.     { 
  109.         case 0: 
  110.             at91_set_gpio_output(AT91_PIN_PB19,0); 
  111.         break; 
  112.   
  113.         case 1: 
  114.           at91_set_gpio_output(AT91_PIN_PB19,1); 
  115.        led_off(); 
  116.         break; 
  117.   
  118.     default: 
  119.   
  120.             return -ENOTTY;
  121.         // break;
  122.     }
  123.     return 0;
  124.   
  125. struct file_operations light_fops = 
  126.     .owner = THIS_MODULE,
  127.     .open = light_open,
  128.     .release = light_release,
  129.     .unlocked_ioctl = light_ioctl,
  130. }; 
  131.   
  132.   
  133. static void light_setup_cdev(struct light_dev *dev,int index) 
  134.     int err,devno = MKDEV(light_major,index); 
  135.   
  136.     cdev_init(&dev->cdev,&light_fops); 
  137.     dev->cdev.owner = THIS_MODULE; 
  138.     dev->cdev.ops = &light_fops; 
  139.   
  140.     err = cdev_add(&dev->cdev,devno,1); 
  141.   
  142.     if(err) 
  143.     { 
  144.         printk(KERN_NOTICE "Error %d adding LED%d",err,index); 
  145.     }
  146.   
  147.   
  148. int __init light_init(void) 
  149.     int result; 
  150.   
  151.     dev_t dev = MKDEV(light_major,0); 
  152.     if(light_major) 
  153.     { 
  154.           
  155.         result = register_chrdev_region(dev,1,"gpio"); 
  156.     } 
  157.   
  158.     if(result < 0) 
  159.     {
  160.         printk(KERN_DEBUG "%s: register char dev failed.\n", __FUNCTION__);
  161.         return result; 
  162.     } 
  163.   
  164.     light_devp = kmalloc(sizeof(struct light_dev),GFP_KERNEL); 
  165.     if(!light_devp) 
  166.     { 
  167.         result = - ENOMEM; 
  168.         goto fail_malloc; 
  169.     } 
  170.   
  171.     memset(light_devp,0,sizeof(struct light_dev)); 
  172.     light_setup_cdev(light_devp,0); 
  173.       
  174.     printk(KERN_DEBUG "%s done\n", __FUNCTION__);
  175.     return 0; 
  176.   
  177.     fail_malloc:unregister_chrdev_region(dev,light_devp); 
  178.     return result; 
  179.       
  180.   
  181. void __exit light_cleanup(void) 
  182.     cdev_del(&light_devp->cdev); 
  183.     kfree(light_devp); 
  184.     unregister_chrdev_region(MKDEV(light_major,0),1); 
  185.   
  186. module_init(light_init); 
  187. module_exit(light_cleanup);
  188. MODULE_AUTHOR("Enzo Fang"); 
  189. MODULE_LICENSE("Dual BSD/GPL");


结论:
使用
request_threaded_irq(gpio_irq.irq,gpio_irqhandler,gpio_threadhandler,gpio_irq.flags,gpio_irq.name,(void*)0);
hardirq和thread_fn同时出现时,处理thread_fn时该中断是打开的

  err = request_threaded_irq(gpio_irq.irq,NULL,gpio_threadhandler,gpio_irq.flags,gpio_irq.name,(void*)0);
但hardirq和thread_fn只有一个存在时,处理thread_fn时,中断是关闭的









本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/5035905.html,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
使用NAT网关轻松为单台云服务器设置多个公网IP
在应用中,有时会遇到用户询问如何使单台云服务器具备多个公网IP的问题。 具体如何操作呢,有了NAT网关这个也不是难题。
35226 0
阿里云服务器ECS登录用户名是什么?系统不同默认账号也不同
阿里云服务器Windows系统默认用户名administrator,Linux镜像服务器用户名root
13918 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
14734 0
如何设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云安全组设置详细图文教程(收藏起来) 阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程。阿里云会要求客户设置安全组,如果不设置,阿里云会指定默认的安全组。那么,这个安全组是什么呢?顾名思义,就是为了服务器安全设置的。安全组其实就是一个虚拟的防火墙,可以让用户从端口、IP的维度来筛选对应服务器的访问者,从而形成一个云上的安全域。
17166 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,云吞铺子总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系统盘、创建快照、配置安全组等操作如何登录ECS云服务器控制台? 1、先登录到阿里云ECS服务器控制台 2、点击顶部的“控制台” 3、通过左侧栏,切换到“云服务器ECS”即可,如下图所示 通过ECS控制台的远程连接来登录到云服务器 阿里云ECS云服务器自带远程连接功能,使用该功能可以登录到云服务器,简单且方便,如下图:点击“远程连接”,第一次连接会自动生成6位数字密码,输入密码即可登录到云服务器上。
33477 0
使用SSH远程登录阿里云ECS服务器
远程连接服务器以及配置环境
13746 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
18709 0
4269
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载