中断处理分为上下半部
上半部:完成紧急的,不耗时的操作(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
jiffy是内核时钟中断的间隔
jiffies 是内核的全局变量,在驱动中可以直接访问 该值记录内核时钟中断产生的个数
tasklet与jiffies比较
|
tasklet |
work queue |
是否可睡眠 |
不允许睡眠 |
可睡眠 |
是否响应中断 |
响应中断 |
响应中断 |
响应时间 |
一个jiffy内 |
执行时间不确定(由内核调度) |
tasklet参考代码
#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);
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;
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);