本期主题:
讲清workqueue和waitqueu:
往期链接:
从操作系统角度而言,实际上有两种中断:
我们来分析一下 中断处理程序 的需求
需求:
- 中断处理程序需要尽可能简短,这样能够快速处理中断;
- 允许在处理A中断时,被B中断打断然后去处理B的事情,允许中断的嵌套;
由于有快速、简短这样的需求,所以操作系统设计出了这样的机制:
软中断就是这样的一个作用,可以使操作延时去做
;
workqueue顾名思义就是工作队列,他有几个特点:
异步执行
的场景;workqueue就相当于在worklist(工作链表)上挂上了一个个的work item(工作节点),每次都从中取出这些item来运行
讲解以下几个API:
- workqueue的结构体定义
- work_struct的结构体定义(work item)
- delay_work接口
1. workqueue定义:
//kernel/workqueue.c
其中的pwqs是 pool workqueues,代表着这个workqueue所管理的pool,pool在这里的意思理解是一个池子,存放着当前wq的信息
2. workstruct定义
其中的func就是work_item的callback函数,entry就是添加到前面的workqueue_strcut的pwqs list中,然后遍历
3. queue_delayed_work
看一个例子,我们写一个ko,实现的功能如下:
#include
#include
#include
static void mykmod_work_handler(struct work_struct *w);
static struct workqueue_struct *wq = 0;
static DECLARE_DELAYED_WORK(mykmod_work, mykmod_work_handler);
static unsigned long onesec;
static void
mykmod_work_handler(struct work_struct *w)
{
pr_info("mykmod work %u jiffies\n", (unsigned)onesec);
}
static int mykmod_init(void)
{
onesec = msecs_to_jiffies(1000);
pr_info("mykmod loaded %u jiffies\n", (unsigned)onesec);
wq = create_singlethread_workqueue("mykmod");
if (!wq)
pr_err("wq init failed!\n");
queue_delayed_work(wq, &mykmod_work, onesec);
return 0;
}
static void mykmod_exit(void)
{
if (wq) {
cancel_delayed_work_sync(&mykmod_work);
destroy_workqueue(wq);
}
pr_info("mykmod exit\n");
}
module_init(mykmod_init);
module_exit(mykmod_exit);
MODULE_DESCRIPTION("mykmod");
MODULE_LICENSE("GPL");
等待队列用于在进程中等待某一特定事件发生,无须频繁轮询。进程在等待期间休眠,事件发生时,内核唤醒进程。总结一下有几个特点:
介绍waitqueue结构体以及下面三种API:
1.waitqueue结构体
2. 初始化等待队列,DECLARE_WAIT_QUEUE_HEAD
3. 将进程加入等待队列,wait_event
4. 唤醒等待队列中的进程,wakeup
2. 初始化等待队列
初始化等待队列,实际上就是waitqueue_head的初始化,可以使用静态初始化也可使用动态初始化;
3. 将进程加入等待队列
通过遍历wq_head来找到entry,并且调用entry的func;
例子讲解:
- 创建一个静态等待队列,并在Moudule init的时候建立一个内核进程;
- 注册好proc_fs,上层通过Cat读取/proc文件时,会将flag置1,并wakeup等待进程;
- 在rmmod 模块时,会将flag置2,并wakup等待进程,此时内核进程退出;
下面是代码以及测试结果:
#include
#include
#include
#include
#include
#include
#include
static int read_count = 0;
static struct task_struct *wait_thread;
// Initializing waitqueue statically
DECLARE_WAIT_QUEUE_HEAD(test_waitqueue);
static int wait_queue_flag = 0;
static int my_waitqueue_show(struct seq_file *m, void *v)
{
printk(KERN_ALERT "Read function\n");
seq_printf(m, "read_count = %d\n", read_count);
wait_queue_flag = 1;
wake_up_interruptible(&test_waitqueue); // wake up only one process from wait queue
return 0;
}
static int my_waitqueue_open(struct inode *inode, struct file *filp)
{
return single_open(filp, my_waitqueue_show, NULL);
}
static struct proc_fs test_wait_queue_fops = {
.proc_open = my_waitqueue_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
static int wait_function(void *unused)
{
while(1) {
printk(KERN_ALERT "Waiting For Event...\n");
// sleep until wait_queue_flag != 0
wait_event_interruptible(test_waitqueue, wait_queue_flag != 0);
if (wait_queue_flag == 2) {
printk(KERN_ALERT "Event Came From Exit Function\n");
return 0;
}
printk(KERN_ALERT "Event Came From Read Function - %d\n", ++read_count);
wait_queue_flag = 0;
}
return 0;
}
static int mywaitqueue_init(void)
{
struct proc_dir_entry *pe;
printk(KERN_ALERT "[Hello] mywaitqueue \n");
pe = proc_create("test_wait_queue", 0644, NULL, &test_wait_queue_fops);
if (!pe)
return -ENOMEM;
// Create the kernel thread with name "MyWaitThread"
wait_thread = kthread_create(wait_function, NULL, "MyWaitThread");
if (wait_thread) {
printk(KERN_ALERT "Thread created successfully\n");
wake_up_process(wait_thread);
} else {
printk(KERN_ALERT "Thread creation failed\n");
}
return 0;
}
static void mywaitqueue_exit(void)
{
wait_queue_flag = 2;
wake_up_interruptible(&test_waitqueue);
printk(KERN_ALERT "[Goodbye] mywaitqueue\n");
remove_proc_entry("test_wait_queue", NULL);
}
module_init(mywaitqueue_init);
module_exit(mywaitqueue_exit);
MODULE_LICENSE("GPL");
waitqueue
和 workqueue
是实时操作系统中用于线程间通信和任务调度的两种机制,它们有一些区别和不同的应用场景。
waitqueue
是一种线程等待队列,用于实现线程间的同步和通信。它允许线程等待某个条件的发生,并在条件满足时被唤醒继续执行。通过将线程放入等待队列中,可以避免线程在条件不满足时的忙等待,提高系统的效率和资源利用率。waitqueue
主要用于实现线程间的同步和事件通知,例如一个线程等待某个资源的可用性或某个事件的发生。
workqueue
是一种任务队列,用于调度和执行后台任务。它可以在后台异步执行一些耗时的操作,而不阻塞当前线程的执行。workqueue
将任务添加到队列中,并由系统自动调度和执行。相比于直接在当前线程中执行任务,使用 workqueue
可以将任务的执行延迟到后台,提高系统的响应性和并发性。workqueue
主要用于处理一些耗时的、非实时性的任务,例如文件系统操作、网络请求、数据处理等。
下面是 waitqueue
和 workqueue
的一些主要差异:
功能目的:waitqueue
用于线程间的同步和通信,而 workqueue
用于后台任务的调度和执行。
调度方式:waitqueue
中的线程需要显式地被唤醒,可以通过条件的满足或其他线程的通知来触发唤醒操作;而 workqueue
中的任务由系统自动调度,无需显式触发。
使用场景:waitqueue
主要用于实现同步和事件通知的场景,例如等待某个资源的可用性或某个事件的发生;而 workqueue
主要用于执行后台任务,例如文件系统操作、网络请求、数据处理等。