Chinaunix首页 | 论坛 | 博客
  • 博客访问: 586938
  • 博文数量: 165
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1554
  • 用 户 组: 普通用户
  • 注册时间: 2013-10-23 22:57
个人简介

我本仁慈,奈何苍天不许

文章分类

全部博文(165)

文章存档

2018年(1)

2016年(33)

2015年(5)

2014年(34)

2013年(92)

分类: LINUX

2013-12-26 16:52:59

Linux中断机制

{//?为何要用到中断机制  

   如果没有中断,CPU就只有用轮询的方式,那样会增加CPU负担,且轮询有时间间隔,

 而中断马上打断CPU当前运行的任务,先进行中断处理后再返回原来的任务执行。响应快。

}

{//linux的中断机制是如何实现的,它和别的系统的中断机制有何区别

 相同的: 中断上下文切换,如保留现场,中断向量表跳转,调用中断处理程序,恢复现场等。

 linux 特有的: 上半部,下半部

 

 中断号                                 

    |   |   |

  ---------------

  |  中断控制器 |

  ---------------

         |

       ---------

     |上半部 |                          //如: 用 request_irq申请的 中断处理程序

     ---------   

         |  

       -----

       |   | -- HI_SOFTIRQ

       |下 | -- TIMER_SOFTIRQ

       |   | -- NET_TX_SOFTIRQ

       |半 | -- NET_RX_SOFTIRQ

       |   |

       |部 | -- TASKLET_SOFTIRQ       //用于中断推迟执行,运行于中断的上下文,不能睡眠

       |   |

       |---|

         |

   -----------------      

    |                |  --   task   (时间片调度任务  对应应用中的进程)

   |   内核 调度    |  --   workqueue   //工作队列,可用于任务的推迟执行,运行于进程上下文, 可睡眠

   |                |  --   ksoftirq   

   -----------------      

   {//--中断在linux内核源码的实现

      1. 中断号定义 

      { 

        见 arch/arm/mach-s5pc100/include/mach/irqs.h arch/arm/plat-s5p/include/plat/irqs.h

        它是在移植时结合电路图和cpu手册获得的

      }  

      2. 申请中断(注册中断号和中断处理程序)

      {

        typedef irqreturn_t (*irq_handler_t)(int, void *); 

        

int request_irq(

unsigned int irq,       //要申请的硬件中断号

irq_handler_t handler,  //是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数

unsigned long flags,    //中断属性  如IRQF_DISABLED 处理时屏蔽所有中断  IRQF_SHARED 表示共享中断 见include/linux/interrupt.h    

     const char *name,   //设置中断名称,在cat /proc/interrupts中可以看到此名称

void *dev_id)           //中断共享时会用到一般为NULL

      }  

      3. 中断号和中断处理程序 关联起来   //request_irq 后 系统自动完成,开发者很少修改到,仅做了解

      {    

         

  中断号  -->  中断描述符 irq_desc [NR_IRQS]   -->  中断服务例程 irqaction  --> handler 中断处理程序

                                               ...                          ...       

                                               -->  中断服务例程 irqaction  --> handler 中断处理程序  

//----include/linux/irq.h

struct irq_desc {   // 中断例程描述符表

        unsigned int            irq;            //该中断描述符的中断号

        struct irqaction        *action;        /* IRQ action list */   //指向该中断号对应的irqaction结构体链表

        unsigned int            status;         /* IRQ status */

}

extern struct irq_desc irq_desc[NR_IRQS];  //NR_IRQS 是中断源数目

//----include/linux/interrupt.h

struct irqaction {

        irq_handler_t handler; //指向中断服务程序

        unsigned long flags;   //中断标志

        const char *name;      //I/O设备名

        void *dev_id;          //设备标识

        struct irqaction *next;// 指向下一个描述符

        int irq;               // IRQ线

        struct proc_dir_entry *dir;//指向IRQn相关的/proc/irq/n目录的描述符

        irq_handler_t thread_fn;

        struct task_struct *thread;

        unsigned long thread_flags;

};

}

4.  使能和屏蔽中断

   disable_irq(int irq); //屏蔽指定中断号的中断

   enable_irq(int irq);  //使能某中断号的中断

   

5.  释放中断

   void free_irq(unsigned int irq,void *dev_id);  //释放指定的中断, irq表示中断号  dev_id中断共享时用,一般为NULL

   

   

6. 中断中耗时部分的推迟处理 (可选)

      推迟处理我们一般用tasklet  workqueue

      DECLARE_TASKLET (xxx_tasklet, xxx_do_tasklet, 0) 注册tasklet

      tasklet_schedule(&xxx_tasklet)  触发tasklet

         

 }

}

