|

点击左上方蓝色“一口Linux”,选择“设为星标”
第一时间看干货文章
?【干货】嵌入式驱动工程师学习路线?【干货】Linux嵌入式知识点-思维导图-免费获取?【就业】一个可以写到简历的基于Linux物联网综合项目?【就业】找工作简历模版
u5radmw24jd64021462118.gif
1一、Linux内核工作队列在Linux内核中,工作队列是一种异步处理机制,用于延迟执行一些需要在进程上下文中执行的任务。工作队列通常由内核模块或驱动程序使用,以避免在中断上下文中执行长时间运行的操作。
如果中断需要执行的任务很多,就需要分为上半部和底半部,上半部就是中断发生的中断服务函数,在这里需要尽可能执行快,以免影响系统其他中断的处理,底半部发生在不久的将来,用来处理中断中未完成的耗时多的工作。
上半部在中断上下文,下半部在进程上下文。工作队列就是底半部机制的其中一种。本质上一种代码推迟执行的机制。
代码推迟到工作队列,可以享有进程上下文的所有好处工作队列可以被调度,也可以睡眠换句话说,如果需要推迟的代码需要睡眠的,就选择工作队列。
二 、工作队列的优点使用工作队列的一个主要优点是避免在中断上下文中执行长时间运行的操作。中断上下文应该尽可能短暂,以免影响系统的响应性和稳定性。使用工作队列可以将长时间运行的操作延迟到稍后的时间执行,从而避免在中断上下文中执行。
另一个优点是工作队列可以在多个CPU上并行执行任务。这可以提高系统的吞吐量和响应性。
总之,工作队列是Linux内核中一种非常有用的异步处理机制。它可以帮助内核模块和驱动程序避免在中断上下文中执行长时间运行的操作,并提高系统的吞吐量和响应性。
三、 工作队列的使用(使用系统自带的工作队列)初始化work_struct静态方法:
DECLARE_WORK(work,work_fn);
动态方法:
INIT_WORK(work,work_fn)
所谓静态就是在编译时候就得到了work_struct的空间,跟随着ko,会增大ko的大小,最后被放到内存上。
所谓的动态就是在运行时候才得到了work_srtuct的空间,这时候是在堆上,本质上也是在内存上。
调度work_struct 到workqueueschedule_work(struct work_struct *work)
schedule_work_on(int cpu, struct work_struct *work)
一个work可以被调度到系统的任意一个CPU,也可以被调度在指定的CPU,这就是以上两个API的区别。
取消一个work_structcancel_work(struct work_struct *work);
cancel_work_sync(struct work_struct *work);
取消一个work的时候,有可能他正在进行,如果需要等待它执行完,可以使用cancel_work_sync,等待其执行完毕再取消.
强制work的执行可能有多种原因,当你想work执行的那一刻,工作队列正在执行别的work或者在睡眠,那么你的work不能如期执行,这时候可以强制让work执行。
一个工作队列上是可以有多个work的,当然也可以针对一个工作队列来强制执行上面的多个work,使用flush_scheduled_work和flush_workqueue。flush_scheduled_work不带参数,特指system_wq,这是一个系统自带的工作队列。
flush_work(struct work_struct *work);
flush_scheduled_work()
flush_workqueue(struct workqueue_struct *_wq)
四、工作队列demo(使用系统自带的工作队列)以下驱动demo,特意使用了schedule_work_on的API,证明一个work是可以指定CPU运行的,通过写/dev/work_queue设备节点,触发调度work,在work回调里执行计算密集型任务,即可观察指定的CPU的使用率,最后卸载模块的时候,特意使用了带sync的API,等待work执行完毕,因此可以看到rmmod模块时会阻塞掉一段时间。
#include
#include
#include
#include
#include
#include
struct my_work_info{
dev_t dev;
struct cdev chrdev;
struct class *work_class;
struct device *device;
struct kobject *sysfs_obj;
};
struct my_work_info *g_data;
volatile int etx_value = 0;
void mywork_fn(struct work_struct *work)
{
int i = 5;
unsigned long long cont;
while(i--)
{
int i, j;
if(work_busy(work))
printk(KERN_INFO "busy
");
for (i = 0; i 100; i++) {
for (j = 0; j 100; j++) {
cont++;
printk(KERN_INFO "cont(%d)
",cont);
}
}
}
}
static DECLARE_WORK(mywork,mywork_fn);
ssize_t work_queue_dev_read(struct file *filep, char __user *ubuf, size_t len, loff_t *offset)
{
printk(KERN_INFO "Read function
");
return 0;
}
ssize_t work_queue_dev_write (struct file *filep, const char __user *ubuf, size_t len, loff_t *offset)
{
printk(KERN_INFO "Write Function
");
if(etx_value)
schedule_work_on(1,&mywork);
else
cancel_work_sync(&mywork);
return len;
}
int work_queue_dev_open (struct inode *inode, struct file *filep)
{
printk(KERN_INFO "Device File Opened...!!!
");
return 0;
}
int work_queue_dev_close (struct inode *inode, struct file *filep)
{
printk(KERN_INFO "Device File Closed...!!!
");
return 0;
}
static struct file_operations work_queue_fops = {
.open = work_queue_dev_open,
.release = work_queue_dev_close,
.read = work_queue_dev_read,
.write = work_queue_dev_write,
};
static ssize_t sysfs_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
printk(KERN_INFO "Sysfs - Read!!!
");
return sprintf(buf, "%d", etx_value);
}
/*
** This function will be called when we write the sysfsfs file
*/
static ssize_t sysfs_store(struct kobject *kobj,
struct kobj_attribute *attr,const char *buf, size_t count)
{
printk(KERN_INFO "Sysfs - Write!!!
");
sscanf(buf,"%d",&etx_value);
return count;
}
struct kobj_attribute etx_attr = __ATTR(etx_value, 0660, sysfs_show, sysfs_store);
static int __init work_queue_init(void)
{
struct my_work_info*wk_info;
int ret = 0;
wk_info = kmalloc(sizeof(struct my_work_info), GFP_KERNEL);
ret = alloc_chrdev_region(&wk_info->dev, 0, 1, "myworkqueue");
if(ret 0){
pr_err(KERN_INFO "Cannot allocate major number
");
return ret;
}
printk(KERN_INFO "Major = %d Minor = %d
",MAJOR(wk_info->dev),
MINOR(wk_info->dev));
cdev_init(&wk_info->chrdev, &work_queue_fops);
ret = cdev_add(&wk_info->chrdev, wk_info->dev, 1);
if(ret){
pr_err(KERN_INFO "Cannot add the device to the system
");
goto err_add;
}
wk_info->work_class = class_create(THIS_MODULE, "myworkqueue");
if(IS_ERR(wk_info->work_class)){
printk(KERN_INFO "Cannot create the struct class
");
goto err_add;
}
wk_info->device = device_create(wk_info->work_class, NULL, wk_info->dev,
NULL, "workqueuedev");
if(IS_ERR(wk_info->device)){
printk(KERN_INFO "Cannot create the struct device
");
goto err_device;
}
wk_info->sysfs_obj = kobject_create_and_add("myworkqueuesysfs",
kernel_kobj);
if(!wk_info->sysfs_obj){
printk(KERN_INFO "Cannot create the sysfs dir
");
goto err_device;
}
if(sysfs_create_file(wk_info->sysfs_obj ,&etx_attr.attr)){
printk(KERN_INFO"Cannot create sysfs file......
");
goto err_sysfs;
}
g_data = wk_info;
return 0;
err_sysfs:
kobject_put(wk_info->sysfs_obj);
sysfs_remove_file(wk_info->sysfs_obj,&etx_attr.attr);
err_device:
class_destroy(wk_info->work_class);
err_add:
unregister_chrdev_region(wk_info->dev,1);
cdev_del(&wk_info->chrdev);
return ret;
}
static void __exit work_queue_exit(void)
{
struct my_work_info*wk_info = g_data;
cancel_work(&mywork);
kobject_put(wk_info->sysfs_obj);
sysfs_remove_file(wk_info->sysfs_obj, &etx_attr.attr);
device_destroy(wk_info->work_class, wk_info->dev);
class_destroy(wk_info->work_class);
cdev_del(&wk_info->chrdev);
unregister_chrdev_region(wk_info->dev,1);
}
module_init(work_queue_init);
module_exit(work_queue_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lzy");
MODULE_DESCRIPTION("Simple Linux device driver (work_queue)");
MODULE_VERSION("1.6");
加载驱动后触发:
echo 1 > /sys/kernel/myworkqueuesysfs/etx_value
echo 1 > /dev/workqueuedev |
|