自定义队列queue_work和内核共享列队schedule_work

自定义队列queue_work和内核共享列队schedule_work#include<linux/delay.h>#include<linux/rtc.h>staticstructdelayed_workmdwq;staticstructworkqueue_struct*mwq;staticvoiddelay_work_func(structwork_struct*work){inti; structtimextxc; structrtc_timetm;

#include <linux/delay.h> 
#include <linux/rtc.h>

static struct delayed_work mdwq;  
static struct workqueue_struct *mwq;  

static void delay_work_func(struct work_struct *work)  { 
     
    int i;  
	struct timex  txc;
	struct rtc_time tm;
    for (i = 0; i < 5; i++) { 
     
		do_gettimeofday(&(txc.time));
		rtc_time_to_tm(txc.time.tv_sec,&tm);
		printk("[%d-%02d-%02d %02d:%02d:%02d]%s:i=%d\n",
			tm.tm_year+1900,tm.tm_mon+1, tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec,
			__FUNCTION__,i);
        msleep(1000); 
    }  
}  
  
static int __init delay_work_init(void)  { 
     
    int ret;  
    int i;  
    mwq = create_workqueue("my workqueue"); 
    if (!mwq) { 
     
        printk(KERN_ERR "Create workqueue failed!\n");  
        return 1;     
    }  
	struct timex  txc;
	struct rtc_time tm;
	do_gettimeofday(&(txc.time));
	rtc_time_to_tm(txc.time.tv_sec,&tm);
	printk("[%d-%02d-%02d %02d:%02d:%02d][%s:Create workqueue successful!]\n",
			tm.tm_year+1900,tm.tm_mon+1, tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec,
			__FUNCTION__);
	
    INIT_DELAYED_WORK(&mdwq, delay_work_func);  
	ret = queue_delayed_work(mwq, &mdwq, msecs_to_jiffies(3000));
    printk(KERN_INFO "%s queue_delayed_work result=[%d]\n", __FUNCTION__, ret); 
    return 0;  
}  
  
static void __exit delay_work_exit(void)  { 
     
    int ret;  
    ret = cancel_delayed_work(&mdwq); 
    flush_workqueue(mwq);  
    destroy_workqueue(mwq);  
    printk(KERN_INFO "Exit! ret=%d\n", ret);  
}  
module_init(delay_work_init);  
module_exit(delay_work_exit);  
MODULE_LICENSE("GPL"); 

有5秒钟延时的运行结果

$ insmod queuework_driver.ko
[2020-07-20 16:31:38][delay_work_init:Create workqueue successful!]
delay_work_init queue_delayed_work result=[1]
[2020-07-20 16:31:41]delay_work_func:i=0
[2020-07-20 16:31:42]delay_work_func:i=1
[2020-07-20 16:31:43]delay_work_func:i=2
[2020-07-20 16:31:44]delay_work_func:i=3
[2020-07-20 16:31:45]delay_work_func:i=4
$ ps
117 root  0:00 [my workqueue/0]
$ rmmod queuework_driver
Exit! ret=0

执行逻辑
create_workqueue
创建工作队列 workqueue_struct,该函数会为cpu创建内核线程
INIT_DELAYED_WORK
初始化延迟的工作 work_struct,指定工作函数
queue_delayed_work
将初始化后的延迟的工作 加入到工作队列中,在第三个参数指定的时间周期后去执行

如果不使用延时 则是分别替换掉三个函数

// INIT_DELAYED_WORK, queue_delayed_work, cancel_delayed_work
INIT_WORK(&mdwq.work, delay_work_func);  
ret = queue_work(mwq, &mdwq);
ret = cancel_work_sync(&mdwq); 

修改后,为不延时的运行结果

$ insmod queuework_driver.ko
[2020-07-20 16:49:31][delay_work_init:Create workqueue successful!]
[2020-07-20 16:49:31]delay_work_func:i=0
delay_work_init queue_delayed_work result=[1]
[2020-07-20 16:49:32]delay_work_func:i=1
[2020-07-20 16:49:33]delay_work_func:i=2
[2020-07-20 16:49:34]delay_work_func:i=3
[2020-07-20 16:49:35]delay_work_func:i=4
$ rmmod queuework_driver
Exit! ret=0

