Workqueue in Linux Kernel

一、什么是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。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

denglin12315

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值