中断处理分为上下半部
上半部:完成紧急的,不耗时的操作(ISR)下半部:不紧急,耗时的操作,一般使用内核延时机制执行下半部
内核延时机制
内核调度优先级内优先级内核延时机制:内核在某个时间点调用执行指定的函数内核延时机制实现方式:tasklet/work queue/软中断(软中断不推荐)
中断 > tasklet > work queue
tasklet用法
- struct tasklet_struct tlet; //定义tasklet
- void handler (unsigned long data); //定义执行函数
- tasklet_init(&tlet, handler, args); //初始化tasklet
- tasklet_schedule(&tlet); //调度tasklet
work queue用法
- struct work_struct my_wq; //定义结构体
- woid do_my_work(struct work_struct *); //定义执行函数
- INIT_WORK(&my_wq, do_my_work); //初始化工作队列
- schedule_work(&my_wq); //调度工作队列
内核确保在一个jiffy内调度执行函数,在下一个时钟中断来之前调度
jiffy
tasklet与jiffies比较jiffy是内核时钟中断的间隔jiffies 是内核的全局变量,在驱动中可以直接访问 该值记录内核时钟中断产生的个数
| tasklet | work queue | |
| 是否可睡眠 | 不允许睡眠 | 可睡眠 |
| 是否响应中断 | 响应中断 | 响应中断 |
| 响应时间 | 一个jiffy内 | 执行时间不确定(由内核调度) |
tasklet参考代码
work queue参考代码#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/cdev.h>#include <asm/uaccess.h>#include <linux/sched.h>#include <linux/semaphore.h>#include <linux/interrupt.h>
MODULE_LICENSE ("GPL");
int hello_major = 250;int hello_minor = 0;int number_of_devices = 1;
struct hello_device{char data[128];int len;wait_queue_head_t rq, wq;struct semaphore sem;struct cdev cdev;struct tasklet_struct tlet;} hello_device;
static int hello_open (struct inode *inode, struct file *filp){filp->private_data = container_of(inode->i_cdev, struct hello_device, cdev);printk (KERN_INFO "Hey! device opened\n");
return 0;}
static int hello_release (struct inode *inode, struct file *filp){printk (KERN_INFO "Hmmm... device closed\n");
return 0;}
ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp){ssize_t result = 0;struct hello_device *dev = filp->private_data;
down(&dev->sem);while (hello_device.len == 0){up(&dev->sem);if (filp->f_flags & O_NONBLOCK)return -EAGAIN;if (wait_event_interruptible(dev->rq, (dev->len != 0))) return -ERESTARTSYS;down(&dev->sem);}
if (count > dev->len) count = dev->len;if (copy_to_user (buff, dev->data, count)){result = -EFAULT;}else{printk (KERN_INFO "wrote %d bytes\n", count);dev->len -= count;result = count;memcpy(dev->data, dev->data+count, dev->len);}up(&dev->sem);wake_up(&dev->wq);
return result;}
ssize_t hello_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos){ssize_t ret = 0;struct hello_device *dev = filp->private_data;
if (count > 128) return -ENOMEM;down(&dev->sem);while (dev->len == 128){up(&dev->sem);if (filp->f_flags & O_NONBLOCK)return -EAGAIN;if (wait_event_interruptible(dev->wq, (dev->len != 128))) return -ERESTARTSYS;down(&dev->sem);}
if (count > (128 - dev->len)) count = 128 - dev->len;if (copy_from_user (dev->data+dev->len, buf, count)) {ret = -EFAULT;}else {dev->len += count;ret = count;}
tasklet_schedule(&dev->tlet);printk("in write jiffies=%ld\n",jiffies);up(&dev->sem);wake_up(&dev->rq);
return ret;}
struct file_operations hello_fops = {.owner = THIS_MODULE,.open = hello_open,.release = hello_release,.read = hello_read,.write = hello_write};
static void char_reg_setup_cdev (void){int error;dev_t devno;
devno = MKDEV (hello_major, hello_minor);cdev_init (&hello_device.cdev, &hello_fops);hello_device.cdev.owner = THIS_MODULE;error = cdev_add (&hello_device.cdev, devno , 1);if (error)printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);}
void jit_tasklet_fn(unsigned long arg){printk("in jit_tasklet_fn jiffies=%ld\n",jiffies);}
static int __init hello_2_init (void){int result;dev_t devno;
devno = MKDEV (hello_major, hello_minor);result = register_chrdev_region (devno, number_of_devices, "hello");
if (result < 0) {printk (KERN_WARNING "hello: can't get major number %d\n", hello_major);return result;}
char_reg_setup_cdev ();init_waitqueue_head(&hello_device.rq);init_waitqueue_head(&hello_device.wq);sema_init(&hello_device.sem, 1);memset(hello_device.data, 0, 128);hello_device.len = 0;
tasklet_init(&hello_device.tlet, jit_tasklet_fn, (unsigned long)&hello_device);
printk (KERN_INFO "char device registered\n");
return 0;}
static void __exit hello_2_exit (void){dev_t devno = MKDEV (hello_major, hello_minor);
cdev_del (&hello_device.cdev);
unregister_chrdev_region (devno, number_of_devices);}
module_init (hello_2_init);module_exit (hello_2_exit);
#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/cdev.h>#include <asm/uaccess.h>#include <linux/sched.h>#include <linux/semaphore.h>#include <linux/interrupt.h>
MODULE_LICENSE ("GPL");
int hello_major = 250;int hello_minor = 0;int number_of_devices = 1;
struct hello_device{char data[128];int len;wait_queue_head_t rq, wq;struct semaphore sem;struct cdev cdev;//struct tasklet_struct tlet;struct work_struct my_wq;} hello_device;
static int hello_open (struct inode *inode, struct file *filp){filp->private_data = container_of(inode->i_cdev, struct hello_device, cdev);printk (KERN_INFO "Hey! device opened\n");
return 0;}
static int hello_release (struct inode *inode, struct file *filp){printk (KERN_INFO "Hmmm... device closed\n");
return 0;}
ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp){ssize_t result = 0;struct hello_device *dev = filp->private_data;
down(&dev->sem);while (hello_device.len == 0){up(&dev->sem);if (filp->f_flags & O_NONBLOCK)return -EAGAIN;if (wait_event_interruptible(dev->rq, (dev->len != 0))) return -ERESTARTSYS;down(&dev->sem);}
if (count > dev->len) count = dev->len;if (copy_to_user (buff, dev->data, count)){result = -EFAULT;}else{printk (KERN_INFO "wrote %d bytes\n", count);dev->len -= count;result = count;memcpy(dev->data, dev->data+count, dev->len);}up(&dev->sem);wake_up(&dev->wq);
return result;}
ssize_t hello_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos){ssize_t ret = 0;struct hello_device *dev = filp->private_data;
if (count > 128) return -ENOMEM;down(&dev->sem);while (dev->len == 128){up(&dev->sem);if (filp->f_flags & O_NONBLOCK)return -EAGAIN;if (wait_event_interruptible(dev->wq, (dev->len != 128))) return -ERESTARTSYS;down(&dev->sem);}
if (count > (128 - dev->len)) count = 128 - dev->len;if (copy_from_user (dev->data+dev->len, buf, count)) {ret = -EFAULT;}else {dev->len += count;ret = count;}
//tasklet_schedule(&dev->tlet);schedule_work(&dev->my_wq);printk("in write jiffies=%ld\n",jiffies);up(&dev->sem);wake_up(&dev->rq);
return ret;}
struct file_operations hello_fops = {.owner = THIS_MODULE,.open = hello_open,.release = hello_release,.read = hello_read,.write = hello_write};
static void char_reg_setup_cdev (void){int error;dev_t devno;
devno = MKDEV (hello_major, hello_minor);cdev_init (&hello_device.cdev, &hello_fops);hello_device.cdev.owner = THIS_MODULE;error = cdev_add (&hello_device.cdev, devno , 1);if (error)printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);}
void my_do_work(struct work_struct *mywork){printk("in my_do_work jiffies=%ld\n",jiffies);}
static int __init hello_2_init (void){int result;dev_t devno;
devno = MKDEV (hello_major, hello_minor);result = register_chrdev_region (devno, number_of_devices, "hello");
if (result < 0) {printk (KERN_WARNING "hello: can't get major number %d\n", hello_major);return result;}
char_reg_setup_cdev ();init_waitqueue_head(&hello_device.rq);init_waitqueue_head(&hello_device.wq);sema_init(&hello_device.sem, 1);memset(hello_device.data, 0, 128);hello_device.len = 0;
//tasklet_init(&hello_device.tlet, jit_tasklet_fn, (unsigned long)&hello_device);INIT_WORK(&hello_device.my_wq, my_do_work);
printk (KERN_INFO "char device registered\n");
return 0;}
static void __exit hello_2_exit (void){dev_t devno = MKDEV (hello_major, hello_minor);
cdev_del (&hello_device.cdev);
unregister_chrdev_region (devno, number_of_devices);}
module_init (hello_2_init);module_exit (hello_2_exit);

本文深入探讨了中断处理的上半部和下半部概念,详细介绍了内核延时机制及其实现方式,包括tasklet、workqueue等,并对比了它们之间的区别。同时,提供了基于中断和内核延时机制的驱动程序示例代码。

被折叠的 条评论
为什么被折叠?



