linux 中断之工作队列workqueue (默认的工作队列,内核默认线程负担重)

Table of Contents

什么是workqueue

两种形式的工作队列

编程步骤

1、初始化

2、登记调度

参考代码


 

什么是workqueue

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

工作队列(workqueue)是另外一种将工作推后执行的形式.工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。最重要的就是工作队列允许被重新调度甚至是睡眠。

内核里一直运行类似worker thread,它会对工作队列中的work进行处理,大致的工作流程原理可以参考下图所示;​​​​​​:

也就是一个线程管理多个工作任务

在这里插入图片描述

两种形式的工作队列

#include <linux/include/workqueue.h>

typedef void (*work_func_t)(struct work_struct *work);

struct work_struct {
	atomic_long_t data;
	struct list_head entry;
	work_func_t func;
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};

work_struct 通知内核线程,在后续的时间里,系统将会自动调用work结构体对应的func函数

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;
};

delay_work 该工作队列里拥有一个timer定时器结构体,从而实现延时工作

编程步骤

1、初始化

struct work_struct btn_work:

INIT_WORK(&btn_work, btn_work_func); //指定工作的处理函数

struct delayed_work btn_dwork:

INIT_DELAYED_WORK(&btn_dwork, btn_dwork_func); //指定延时工作的处理函数

2、登记调度

struct work_struct btn_work

schedule_work(&btn_work); //登记工作,内核会将这个工作交给内核默认的线程管理

struct work_struct btn_work

//登记延时的工作,并且指定延时的工作处理函数是5秒以后去执行。如果延时时间还没有到期,由重新登记,

schedule_delayed_work(&btn_dwork, 5*HZ);

参考代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>

//定义按键硬件相关的数据结构
struct button_resource {
    int irq;    //中断号
    char *name; //中断名称
};

//初始化按键信息
static struct button_resource btn_info[] = {
    [0] = {
        .irq = IRQ_EINT(0),
        .name = "KEY_UP"
    },
    [1] = {
        .irq = IRQ_EINT(1),
        .name = "KEY_DOWN"
    }
};

//分配工作和延时工作
static struct work_struct btn_work;
static struct delayed_work btn_dwork;

//工作处理函数
static void btn_work_func(struct work_struct *work)
{
    printk("%s: %#x\n", __func__, work);
}

//延时工作处理函数
static void btn_dwork_func(struct work_struct *work)
{
    printk("%s: %#x\n", __func__, work);
}

//中断处理函数就是顶半部
static irqreturn_t button_isr(int irq, void *dev_id)
{
    //登记工作,内核会在适当的时候执行工作对应的处理函数
    //schedule_work(&btn_work);
    
    //登记延时工作,并且指定延时的时间间隔为3S
    schedule_delayed_work(&btn_dwork, 3*HZ);

    printk("%s\n", __func__);
    return IRQ_HANDLED; //处理完毕
}

static int btn_init(void)
{
    int i;

    printk("register irq!\n");

    for (i = 0; i < ARRAY_SIZE(btn_info); i++)
        request_irq(btn_info[i].irq, button_isr, 
                IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
                btn_info[i].name, &btn_info[i]);
   
    //初始化工作和延时的工作
    INIT_WORK(&btn_work, btn_work_func); //指定工作处理函数
    INIT_DELAYED_WORK(&btn_dwork, btn_dwork_func); //指定延时工作处理函数
    printk("%s: %#x, %#x\n", 
            __func__, &btn_work, &btn_dwork);
    return 0;
}

static void btn_exit(void)
{
    int i;

    printk("unregister irq!\n");

    //注意注册中断传递的参数和释放中断传递的参数一定要一致!
    for(i = 0; i < ARRAY_SIZE(btn_info); i++)
        free_irq(btn_info[i].irq, &btn_info[i]);
}
module_init(btn_init);
module_exit(btn_exit);
MODULE_LICENSE("GPL");

参考博文:https://www.cnblogs.com/zxc2man/p/6604290.html

https://blog.csdn.net/u010632165/article/details/88240665

发布了137 篇原创文章 · 获赞 106 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/shenlong1356/article/details/103290945