{//?如何使用中断

  {//---按键驱动  

   # cd  s5pc100_irq

   # insmod s5pc100_irq.ko

   按k1按键后, 终端显示 irq: interrupt 33

   

   //---s5pc100_irq

#include  

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

MODULE_LICENSE("Dual BSD/GPL");

MODULE_AUTHOR("farsight");

static int irq_major = 250;

static int irq_minor = 0;

static struct cdev irq_cdev;

void my_delay_work(unsigned long);

static DECLARE_TASKLET(tlet, my_delay_work,0); //静态创建一个tasklet , my_delay_work(函数用于处理推迟执行的事情

struct work_struct my_wq;

static struct file_operations s5pc100_irq_ops = {

.owner = THIS_MODULE,

};

static int irq_setup_cdev(struct cdev *cdev, 

struct file_operations *fops)

{

int result;

dev_t devno = MKDEV(irq_major, irq_minor);

cdev_init(cdev, fops);

cdev->owner = THIS_MODULE;

result = cdev_add(cdev, devno, 1);

if(result)

{

printk("irq: cdev add faiirq\n");

return result;

}

return 0;

}

// 中断处理程序 (对应中断的上半部)

static irqreturn_t  irqhandler(int irqno, void *dev_id)  //注意里面不能用会引起睡眠的函数 如信号量

{

printk("irq: interrupt %d\n", irqno);

  tasklet_schedule(&tlet);   //触发tasklet调度执行

  //schedule_work(&my_wq);

  printk("triger delay work schedule jiffies=%ld\n",jiffies);    //每发生一次timer interruptJiffies unsigned long)变数会被加一 ,用来纪录系统 自开机以来 的 timer interrupts

return IRQ_HANDLED;

}

void my_delay_work(unsigned long arg)

{

 printk(" used to do need more time things jiffies=%d",jiffies);

  }

static int __init s5pc100_irq_init(void)

{

int result;

dev_t devno = MKDEV(irq_major, irq_minor);

result = register_chrdev_region(devno, 1, "s5pc100_irq");

if(result)

{

printk("irq: unable to get major %d\n", irq_major);

return result;

}

result = irq_setup_cdev(&irq_cdev, &s5pc100_irq_ops);

if(result)

return result;

      //申请中断

result = request_irq(IRQ_EINT(1),  //硬件中断号    IRQ_EINT(1) = 1+ S5P_EINT_BASE1 = 1 + S5P_IRQ_OFFSET = 1+32 = 33 

                      irqhandler,  //中断处理函数

                      IRQF_DISABLED|IRQF_TRIGGER_FALLING, //IRQF_DISABLED 处理时屏蔽所有中断   见include/linux/interrupt.h

                                                          //IRQF_TRIGGER_FALLING  中断下降沿触发

                                                          //      其它 如IRQF_SHARED 表示共享中断

                      "EINT 1",   //设置中断名称,在cat /proc/interrupts 

                      NULL); 

if(result)

printk("irq: request irq %d failed!\n", IRQ_EINT(1));

printk("irq: driver instalirq, with major %d!\n", irq_major);

//INIT_WORK(&my_wq,(void *)my_delay_work);//for 2.6.20

return 0;

}

static void __exit s5pc100_irq_exit(void)               

{

dev_t devno = MKDEV(irq_major, irq_minor);

free_irq(IRQ_EINT(1), NULL);   //释放中断

cdev_del(&irq_cdev);

unregister_chrdev_region(devno, 1);

printk("irq: driver uninstalirq!\n");               

}

module_init(s5pc100_irq_init);

module_exit(s5pc100_irq_exit);

  }      

  

}

{//---时间相关

jiffies:

  全局变量jiffies用来记录自系统启动以来产生的节拍的总数      // Linux核心每隔固定周期会发出一次timer interrupt (IRQ 0)Jiffies会相应的加一

  启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值。

  一秒内时钟中断的次数等于Hz,所以jiffies一秒内增加的值也就是HZ

  

短延时:  //接近或短于一个jiffies

    udelay  mdelay 是软循环忙等待,消耗CPU时间, 这里测下来1jiffies = 4ms 所以,使用时延时小于4ms的可用它们。

    

长延时:        

    用查询jiffies的方法   如 time_after

    内核调度超时           schedule_timeout(signed long timeout)     

    基于等待队列的睡眠超时 sleep_on_timeout(wait_queue_head_t *q, long timeout)   //最后也是调用内核schedule

     

{//-----定时器例子

#cd time

#make

#insmod hello.ko

#gcc test.c

#./a.out 

   递增显示

   seconds after open /dev/hello:1

     seconds after open /dev/hello:2

     seconds after open /dev/hello:3

     

{//---time/hello.c

  atomic_t sec_count;

  struct timer_list s_timer; /*设备要使用的定时器*/

  

  /*定时器处理函数*/

  static void second_timer_handle(unsigned long arg)

  {

    mod_timer(&s_timer,jiffies + HZ); //modify a timer's timeout

    atomic_inc(&sec_count);

    printk("current jiffies is %ld\n", jiffies);

  }

  

  static int hello_open (struct inode *inode, struct file *file)

  {

    /*初始化定时器*/

    init_timer(&s_timer);

    s_timer.function = &second_timer_handle;

    s_timer.expires = jiffies + HZ;   //超时值是一个jiffies值,当jiffies值大于超时值timer->expires时,timer->function函数就会被运行

  

    add_timer(&s_timer); /*添加(注册)定时器*/

    atomic_set(&sec_count,0);//计数清0

    return 0;

  }

  ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp)

  {

     int counter; 

     counter = atomic_read(&sec_count);

     if (put_user(counter,(int *)buff))   //Write a simple value into user space.     

       return -EFAULT;

     return sizeof(unsigned int);

  }

  }

}

}

}

阅读(719) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~