Linux内核中的工作队列是一种异步处理机制,用于延迟执行一些需要在进程上下文中执行的任务。以下是关于Linux工作队列的详解:
-
工作队列的组成:
- 工作项(work item):是工作队列中的元素,由一个回调函数和多个回调函数输入参数的集合组成,有时也会有额外的属性成员。通过一个结构体即可记录和描述一个工作项。
- 工作队列(work queue):是执行在线程上下文中,因此工作执行过程中可以被重新调度、抢占、睡眠。工作队列可以把工作推后,交由一个内核线程去执行。
-
工作队列的特性:
- 异步:工作不是在本中断或线程中执行,而是交由其他线程执行。
- 延期:交由低优先级线程执行,执行前可能被抢占,也可能有其他工作排在前面执行,所以从提交工作队列到工作真正被执行之间会延时不定长时间。
- 排队:FIFO模式先到先执行。也可能会有多个优先级的工作队列,低优先级的工作队列要等到高优先级的工作队列全部执行完成后才能执行。
- 缓存:既然是队列它就能缓存多个项,需要异步执行丢进去就行,队列会逐个执行。
-
工作队列的使用场景:
- 通常由内核模块或驱动程序使用,以避免在中断上下文中执行长时间运行的操作。如果中断需要执行的任务很多,就需要分为上半部和底半部,上半部就是中断发生的中断服务函数,在这里需要尽可能执行快,以免影响系统其他中断的处理;底半部发生在不久的将来,用来处理中断中未完成的耗时多的工作。上半部在中断上下文,下半部在进程上下文。工作队列就是底半部机制的其中一种。
总的来说,Linux工作队列是一种用于处理异步、延时、排队和缓存多个任务的机制,通常在内核模块或驱动程序中使用,以避免在中断上下文中执行长时间运行的操作。
简单的Linux工作队列的示例代码。请注意,这个示例代码是在Linux内核模块中实现的。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/workqueue.h>
// 定义工作项结构体
struct my_work {
struct work_struct work;
int data;
};
// 定义工作处理函数
static void my_work_func(struct work_struct *work)
{
struct my_work *my_work = container_of(work, struct my_work, work);
printk(KERN_INFO "Work queue: Data = %d\n", my_work->data);
}
// 模块初始化函数
static int __init my_module_init(void)
{
struct my_work *my_work;
printk(KERN_INFO "Module loaded\n");
// 创建工作项
my_work = kmalloc(sizeof(struct my_work), GFP_KERNEL);
if (!my_work) {
printk(KERN_ALERT "Failed to allocate memory\n");
return -ENOMEM;
}
my_work->data = 42; // 设置工作项数据
// 初始化工作项并设置处理函数
INIT_WORK(&my_work->work, my_work_func);
// 将工作项添加到工作队列中执行
schedule_work(&my_work->work);
return 0;
}
// 模块清理函数
static void __exit my_module_exit(void)
{
printk(KERN_INFO "Module unloaded\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Linux Work Queue Example");
这个代码创建了一个名为my_work的工作项,其中包含一个整数值data。在模块初始化函数中,我们使用kmalloc为工作项分配内存,并使用INIT_WORK宏初始化工作项,将处理函数my_work_func与工作项关联起来。然后,使用schedule_work函数将工作项添加到工作队列中执行。当工作项被处理时,处理函数my_work_func将被调用,并打印出工作项中的数据值。最后,在模块清理函数中释放分配的内存。
延时工作队列(Delayed Work Queue)是Linux内核中用于在指定时间后执行工作项的一种机制。它是在标准工作队列的基础上增加了延时功能,允许将工作项推迟到未来的某个时间点执行。以下是关于Linux延时工作队列的详解:
一、延时工作队列的创建和初始化
创建一个工作项结构体,通常使用struct delayed_work。
定义工作项的处理函数,该函数将在指定的延时时间后执行。
在模块初始化函数中,使用INIT_DELAYED_WORK宏初始化工作项,并将其与工作项处理函数关联起来。
二、设置延时时间和添加工作项到延时工作队列
使用schedule_delayed_work函数将工作项添加到延时工作队列中,并指定延时时间。该函数会使工作项在经过指定的延时时间后被调度执行。
三、取消延时工作项
如果需要在延时时间到达之前取消工作项的执行,可以使用cancel_delayed_work函数。该函数会取消指定的工作项,并防止其执行。
下面是一个简单的示例代码,演示了如何使用延时工作队列:
c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
// 定义延时工作项结构体
struct my_delayed_work {
struct delayed_work work;
int data;
};
// 定义工作项处理函数
static void my_delayed_work_func(struct work_struct *work)
{
struct my_delayed_work *my_delayed_work = container_of(work, struct my_delayed_work, work);
printk(KERN_INFO "Delayed Work Queue: Data = %d\n", my_delayed_work->data);
}
// 模块初始化函数
static int __init my_module_init(void)
{
struct my_delayed_work *my_delayed_work;
printk(KERN_INFO "Module loaded\n");
// 创建延时工作项并分配内存
my_delayed_work = kmalloc(sizeof(struct my_delayed_work), GFP_KERNEL);
if (!my_delayed_work) {
printk(KERN_ALERT "Failed to allocate memory\n");
return -ENOMEM;
}
my_delayed_work->data = 42; // 设置工作项数据
// 初始化延时工作项并设置处理函数
INIT_DELAYED_WORK(&my_delayed_work->work, my_delayed_work_func);
// 将延时工作项添加到延时工作队列,并设置延时时间为5秒(5000毫秒)
schedule_delayed_work(&my_delayed_work->work, msecs_to_jiffies(5000));
return 0;
}
// 模块清理函数
static void __exit my_module_exit(void)
{
// 在模块卸载时,确保所有的延时工作项都已执行完毕或已取消,并释放分配的内存。
printk(KERN_INFO "Module unloaded\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Linux Delayed Work Queue Example"