开发者社区> sky-heaven> 正文

linux中断申请之request_threaded_irq【转】

简介: 转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=21977330&id=3755609 在linux里,中断处理分为顶半(top half),底半(bottom half),在顶半里处理优先级比较高的事情,要求占用中断时间尽量的短,在处理完成后,就激活底半,有底半处理其余任务。
+关注继续查看

转自: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
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

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

相关文章
使用SSH远程登录阿里云ECS服务器
远程连接服务器以及配置环境
12109 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,大概有三种登录方式:
8722 0
如何设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云安全组设置详细图文教程(收藏起来) 阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程。阿里云会要求客户设置安全组,如果不设置,阿里云会指定默认的安全组。那么,这个安全组是什么呢?顾名思义,就是为了服务器安全设置的。安全组其实就是一个虚拟的防火墙,可以让用户从端口、IP的维度来筛选对应服务器的访问者,从而形成一个云上的安全域。
16922 0
windows server 2008阿里云ECS服务器安全设置
最近我们Sinesafe安全公司在为客户使用阿里云ecs服务器做安全的过程中,发现服务器基础安全性都没有做。为了为站长们提供更加有效的安全基础解决方案,我们Sinesafe将对阿里云服务器win2008 系统进行基础安全部署实战过程! 比较重要的几部分 1.
11657 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
17857 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
24433 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
19384 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,云吞铺子总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系统盘、创建快照、配置安全组等操作如何登录ECS云服务器控制台? 1、先登录到阿里云ECS服务器控制台 2、点击顶部的“控制台” 3、通过左侧栏,切换到“云服务器ECS”即可,如下图所示 通过ECS控制台的远程连接来登录到云服务器 阿里云ECS云服务器自带远程连接功能,使用该功能可以登录到云服务器,简单且方便,如下图:点击“远程连接”,第一次连接会自动生成6位数字密码,输入密码即可登录到云服务器上。
32656 0
+关注
sky-heaven
我是一个技术爱好者,喜欢分享交流技术心得
1191
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
OceanBase 入门到实战教程
立即下载
阿里云图数据库GDB,加速开启“图智”未来.ppt
立即下载
实时数仓Hologres技术实战一本通2.0版(下)
立即下载