用一個工作佇列的例項來講解其使用
1. source code
#include <linux/init.h> #include <linux/module.h> #include <linux/device.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/kobject.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/time.h> #include <linux/time64.h> #include <linux/of.h> #include <linux/completion.h> #include <linux/mfd/core.h> #include <linux/kernel.h> #include <linux/seq_file.h> #include <linux/fs.h> #include <linux/proc_fs.h> #include <linux/sched.h> #include <linux/uaccess.h> struct work_struct work_demo; struct work_struct work_demo2; struct workqueue_struct *workqueue_demo; static void work_demo_func(struct work_struct *work) { printk("%s ,cpu id = %d,taskname = %s\n", __func__,raw_smp_processor_id(),current->comm); mdelay(1000*10); } static int workqueue_proc_show(struct seq_file *m, void *v) { printk("%s ,cpu id = %d\n",__func__,raw_smp_processor_id()); //queue_work(workqueue_demo,&work_demo); schedule_work(&work_demo); return 0; } static int workqueue_proc_open(struct inode *inode, struct file *file) { return single_open(file, workqueue_proc_show, NULL); } static ssize_t workqueue_proc_store(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { char buf[count]; copy_from_user(buf, buffer, count); if(buf[0] == '1') { printk("%s ,work_demo,cpu id = %d\n",__func__,raw_smp_processor_id()); queue_work(workqueue_demo,&work_demo); printk("queue work_demo end\n"); } else if(buf[0] == '2') { printk("%s ,work_demo2,cpu id = %d\n",__func__,raw_smp_processor_id()); queue_work(workqueue_demo,&work_demo2); } return count; } static const struct file_operations workqueue_proc_fops = { .open = workqueue_proc_open, .read = seq_read, .write = workqueue_proc_store, .llseek = seq_lseek, .release = single_release, }; static int __init workqueue_init(void) { INIT_WORK(&work_demo, work_demo_func); INIT_WORK(&work_demo2, work_demo_func); //workqueue_demo = create_workqueue("workqueue demo"); workqueue_demo = alloc_workqueue("workqueue demo", 0, 2); proc_create("workqueue", 0, NULL, &workqueue_proc_fops); return 0; } static void __exit workqueue_exit(void) { return ; } module_init(workqueue_init); module_exit(workqueue_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jon"); MODULE_ALIAS("platform"); MODULE_DESCRIPTION("workqueue demo driver");
2.測試過程
首先說明一下,如果呼叫的是“cat /proc/workqueue ",呼叫的是schedule_work,也就是使用系統的預設工作佇列。
隨後我們連續排程2次schedule_work看看會發生什麼
場景一:work_func中使用mdelay延遲10s,---- work_demo_func
/ # cat /proc/workqueue
workqueue_proc_show ,cpu id = 2
wake_up_worker
work_demo_func ,cpu id = 2,taskname = kworker/2:1
/ #
/ #
/ # cat /proc/workqueue
workqueue_proc_show ,cpu id = 3
no wake_up_worker
…
work_demo_func ,cpu id = 2,taskname = kworker/2:1
第一次排程後,由於work_func中排程的是mdelay,cpu 2在空轉,因此第二次排程工作佇列的時候執行的是cpu 3,但是由於同一個work當前在cpu 2的Normal執行緒池中執行,因此Linux系統將本次排程的work依然分發給了cpu 2的執行緒池指向的worklist,等待上一次的work執行完成後再接著執行。
場景二:work_func中使用msleep延遲10s, ----work_demo_func
/ # cat /proc/workqueue
workqueue_proc_show ,cpu id = 3
wake_up_worker
work_demo_func ,cpu id = 3,taskname = kworker/3:1
/ #
/ #
/ # cat /proc/workqueue
workqueue_proc_show ,cpu id = 3
wake_up_worker
/ #
…
work_demo_func ,cpu id = 3,taskname = kworker/3:1
第一次排程的時候,由於work_func中執行的是msleep,因此cpu 3的當前程序進入阻塞,程序發生切換。第二次排程的時候由於上一次是msleep,因此本次執行的還是cpu 3只是程序的id不同罷了,由於當前執行緒池的所有執行緒全部都阻塞了,系統判定需要喚醒新的idle執行緒來執行這個work,隨後在work_thread的具體處理(process_one_work)中,系統發現本次work的上一次排程在該cpu的執行緒池中還沒有執行完畢,因此將其插入到scheduled的連結串列後等待下次執行。
場景三:work_func中使用msleep,同時工作佇列採用自定義的PerCpu,max_active為2(意味著最多在每個CPU上併發2次)。同時採取2次排程不同的work。
/ # echo 1 > /proc/workqueue
workqueue_proc_store ,work_demo,cpu id =0
wake_up_worker
queue work_demo end
work_demo_func ,cpu id =0,taskname = kworker/0:1
/ #
/ # echo 2 > /proc/workqueue
workqueue_proc_store ,work_demo2,cpu id = 0
wake_up_worker
work_demo_func ,cpu id = 0,taskname = kworker/0:2
第一次排程的時候,核心是在cpu 0上排程的work_demo,由於work_func中執行的msleep,因此cpu 0在當前的程序中進入阻塞了,隨後程序傳送切換,第二次排程的時候由於上一次是msleep,因此本次執行的還是cpu 0只是程序的id不同罷了,由於當前執行緒池的所有執行緒全部阻塞了,系統判定需要喚醒新的idle執行緒來執行這個work,隨後在work_thread的具體處理(process_one_work)中,系統發現前後2次排程的是不同的work,滿足開新執行緒的條件,因此係統在當前CPU的執行緒池中開啟了一個新的執行緒kworker/0:2來執行這個work。
場景四:work_func中使用mdelay,同時工作佇列採用自定義的PerCpu,max_active為2(意味著最多在每個CPU上併發2次)。同時採取2次排程不同的work。
/ # echo 1 > /proc/workqueue
workqueue_proc_store ,work_demo,cpu id = 0
wake_up_worker
queue work_demo end
work_demo_func ,cpu id = 0,taskname = kworker/0:1
/ #
/ # echo 2 > /proc/workqueue
workqueue_proc_store ,work_demo2,cpu id = 2
wake_up_worker
work_demo_func ,cpu id = 2,taskname = kworker/2:1
第一次排程的時候,核心是在cpu 0上排程的work_demo,由於work_func中執行的mdelay,因此cpu 0在當前的程序中空轉,第二次排程的時候由於上一次是mdelay,因此本次執行的是cpu 2,由於當前執行緒池的所有執行緒全部阻塞了,系統判定需要喚醒新的idle執行緒來執行這個work,隨後在work_thread的具體處理(process_one_work)中,系統發現該work是第一次排程,滿足開新執行緒的條件,因此係統在當前CPU的執行緒池中開啟了一個新的執行緒kworker/2:1來執行這個work。