Linux驱动编程 - 中断上半部和下半部_工作队列

workqueue 简介:


        工作队列(workqueue)是 Linux 内核中一种将工作推迟到进程上下文执行的机制,因此工作队列允许被重新调度甚至是睡眠。它属于中断下半部机制的一种。但它并不一定要和中断一起使用,即使没有中断,也可以在Linux驱动中独立使用

        Linux中通过调用workqueue的接口就能创建内核线程。并且可以根据当前系统CPU的个数创建线程的数量,使得线程处理的事务能够并行化。

Linux驱动编程使用 workqueue 时,我们需要注意一下2点:

  • 系统默认有system_wq作为workqueue供我们使用
  • 我们也能创建自己的workqueue

一、workqueue的API函数


源码路径:kernel/workqueue.c

头文件路径:include/linux/workqueue.h

1. create_workqueue(name)
功能:用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。
输入参数:
    name:workqueue的名称

2. create_singlethread_workqueue(name)
功能:用于创建workqueue,只创建一个内核线程。输入参数:
输入参数:
    name:workqueue名称
 
3. destroy_workqueue(struct workqueue_struct *wq)
功能:释放workqueue队列。输入参数:
输入参数:
    workqueue_struct:需要释放的workqueue队列指针

4. schedule_work(struct work_struct *work);
功能:调度执行一个具体的任务
输入参数:
    work_struct:具体任务对象指针

5. schedule_delayed_work(struct delayed_work *dwork, unsigned long delay)
功能:延迟一定时间去执行一个具体的任务,功能与schedule_work类似,多了一个延迟时间,
输入参数:
    work_struct:具体任务对象指针
    delay:延迟时间

6. queue_work(struct workqueue_struct *wq, struct work_struct *work)
功能:调度执行一个指定workqueue中的任务。
输入参数:
    workqueue_struct:指定的workqueue指针
    work_struct:具体任务对象指针

7. queue_delayed_work(struct workqueue_struct *wq,
                           struct delayed_work *dwork, unsigned long delay)
功能:延迟调度执行一个指定workqueue中的任务。功能与queue_work类似,输入参数多了一个delay。 

create_workqueue() 最终调用 alloc_workqueue()函数

#define create_workqueue(name)                        \
    alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))

二、使用方法


1、默认的workqueue队列

  • 工作队列 work_struct
//结构体
struct work_struct {
    atomic_long_t data;         //相关标志位
    struct list_head entry;     //链表节点
    work_func_t func;           //工作函数
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;   //死锁检测
#endif
};
1、定义
static struct work_struct work;
static void work_callback_func(struct work_struct *work)  
{  
} 

2、初始化
INIT_WORK(&work, work_callback_func); 

3、触发工作队列
schedule_work(&work);  
  • 延时工作队列 delayed_work
//延时结构体
struct delayed_work {
    struct work_struct work;
    struct timer_list timer;

    /* target workqueue and CPU ->timer uses to queue ->work */
    struct workqueue_struct *wq;
    int cpu;
};
1、定义
static struct delayed_work delayed_work;  
static void delayed_callback_func(struct work_struct *work)  
{  
}  

2、初始化
INIT_DELAYED_WORK(&delayed_work, delayed_callback_func);  

3、触发延时工作队列
schedule_delayed_work(&delayed_work, 5 * HZ);  

三、示例


1、系統默认的workqueue队列

        linux系统已经提供了一个默认的workqueue队列“system_wq”,并提供了一套函数来方便大家使用

#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/err.h>
#include <linux/delay.h>
 
static struct work_struct test_work;                //工作队列
static struct delayed_work test_delayed_work;        //延时工作队列
 
static void test_func(struct work_struct *work)
{
    printk("%s, %d, %ld\n", __func__, __LINE__, jiffies);
}
 
static void test_delayed_func(struct work_struct *work)
{
    printk("%s, %d, %ld\n", __func__, __LINE__, jiffies);
}
 
static int __init test_init(void)
{
    int ret;
    printk("%s, %d, %ld\n", __func__, __LINE__, jiffies);
 
    INIT_WORK(&test_work, test_func);                            //初始化
    ret = schedule_work(&test_work);                            //调度工作队列
 
    INIT_DELAYED_WORK(&test_delayed_work, test_delayed_func);    //初始化
    ret = schedule_delayed_work(&test_delayed_work, 5 * HZ);    //调度延时工作队列
 
    printk("%s, %d, %ld\n", __func__, __LINE__, jiffies);
 
    return 0;
}
 
static void __exit test_exit(void)
{
    cancel_work_sync(&test_work);
    cancel_delayed_work_sync(&test_delayed_work);
}
 
module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

当然,你也可以用DECLARE_WORK来完成和INIT_WORK同样初始化work的工作。区别是DECLARE_WORK是预编译命令,而INIT_WORK可以在code中动态初始化。

schedule_work 用的就是 system_wq。我们看看schedule_work函数定义,一切就真相大白了!

static inline bool schedule_work(struct work_struct *work)
{
    return queue_work(system_wq, work);
}

2、自己创建workqueue队列

#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/sysfs.h>
#include <linux/ctype.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
 
//工作以队列结构组织成工作队列(workqueue),其数据结构为workqueue_struct,
static struct workqueue_struct *test_wq = NULL;    
 
//把推后执行的任务叫做工作(work),描述它的数据结构为work_struct
static struct work_struct   work;
 
//定义工作队列调用函数
void work_func(struct work_struct *work){
      printk(KERN_INFO "---%s---\n",__func__);  
}
 
static int __init test_init(void){
    int i;
 
    /* 创建工作队列workqueue_struct,该函数会为cpu创建内核线程 */
    test_wq = create_workqueue("test_wq");   

    /* 初始化工作work_struct,指定工作函数 */
    INIT_WORK(&work,work_func);
 
    /* 将工作加入到工作队列中,最终唤醒内核线程 */
    queue_work(test_wq, &work);
 
    for(i = 0; i < 3; i++)
    {
        printk(KERN_INFO "%s, %s, %d\n", __FILE__, __func__, __LINE__);
    }
    return 0;
}
 
static void __exit test_exit(void){    
    printk(KERN_INFO "%s, %s, %d\n", __FILE__, __func__, __LINE__);
}

运行结果可以看出,是先执行的test_init里的printk()打印程序,后执行workqueue的程序

test/test.c, test_init, 35
test/test.c, test_init, 35
test/test.c, test_init, 35
---work_func---
test/test.c, test_exit, 41

新建workqueue可以避免system_wq中被挂的work过多,或是某个被挂上去的work处理函数死锁,导致我们的work没有及时调度到。

但是,创建太多workqueue会导致系统调度开销的变大,所以需要取舍。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值