一、什么是workqueue
Work queues defer work into a kernel thread; this bottom half always runs in process context. Because workqueue is allowing users to create a kernel thread and bind work to the kernel thread. So, this will run in process context and the work queue can sleep.
- Code deferred(延期) to a work queue has all the usual benefits of process context.
- Most importantly, work queues are schedulable and can therefore sleep.
二、如何判断使用workqueue还是softirq/tasklet——看是否需要sleep
Normally, it is easy to decide between using workqueue and softirq/tasklet:
- If the deferred work needs to sleep, then workqueue is used.
- If the deferred work need not sleep, then softirq or tasklet are used.
三、实现workqueue的方式
There are two ways to implement Workqueue in the Linux kernel.
1、Using global workqueue
2、Creating Own workqueue
四、 Using global workqueue
1、相关的API
DECLARE_WORK(name, void (*func)(void *))
int schedule_work( struct work_struct *work );
int scheduled_delayed_work( struct delayed_work *dwork, unsigned long delay );
int schedule_work_on( int cpu, struct work_struct *work );
int scheduled_delayed_work_on(int cpu, struct delayed_work *dwork, unsigned long delay );
int flush_work( struct work_struct *work );
void flush_scheduled_work( void );
int cancel_work_sync( struct work_struct *work );
int cancel_delayed_work_sync( struct delayed_work *dwork );
work_pending( work );
delayed_work_pending( work );
2、示例代码——静态方法创建work
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h> //kmalloc()
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/workqueue.h>
#define IRQ_NO 11
void work_fn(struct work_struct *work);
/*Creating work by Static Method */
DECLARE_WORK(work, work_fn);
/*Workqueue Function*/
void work_fn(struct work_struct *work)
{
printk(KERN_INFO "Executing Work Function\n");
}
// Interrupt handler for IRQ 11.
static irqreturn_t irq_handler(int irq, void *dev_id)
{
printk(KERN_INFO "Shared IRQ: Interrupt Occurred");
schedule_work(&work);
return IRQ_HANDLED;
}
volatile int etx_value = 0;
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
/*************** Driver Fuctions **********************/
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp,
char __user *buf, size_t len, loff_t *off);
static ssize_t etx_write(struct file *filp,
const char *buf, size_t len, loff_t *off);
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = etx_read,
.write = etx_write,
.open = etx_open,
.release = etx_release,
};
static int etx_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Device File Opened...!!!\n");
return 0;
}
static int etx_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Device File Closed...!!!\n");
return 0;
}
static ssize_t etx_read(struct file *filp,
char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Read function\n");
asm("int $0x3B"); // Corresponding to irq 11
return 0;
}
static ssize_t etx_write(struct file *filp,
const char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Write Function\n");
return len;
}
static int __init etx_driver_init(void)
{
/*分配设备号*/
if ((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) < 0)
{
printk(KERN_INFO "Cannot allocate major number\n");
return -1;
}
printk(KERN_INFO "Major = %d Minor = %d \n", MAJOR(dev), MINOR(dev));
/*创建cdev结构*/
cdev_init(&etx_cdev, &fops);
/*添加设备到系统*/
if ((cdev_add(&etx_cdev, dev, 1)) < 0)
{
printk(KERN_INFO "Cannot add the device to the system\n");
goto r_class;
}
/*创建设备类*/
if ((dev_class = class_create(THIS_MODULE, "etx_class")) == NULL)
{
printk(KERN_INFO "Cannot create the struct class\n");
goto r_class;
}
/*创建设备*/
if ((device_create(dev_class, NULL, dev, NULL, "etx_device")) == NULL)
{
printk(KERN_INFO "Cannot create the Device 1\n");
goto r_device;
}
if (request_irq(IRQ_NO, irq_handler, IRQF_SHARED, "etx_device", (void *)(irq_handler)))
{
printk(KERN_INFO "my_device: cannot register IRQ ");
goto irq;
}
printk(KERN_INFO "Device Driver Insert...Done!!!\n");
return 0;
irq:
free_irq(IRQ_NO, (void *)(irq_handler));
r_device:
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev, 1);
cdev_del(&etx_cdev);
return -1;
}
static void __exit etx_driver_exit(void)
{
free_irq(IRQ_NO, (void *)(irq_handler));
device_destroy(dev_class, dev);
class_destroy(dev_class);
cdev_del(&etx_cdev);
unregister_chrdev_region(dev, 1);
printk(KERN_INFO "Device Driver Remove...Done!!!\n");
}
module_init(etx_driver_init);
module_exit(etx_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ldeng");
MODULE_DESCRIPTION("A simple device driver - Interrupts");
MODULE_VERSION("1.9");
3.示例代码——动态方法创建work
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h> //kmalloc()
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/workqueue.h>
#define IRQ_NO 11
/* Work structure */
static struct work_struct work;
void work_fn(struct work_struct *work);
/*Work Function*/
void work_fn(struct work_struct *work)
{
printk(KERN_INFO "Executing Work Function\n");
}
// Interrupt handler for IRQ 11.
static irqreturn_t irq_handler(int irq, void *dev_id)
{
printk(KERN_INFO "Shared IRQ: Interrupt Occurred");
schedule_work(&work);
return IRQ_HANDLED;
}
volatile int etx_value = 0;
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
/*************** Driver Fuctions **********************/
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp,
char __user *buf, size_t len, loff_t *off);
static ssize_t etx_write(struct file *filp,
const char *buf, size_t len, loff_t *off);
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = etx_read,
.write = etx_write,
.open = etx_open,
.release = etx_release,
};
static int etx_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Device File Opened...!!!\n");
return 0;
}
static int etx_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Device File Closed...!!!\n");
return 0;
}
static ssize_t etx_read(struct file *filp,
char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Read function\n");
asm("int $0x3B"); // Corresponding to irq 11
return 0;
}
static ssize_t etx_write(struct file *filp,
const char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Write Function\n");
return len;
}
static int __init etx_driver_init(void)
{
/*分配设备号*/
if ((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) < 0)
{
printk(KERN_INFO "Cannot allocate major number\n");
return -1;
}
printk(KERN_INFO "Major = %d Minor = %d \n", MAJOR(dev), MINOR(dev));
/*创建cdev结构*/
cdev_init(&etx_cdev, &fops);
/*添加设备到系统*/
if ((cdev_add(&etx_cdev, dev, 1)) < 0)
{
printk(KERN_INFO "Cannot add the device to the system\n");
goto r_class;
}
/*创建设备类*/
if ((dev_class = class_create(THIS_MODULE, "etx_class")) == NULL)
{
printk(KERN_INFO "Cannot create the struct class\n");
goto r_class;
}
/*创建设备*/
if ((device_create(dev_class, NULL, dev, NULL, "etx_device")) == NULL)
{
printk(KERN_INFO "Cannot create the Device 1\n");
goto r_device;
}
if (request_irq(IRQ_NO, irq_handler, IRQF_SHARED, "etx_device", (void *)(irq_handler)))
{
printk(KERN_INFO "my_device: cannot register IRQ ");
goto irq;
}
printk(KERN_INFO "Device Driver Insert...Done!!!\n");
return 0;
irq:
free_irq(IRQ_NO, (void *)(irq_handler));
r_device:
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev, 1);
cdev_del(&etx_cdev);
return -1;
}
static void __exit etx_driver_exit(void)
{
free_irq(IRQ_NO, (void *)(irq_handler));
device_destroy(dev_class, dev);
class_destroy(dev_class);
cdev_del(&etx_cdev);
unregister_chrdev_region(dev, 1);
printk(KERN_INFO "Device Driver Remove...Done!!!\n");
}
module_init(etx_driver_init);
module_exit(etx_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ldeng");
MODULE_DESCRIPTION("A simple device driver - Interrupts");
MODULE_VERSION("1.9");
五、Creating Own workqueue
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h> //kmalloc()
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/workqueue.h>
#define IRQ_NO 11
static struct workqueue_struct *own_workqueue;
static void work_fn(struct work_struct *work);
static DECLARE_WORK(work, work_fn);
/*Work Function*/
static void work_fn(struct work_struct *work)
{
printk(KERN_INFO "Executing Work Function\n");
return;
}
//Interrupt handler for IRQ 11.
static irqreturn_t irq_handler(int irq,void *dev_id) {
printk(KERN_INFO "Shared IRQ: Interrupt Occurred\n");
/*Allocating work to queue*/
queue_work(own_workqueue, &work);
return IRQ_HANDLED;
}
volatile int etx_value = 0;
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
/*************** Driver Fuctions **********************/
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp,
char __user *buf, size_t len, loff_t *off);
static ssize_t etx_write(struct file *filp,
const char *buf, size_t len, loff_t *off);
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = etx_read,
.write = etx_write,
.open = etx_open,
.release = etx_release,
};
static int etx_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Device File Opened...!!!\n");
return 0;
}
static int etx_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Device File Closed...!!!\n");
return 0;
}
static ssize_t etx_read(struct file *filp,
char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Read function\n");
asm("int $0x3B"); // Corresponding to irq 11
return 0;
}
static ssize_t etx_write(struct file *filp,
const char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Write Function\n");
return len;
}
static int __init etx_driver_init(void)
{
/*分配设备号*/
if ((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) < 0)
{
printk(KERN_INFO "Cannot allocate major number\n");
return -1;
}
printk(KERN_INFO "Major = %d Minor = %d \n", MAJOR(dev), MINOR(dev));
/*创建cdev结构*/
cdev_init(&etx_cdev, &fops);
/*添加设备到系统*/
if ((cdev_add(&etx_cdev, dev, 1)) < 0)
{
printk(KERN_INFO "Cannot add the device to the system\n");
goto r_class;
}
/*创建设备类*/
if ((dev_class = class_create(THIS_MODULE, "etx_class")) == NULL)
{
printk(KERN_INFO "Cannot create the struct class\n");
goto r_class;
}
/*创建设备*/
if ((device_create(dev_class, NULL, dev, NULL, "etx_device")) == NULL)
{
printk(KERN_INFO "Cannot create the Device 1\n");
goto r_device;
}
if (request_irq(IRQ_NO, irq_handler, IRQF_SHARED, "etx_device", (void *)(irq_handler)))
{
printk(KERN_INFO "my_device: cannot register IRQ ");
goto irq;
}
/*Creating workqueue */
own_workqueue = create_workqueue("own_wq");
printk(KERN_INFO "Device Driver Insert...Done!!!\n");
return 0;
irq:
free_irq(IRQ_NO, (void *)(irq_handler));
r_device:
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev, 1);
cdev_del(&etx_cdev);
return -1;
}
static void __exit etx_driver_exit(void)
{
free_irq(IRQ_NO, (void *)(irq_handler));
device_destroy(dev_class, dev);
class_destroy(dev_class);
cdev_del(&etx_cdev);
unregister_chrdev_region(dev, 1);
printk(KERN_INFO "Device Driver Remove...Done!!!\n");
}
module_init(etx_driver_init);
module_exit(etx_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ldeng");
MODULE_DESCRIPTION("A simple device driver - Interrupts");
MODULE_VERSION("1.9");
- 如果要使用自己的专用工作队列,则应使用
create_workqueue
创建工作队列。此时,您需要使用queue_work
函数对工作队列进行工作。 - 如果您不想创建任何自己的工作队列,则可以使用内核全局工作队列。在这种情况下,您可以使用
schedule_work
函数将工作置于全局 workqueue。