可以看出时间上是没有间隙的。

这里需要预先了解有使用ps查看当前进程可以看到 [events/0]
[events/0] 属于单核的处理器提供了一个死循环的缺省的工作者线程
用巡视的方式处理队列中的任务

可以通过 INIT_DELAYED_WORK 在这个任务队列中加入一个任务
并且调用 queue_delayed_work 来通知更新了队列以便巡视的时候多个一个任务需要执行
在 queue_delayed_work 之后,工作 func 就会被 [events/0] 执行到。

其中两个重要结构体

struct delayed_work mdwq; // 指向需要执行的函数
struct workqueue_struct *mwq;  // 工作队列

补充
create_workqueue
创建一个任务。
Linux 3.10 之后的版本用 alloc_workqueue 代替 create_workqueue
但是当前我的测试环境为 Linux 2.6 所以仍然使用 create_workqueue

以上为使用了自己的工作队列,即自定义队列
在许多情况下,设备驱动程序不需要有自己的工作队列。如果我们只是偶尔需要向队列中提交任务,
则一种更简单、更有效的办法是使用内核提供的共享的默认工作队列。
使用 schedule_work 可以把工作提交到共享工作队列中,由内核默认提供的工作队列[events/0]执行。

static int __init delay_work_init(void)  {  
    int ret;  
    int i; 
	struct timex  txc;
	struct rtc_time tm;
	do_gettimeofday(&(txc.time));
	rtc_time_to_tm(txc.time.tv_sec,&tm);
	printk("[%d-%02d-%02d %02d:%02d:%02d][%s(pid=%d):Create workqueue successful!]\n",
			tm.tm_year+1900,tm.tm_mon+1, tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec,
			__FUNCTION__, current->pid);
    
	INIT_WORK(&mdwq.work, delay_work_func);  
	ret = schedule_work(&mdwq.work);
    printk(KERN_INFO "%s queue_delayed_work result=[%d]\n", __FUNCTION__, ret);  
    return 0;  
}  
  
static void __exit delay_work_exit(void)  {  
    int ret;  
	ret = cancel_work_sync(&mdwq); 
    //flush_workqueue(mwq);  
    //destroy_workqueue(mwq);  
    printk(KERN_INFO "Exit! ret=%d\n", ret);  
}  

输出相关的线程的ID可以看到

$ insmod queuework_driver.ko
[2020-07-20 16:49:31][delay_work_init(pid=100):Create workqueue successful!]
[2020-07-20 16:49:31]delay_work_func(pid=4):i=0
delay_work_init queue_delayed_work result=[1]
[2020-07-20 16:49:32]delay_work_func(pid=4):i=1
[2020-07-20 16:49:33]delay_work_func(pid=4):i=2
[2020-07-20 16:49:34]delay_work_func(pid=4):i=3
[2020-07-20 16:49:35]delay_work_func(pid=4):i=4
$ ps
4 root  0:00 [events/0]
$ rmmod queuework_driver
Exit! ret=0

可以看出执行的线程是 ID为4的[events/0],所以表示使用 schedule_work 把工作提交到
内核默认提供的工作队列[events/0]执行了。

需要使用工作队列时,可以考虑内核提供的下面两种接口

schedule_work/schedule_delayed_work
create_workqueue/queue_work/queue_delayed_work
schedule_work 把任务提交到内核默认提供的工作队列[events/0]中执行
schedule_delayed_work 把任务提交到内核默认提供的工作队列[events/0]中,(延时一定的时间)执行
queue_work 把任务提交到自定义创建的队列[my workqueue/0]中执行
queue_delayed_work 把任务提交到自定义创建的队列[my workqueue/0]中,(延时一定的时间)执行
INIT_WORK 把需要执行的函数初始化(封装)为一个工作队列中的任务

使用 ps 命令可以查看两者在内核中属于不同线程 4 root 0:00 [events/0] 以及 117 root 0:00 [my workqueue/0]今天的文章自定义队列queue_work和内核共享列队schedule_work分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/23908.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注