内核驱动系列--中断和定时器
2011-10-23 18:30
253 查看
一、中断
1 概述:
Linux 的中断处理分为顶半部和底半部,顶半部完成尽可能少得的比较紧急的功能,往往只是简单的完成“登记中断”的工作,
就是就是将底半部处理程序挂到该设备的底半部处理队列中去。但是,也不能僵化的认为linux设备驱动中的中断处理一定分
为两个半部,如果中断要处理的工作本身就很少,则完全可以在顶半部全部完成。查看/proc/interrupts文件可以获得系统
中断的统计信息。
2 中断编程:
1 申请和释放中断
(1) 申请irq
int request_irq (unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname,
void *dev_id)
irq 是要申请的中断号,handler是向系统登记的中断处理函数,irq_flags是中断处理的属性,可以指定中断的触发
方式机处理方式。dev_id 在中断共享时会用到,一般设置为这个设备的结构体或者NULL.
(2) 释放irq
void free_irq (unsigned int irq, void *dev_id); 参数定义与request_irq()相同。
2 使能和屏蔽中断
(1) 屏蔽:void disable_irq (int irq);
void disable_irq_nosync (ing irq);//立即返回
void enable_irq (int irq);
#define local_irq_save (flags)//屏蔽本cpu所有
void local_irq_disable (void) //屏蔽本cpu所有中断
(2) 恢复中断
#define local_irq_restore (flags)
void local_irq_enable (void);
3 底半部机制--实现机制主要有tasklet, 工作队列 和 软中断。
(1) tasklet
void my_tasklet_func (unsigned long);
DECLARE_TASKLET (my_tasklet, my_tasklet_func, data);
/*定义一个tasklet结构my_tasklet, 与my_tasklet_func(data)函数相关联*/
tasklet_schedule (&my_tasklet);
/*使系统在适当的时候调度tasklet注册的函数*/
(2)工作队列
struct work_struct my_wq;
void my_wq_func (unsigned long);
INIT_WORK (&my_wq, (void(*)(void *))my_wq_func, NULL);
/*初始化工作队列并将其与处理函数绑定*/
schedule_work (&my_wq); /*调度工作队列执行*/
(3)软中断(与通常说的软中断(软件指令引发的中断),比如arm的swi是完全不同的概念)
在linux内核中,用softirq_action结构体表征一个软中断,这个结构体包含软中断处理函数指针和传递给该
函数的参数。使用open_softirq()函数可以注册软中断对应的处理函数,而raise_softirq()函数可以触发一
个软中断。
软中断和tasklet 运行与软中断上下文,仍属于原子上下文的一种,而工作队列则运行与进程上下文。因此,
软中断和tasklet处理函数中不能睡眠,而工作队列处理函数中允许睡眠。
local_bh_disable() 和 local_bh_enable()是内核中用于禁止和使能软中断和tasklet底半部机制的函数。
3 实例--s3c2410实时钟中断
参考linux模块,在/drivers/rtc/rtc-s3c.c 和/drivers/rtc/interface.c文件中。
二、时钟
1 概述:
软件意义上的定时器最终依赖硬件定时器来实现,内核在时钟中断发生后检测个定时器释放到期,到期后的定时器处理函数
将作为软中断底半部执行。驱动编程中,可以利用一组函数和数据结构来完成定时器触发工作或者某些周期性任务。
2 用到的数据结构和函数
(1) 一个timer_list 结构体的实例对应一个定时器,其定义如下:
struct timer_list {
struct list_head entry, /*定时器列表*/
unsigned long expires, /*定时器到期时间*/
void (*function) (unsigned long), /*定时器处理函数*/
unsigned long data,/*作为参数被传入定时器处理函数*/
struct timer_base_s *base,
...
};
实例化 struct timer_list my_timer;
(2) 初始化定时器
void init_timer (struct timer_list *timer);
TIMER_INITIALIZER (_function, _expires, _data)
DEFINE_TIMER (_name, _function, _expires, _data)
setup_timer ();
(3) 增加定时器
void add_timer (struct timer_list *timer);
(4) 删除定时器
int del_timer (struct timer_list *timer);
(5) 修改定时器的expire
int mod_timer (struct timer_list *timer, unsigned long expires);
(6) 对于周期性的任务,linux内核还提供了一种delayed_work机制来完成,本质上用工作队列和定时器实现。
3 实例--秒字符设备
second_drv.c
second_test.c
1 概述:
Linux 的中断处理分为顶半部和底半部,顶半部完成尽可能少得的比较紧急的功能,往往只是简单的完成“登记中断”的工作,
就是就是将底半部处理程序挂到该设备的底半部处理队列中去。但是,也不能僵化的认为linux设备驱动中的中断处理一定分
为两个半部,如果中断要处理的工作本身就很少,则完全可以在顶半部全部完成。查看/proc/interrupts文件可以获得系统
中断的统计信息。
2 中断编程:
1 申请和释放中断
(1) 申请irq
int request_irq (unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname,
void *dev_id)
irq 是要申请的中断号,handler是向系统登记的中断处理函数,irq_flags是中断处理的属性,可以指定中断的触发
方式机处理方式。dev_id 在中断共享时会用到,一般设置为这个设备的结构体或者NULL.
(2) 释放irq
void free_irq (unsigned int irq, void *dev_id); 参数定义与request_irq()相同。
2 使能和屏蔽中断
(1) 屏蔽:void disable_irq (int irq);
void disable_irq_nosync (ing irq);//立即返回
void enable_irq (int irq);
#define local_irq_save (flags)//屏蔽本cpu所有
void local_irq_disable (void) //屏蔽本cpu所有中断
(2) 恢复中断
#define local_irq_restore (flags)
void local_irq_enable (void);
3 底半部机制--实现机制主要有tasklet, 工作队列 和 软中断。
(1) tasklet
void my_tasklet_func (unsigned long);
DECLARE_TASKLET (my_tasklet, my_tasklet_func, data);
/*定义一个tasklet结构my_tasklet, 与my_tasklet_func(data)函数相关联*/
tasklet_schedule (&my_tasklet);
/*使系统在适当的时候调度tasklet注册的函数*/
(2)工作队列
struct work_struct my_wq;
void my_wq_func (unsigned long);
INIT_WORK (&my_wq, (void(*)(void *))my_wq_func, NULL);
/*初始化工作队列并将其与处理函数绑定*/
schedule_work (&my_wq); /*调度工作队列执行*/
(3)软中断(与通常说的软中断(软件指令引发的中断),比如arm的swi是完全不同的概念)
在linux内核中,用softirq_action结构体表征一个软中断,这个结构体包含软中断处理函数指针和传递给该
函数的参数。使用open_softirq()函数可以注册软中断对应的处理函数,而raise_softirq()函数可以触发一
个软中断。
软中断和tasklet 运行与软中断上下文,仍属于原子上下文的一种,而工作队列则运行与进程上下文。因此,
软中断和tasklet处理函数中不能睡眠,而工作队列处理函数中允许睡眠。
local_bh_disable() 和 local_bh_enable()是内核中用于禁止和使能软中断和tasklet底半部机制的函数。
3 实例--s3c2410实时钟中断
参考linux模块,在/drivers/rtc/rtc-s3c.c 和/drivers/rtc/interface.c文件中。
二、时钟
1 概述:
软件意义上的定时器最终依赖硬件定时器来实现,内核在时钟中断发生后检测个定时器释放到期,到期后的定时器处理函数
将作为软中断底半部执行。驱动编程中,可以利用一组函数和数据结构来完成定时器触发工作或者某些周期性任务。
2 用到的数据结构和函数
(1) 一个timer_list 结构体的实例对应一个定时器,其定义如下:
struct timer_list {
struct list_head entry, /*定时器列表*/
unsigned long expires, /*定时器到期时间*/
void (*function) (unsigned long), /*定时器处理函数*/
unsigned long data,/*作为参数被传入定时器处理函数*/
struct timer_base_s *base,
...
};
实例化 struct timer_list my_timer;
(2) 初始化定时器
void init_timer (struct timer_list *timer);
TIMER_INITIALIZER (_function, _expires, _data)
DEFINE_TIMER (_name, _function, _expires, _data)
setup_timer ();
(3) 增加定时器
void add_timer (struct timer_list *timer);
(4) 删除定时器
int del_timer (struct timer_list *timer);
(5) 修改定时器的expire
int mod_timer (struct timer_list *timer, unsigned long expires);
(6) 对于周期性的任务,linux内核还提供了一种delayed_work机制来完成,本质上用工作队列和定时器实现。
3 实例--秒字符设备
second_drv.c
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #include <linux/slab.h> #define SECOND_MAJOR 248 static int second_major = SECOND_MAJOR; struct second_dev { struct cdev cdev; atomic_t counter; struct timer_list s_timer; }; struct second_dev *second_devp; static void second_timer_handle (unsigned long arg) { mod_timer (&second_devp->s_timer, jiffies + HZ); atomic_inc (&second_devp->counter); printk (KERN_NOTICE "current jiffies is %ld\n", jiffies); } int second_open (struct inode *inode, struct file *filp) { init_timer (&second_devp->s_timer); second_devp->s_timer.function = &second_timer_handle; second_devp->s_timer.expires = jiffies + HZ; add_timer (&second_devp->s_timer); atomic_set (&second_devp->counter, 0); return 0; } int second_release (struct inode *inode, struct file *filp) { del_timer (&second_devp->s_timer); return 0; } static ssize_t second_read (struct file *filp, char __user *buf, size_t count, loff_t *ppos) { int counter; counter = atomic_read (&second_devp->counter); if (put_user (counter, (int *)buf)) return -EFAULT; else return sizeof (unsigned int); } static const struct file_operations second_fops = { .owner = THIS_MODULE, .open = second_open, .release = second_release, .read = second_read, }; static void second_setup_cdev (struct second_dev *dev, int index) { int err, devno = MKDEV (second_major, index); cdev_init (&dev->cdev, &second_fops); dev->cdev.owner = THIS_MODULE; err = cdev_add (&dev->cdev, devno, 1); if (err) printk (KERN_NOTICE "Error %d adding CDEV %d", err, index); } int second_init (void) { int ret; dev_t devno = MKDEV (second_major, 0); if (second_major) ret = register_chrdev_region (devno, 1, "second"); else { return alloc_chrdev_region (&devno, 0, 1, "second"); second_major = MAJOR (devno); } if (ret < 0) return ret; second_devp = kmalloc (sizeof (struct second_dev), GFP_KERNEL); if (!second_devp) { ret = -ENOMEM; goto fail_malloc; } memset (second_devp, 0, sizeof (struct second_dev)); second_setup_cdev (second_devp, 0); return 0; fail_malloc: unregister_chrdev_region (devno, 1); return ret; } void second_exit (void) { cdev_del (&second_devp->cdev); kfree (second_devp); unregister_chrdev_region (MKDEV (second_major, 0), 1); } MODULE_AUTHOR ("Ljia-----Ljia"); MODULE_LICENSE ("Dual BSD/GPL"); module_param (second_major, int, S_IRUGO); module_init (second_init); module_exit (second_exit);
second_test.c
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> int main (void) { int fd; int counter = 0; int old_counter = 0; fd = open ("/dev/second", O_RDONLY); if (fd != -1) { while (1) { read (fd, &counter, sizeof (unsigned int)); if (counter != old_counter) { printf ("seconds after open /dev/second: %d\n", counter); old_counter = counter; } } } else { printf ("Device open failure\n"); } return 0; }
相关文章推荐
- 内核驱动系列--中断和定时器
- linux驱动开发学习--对中断和内核定时器的学习笔记
- S3C6410 按键驱动(四) --- 内核定时器的使用
- linux设备驱动--内核等待队列知识点---结合中断使用
- s3c2410触摸屏驱动(2.6内核)分析 -中断下半部
- ZYNQ+Vivado2015.2系列(十三)私有定时器中断
- linux驱动学习之内核定时器使用
- 使用内核定时器的second字符设备驱动及测试代码
- linux驱动之定时器的介绍和内核时间的学习
- 内核驱动 - 内核定时器
- linux驱动之内核定时器驱动设计
- 怎样取消已经被内核占用的按键驱动中断号
- Linux设备驱动——内核定时器
- Delphi驱动开发研究之内核同步对象—线程与定时器
- (驱动)(中断)(定时器)中断结合定时器完成按键消抖的驱动程序
- 中断驱动学习与实例——定时器0中断实现led流水灯
- Linux从用户层到内核层系列 - TCP/IP协议栈部分系列5:内核定时器的定义与使用及STP定时器
- linux驱动之内核定时器驱动设计-- timer
- 内核驱动 - 混杂设备驱动 + 中断处理函数
- arm 驱动基础:字符设备中断实现过程(内核->驱动->应用)