1. 程式人生 > >用一個工作佇列的例項來講解其使用

用一個工作佇列的例項來講解其使用

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。