该系列为linux中常见功能demo,方便后续以此进行扩展应用。
一、简介
workqueue是对内核线程封装的用于处理各种工作项的一种处理方法, 由于处理对象是用链表拼接一个工作项, 依次取出来处理, 然后从链表删除,就像一个队列排好队依次处理一样, 所以也称工作队列,所谓封装可以简单理解一个中转站, 一边指向“合适”的内核线程, 一边接受你丢过来的工作项, 用结构体 workqueue_srtuct表示, 而所谓工作项也是个结构体 -- work_struct, 里面有个成员指针, 指向你最终要实现的函数。
二、demo
1、要创建及使用一个workqueue,需要经过这么几个步骤:
① 使用create_workqueue创建一个自己的workqueue队列(也可以使用系统已经为我们创建好的对列),为系统中的每个CPU都创建一个内核线程。对应的参数即创建的workqueue名称。
② 创建完对列后需要初始化work_struct,此时就会用到INIT_WORK方法,第一个参数work_struct可以理解为一个指向工作任务的指针,第二个参数即对应的工作任务执行方法。可以简单理解为,我们创建了一个指向工作任务的work_struct指针,当需要对工作对列中的任务进行处理时,就会执行到第二参数对应的方法。
③ 如上,我们已经创建了工作对列,以及一个工作任务,那么我们如何进行调度执行这个工作任务呢?这就需要queue_work方法了。该方法会将我们先前创建的工作任务添加到我们创建的工作对列中,添加到对列中后就会在这个线程激活时就会运行第二步中的工作任务方法了。理论上在没有阻塞的情况下,会立即执行work_handle方法。
2、当然对于work_handle的执行次数我们是可以进行设定的,对于中断的处理,我们可能只执行一次就可以了,但有些情况下我们需要循环的执行work_handle方法,比如我们需要轮训某个外设的数据。那么我们就可以在work_handle方法中添加需要的延迟,并重新调用queue_work将work_struct添加到对列中即可。
对于循环执行的情况来说,我们需要有个开关来控制其是否一直执行。我们可以给上层提供一个节点。这个节点我们又有多种方式进行创建。该demo介绍了两种方法,一种是在proc下面创建,一种是在sys下创建。
① 在proc目录下创建的方法用 WORKQUEUE_PROC_BUILD 宏来控制,打开后的情况是会在proc目录下创建一个workqueue_main节点。对这个节点写1即可开启循环执行work_handle。写0即可关闭。(对于proc下创建节点的方式可以参照另一篇文档:https://blog.csdn.net/u010787514/article/details/88236649)
② 在sys目录创建节点的方法用 WORKQUEUE_SYS_BUILD 宏来控制,打开后会在sys目录下创建work_queue子目录,在work_queue子目录下会创建两个节点work_queue_flag和work_queue_active。该demo中work_queue_active没有特殊用途。work_queue_flag节点来控制是否循环执行work_handle。同样的写1是开启循环,写0是关闭。(可参考:https://blog.csdn.net/chenmiaowu88/article/details/54427013)
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/uaccess.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/kobject.h>
//#include "workqueue_main.h"
//#define WORKQUEUE_PROC_BUILD
#define WORKQUEUE_SYS_BUILD
struct workqueue_struct *workqueue_main;
struct work_struct work_main;
int flag_workqueue = 0;
#ifdef WORKQUEUE_SYS_BUILD
static struct kobject *k_obj = NULL;
static char node_one_buf[20] = {0};
#endif
void work_handle (struct work_struct *work)
{
printk("enter work handle %d", flag_workqueue);
if (flag_workqueue) {
mdelay(1000);
queue_work(workqueue_main, &work_main);
}
}
#ifdef WORKQUEUE_PROC_BUILD
static ssize_t workqueue_proc_store(struct file *file, const char __user *buf,
size_t len, loff_t *pos)
{
char temp[1] = {0};
if (copy_from_user(temp, buf, len))
return -EFAULT;
if (temp[0] == '1')
flag_workqueue = 1;
else
flag_workqueue = 0;
if (flag_workqueue) {
mdelay(1000);
queue_work(workqueue_main, &work_main);
}
return len;
}
static ssize_t workqueue_proc_show(struct file *file, char __user *buf,
size_t len, loff_t *pos)
{
return 0;
}
static const struct file_operations workqueue_proc_fops = {
.write = workqueue_proc_store,
.read = workqueue_proc_show,
};
#endif
#ifdef WORKQUEUE_SYS_BUILD
static ssize_t work_queue_flag_show(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
{
if (flag_workqueue)
node_one_buf[0] = '1';
else
node_one_buf[0] = '0';
return sprintf(buf, "%s: %s\n", attr->attr.name, node_one_buf);
}
static ssize_t work_queue_flag_store(struct kobject *kobj, struct kobj_attribute *attr, const char * buf, size_t n)
{
printk("enter, node: %s\n", attr->attr.name);
sprintf(node_one_buf, "%s", buf);
if (node_one_buf[0] == '1')
flag_workqueue = 1;
else
flag_workqueue = 0;
if (flag_workqueue) {
mdelay(1000);
queue_work(workqueue_main, &work_main);
}
return n;
}
static ssize_t work_queue_active_show(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
{
printk("enter, node: %s\n", attr->attr.name);
return sprintf(buf, "%s: %s\n", attr->attr.name, node_one_buf);
}
static struct kobj_attribute work_queue_flag_attribute =
__ATTR(work_queue_flag, S_IWUSR|S_IRUGO, work_queue_flag_show, work_queue_flag_store);
static struct kobj_attribute work_queue_active_attribute =
__ATTR(work_queue_active, S_IWUSR|S_IRUGO, work_queue_active_show, NULL);
static struct attribute *work_queue_attributes[] = {
&work_queue_flag_attribute.attr,
&work_queue_active_attribute.attr,
NULL
};
static const struct attribute_group work_queue_attr_group = {
.attrs = work_queue_attributes,
};
#endif
static int work_mian_init (void)
{
workqueue_main = create_workqueue("my_queue");
INIT_WORK(&work_main, work_handle);
#ifdef WORKQUEUE_PROC_BUILD
proc_create("workqueue_main", 0666, NULL, &workqueue_proc_fops);
#endif
#ifdef WORKQUEUE_SYS_BUILD
if ((k_obj = kobject_create_and_add("work_queue", NULL)) == NULL ) {
printk("work_queue sys node create error \n");
return -1;
}
if(sysfs_create_group(k_obj, &work_queue_attr_group) ) {
printk("sysfs_create_group failed\n");
kobject_put(k_obj);;
}
#endif
queue_work(workqueue_main, &work_main);
return 0;
}
static void work_mian_exit(void)
{
printk("Goodbye,cruel world!\n");
destroy_workqueue(workqueue_main);
}
module_init(work_mian_init);
module_exit(work_mian_exit);
MODULE_LICENSE("GPL v2");
如上即是一个简单的workqueue的demo。这只是其中的一点点,实际工作中比这要复杂的多,欢迎一起学习,一起进步。