简要阐述linux系统中断关键点

        中断处理是外围设备操作的基石,大致的中断流程图如下文,关于中断控制器的文章可以看我之前发过的贴子。本文侧重阐述中断时上下半部和内核调度部分的关键点

一 、上半部

       为了提高系统的实时性,linux中把中断分为上下半部,其中上半部的处理需要很快,否则会影响系统的实时性。

        上半部采用轮询机制,每个中断的上半部是平等的,无法人为设置优先级,先来先处理(但受硬件顺序影响,响应是立即的(不可抢占),所以一旦某个中断的上半部十分繁杂,CPU就会一直卡在这里,严重影响整体的实时性。

二、 下半部

对于在中断中相对耗时的操作,可以从上半部移到下半部,如将指令放在TASKLET_SOFTIRQ中  

也可以在下半部进行延时的操作或者进行其它中断的屏蔽

延时一秒:

屏蔽:

        最重要的一点,由于中断中没有PCB(Process Control Block,进程控制块)来控制跳转,所以在上下半部中不可以使用msleep()这类函数,否则会造成内核崩溃。简单理解就是中断一旦被切换到别处就没法回来,中断上下文不是线程,不能“睡”或“切出去”等待再回来。

三、内核调度

        更复杂的操作则常放在内核调度的WORK_QUEUE中,在普通进程/线程上下文(kernel thread)中运行。优先级低于 Tasklet;属于内核工作线程,按调度策略(SCHED_NORMAL 或 SCHED_FIFO)执行。可以进行睡眠、阻塞、访问用户空间、耗时较长的操作。

再次记住这张结构图:

下面是一个关于中断的驱动代码,使用外部按键作为中断触发信号,可以参考

#include <linux/init.h>                        
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/interrupt.h> 

#ifdef CONFIG_OF
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#endif


struct key_dev{ //定义设备的类
  struct device *dev;
  const char		*name; 
  unsigned int gpio_num;
  unsigned int irq_num;	
};

irqreturn_t key_interrupt(int irqno, void *devid) 
{
  pr_info("do interrupt irqno = %d\n", irqno);
  return IRQ_HANDLED; 
}

int keys_probe(struct platform_device *pdev)
{
  int  ret = -1;
  struct key_dev *key;  //创建设备对象

  enum of_gpio_flags flags;

  key = devm_kzalloc(&pdev->dev, sizeof(*key), GFP_KERNEL); /*为设备对象分配空间
    函数 devm_kzalloc() 和kzalloc()一样都是内核内存分配函数,但是devm_kzalloc()是跟设备(device)有关的,当设备(device)被detached或者驱动(driver)卸载(unloaded)时,内存会被自动释放。
    当然,当内存不再使用时,可以使用函数devm_kfree()释放。
     而kzalloc() 必须手动释放(配对使用kfree()),但如果工程师检查不仔细,则有可能造成内存泄漏
     devm_kzalloc 有在统一设备模型的设备树记录空间,有自动释放的第二道防线,更安全。
     如 在keys_remove中 不加 devm_kfree,但再probe中途异常要加
     */
  if (!key){
      pr_err("devm_kzalloc fail \n");
      ret = -ENOMEM;
      goto err_devm_kzalloc;
   }
   key->dev = &pdev->dev;
   key->name ="lan key";
	
   key->gpio_num = of_get_named_gpio_flags(pdev->dev.of_node, "key_gpio", 0, &flags); //获取设备树节点的属性key_gpio
  if(!gpio_is_valid(key->gpio_num)){
        pr_err("gpio is not valid \n");
        goto err_of_get_named_gpio_flags;	
  }
	
  ret = devm_gpio_request(&pdev->dev,key->gpio_num, "gpio_num");  //申请gpio的使用
	    //devm是为统一设备模型里 提供的api,尽量用这一套,更统一安全
  if (ret) {
     pr_err("devm_gpio_request err\n");
     goto err_devm_gpio_request;
  }
	
  pr_info("gpio num = %d\r\n", key->gpio_num);
  key->irq_num=gpio_to_irq(key->gpio_num); //根据IO编号获取其中断号(注:这里是软件编号只是为了区分,不是硬件芯片手册上的编号)
  pr_info("irq num = %d \n", key->irq_num);

  ret = devm_request_irq(&pdev->dev, 
                    key->irq_num,  //中断号(用于区分不同中断) 
                    key_interrupt,  //中断处理函数
                    IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, //中断标志  上升沿或下降沿触发
                    key->name,  //中断名称  在cat /proc/interrupts  克看到
                    key); // 共享中断时才用(共享一中断线, 用于标识中断处理程序,中断标志需设为IRQF_SHARED)                           
  if (ret < 0) {
    pr_err("devm_request_irq fail\n");
    goto err_devm_request_irq;
  }

  pr_notice("keys_probe ok \n");
  return 0;
	
  //异常处理
err_devm_request_irq:	
  devm_gpio_free(&pdev->dev,key->gpio_num);
err_devm_gpio_request:
err_of_get_named_gpio_flags:
  devm_kfree(&pdev->dev,key);  //释放设备空间
err_devm_kzalloc:
  return ret;	
}

int keys_remove(struct platform_device *pdev)
{   
  //因devm分配的资源,在rmmod时会自动释放,故不用再加下面的释放函数
  //devm_free_irq(&pdev->dev,irq_num,NULL);	 //释放中断
  //devm_gpio_free(&pdev->dev,gpio_num); //释放gpio管脚
  printk("keys_remove  ok 1\n");    
  return 0;
}

static const struct of_device_id of_key_match[] = {
    { .compatible = "lan_key_irq", },  //匹配设备树的ID
    {},
};

MODULE_DEVICE_TABLE(of, of_key_match);

struct platform_driver keys_drv = {
   .driver = {
     .owner = THIS_MODULE,
     .name = "lan_key_irq driver" ,
     .of_match_table = of_key_match,
   },
   .probe = keys_probe,
   .remove = keys_remove,    
};

module_platform_driver(keys_drv);
MODULE_LICENSE("GPL");

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值