linux内核工作队列之work_struct学习总结(代码片段)

小麦大叔 小麦大叔     2023-01-19     737

关键词:

前言

编写Linux驱动的时候对于work_struct的使用还是很普遍的,很早之前就在阅读驱动源码的时候就看到了它的踪影,根据其命名大概知道了它的具体作用,但是仍然不知所以,同时,伴随出现的还有delayed_work以及workqueue_struct,抱着知其然并知其所以然的态度,在这里归纳总结一下work_struct,以及如何在驱动中使用,因为工作队列相对来说比较复杂,篇幅和能力有限,只能介绍相对重要的部分。

workqueue

内核里一直运行类似worker thread,它会对工作队列中的work进行处理,大致的工作流程原理可以参考下图所示;

在这里的work则是work_struct变量,并且绑定一个执行函数——typedef void (*work_func_t)(struct work_struct *work);。在worker thread中会对非空的工作队列进行工作队列的出队操作,并运行work绑定的函数。

work_struct

work_struct的数据结构如下,暂时我们还无法关注其原理,只关注如何去开启一个work

#include <linux/include/workqueue.h>

typedef void (*work_func_t)(struct work_struct *work);

struct work_struct 
	atomic_long_t data;
	struct list_head entry;
	work_func_t func;
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
;

通过数据结构可以知道,每当我们定义一个work_struct变量的时候,需要绑定一个work_func_t类型的函数指针。

函数功能
INIT_WORK(_work, _func)初始化一个work
INIT_WORK_ONSTACK(_work, _func)在栈上初始化一个work
flush_work(struct work_struct *work);销毁一个work
schedule_work(struct work_struct *work)调度一个work开始运行

例程

下面写了 一个demo,模块初始化之后会每隔5秒调度work开始运行,最终demo_work_func会运行规定的次数,并打印传递的参数和进程信息。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/time.h>

#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/slab.h> //kmalloc kfree

#include <linux/sched.h>
#include <linux/delay.h>

static char data[] = "test for demo work";

struct work_ctx
	struct work_struct real_work;
	char *str;
	int arg;
work_ctx;

struct work_ctx *demo_work;

static void demo_work_func(struct work_struct *work)

	struct work_ctx *temp_work = container_of(work,struct work_ctx,real_work);
	printk(KERN_INFO "[work]=> PID: %d; NAME: %s\\n", current->pid, current->comm);
	printk(KERN_INFO "[work]=> sleep 1 seconds\\n");
	set_current_state(TASK_INTERRUPTIBLE);
	schedule_timeout(1 * HZ); //Wait 1 seconds
	printk(KERN_INFO "[work]=> data is: %d  %s\\n", temp_work->arg,temp_work->str);


static int __init demo_thread_init(void)
	int count = 10;
	demo_work = kmalloc(sizeof(*demo_work),GFP_KERNEL);	
	INIT_WORK(&demo_work->real_work, demo_work_func);
	demo_work->str = data;
	while(count--)
		msleep(5000);
		demo_work->arg = count;
		schedule_work(&demo_work->real_work);

	
	return 0;


module_init(demo_thread_init);

static void __exit demo_thread_exit(void)
	flush_work(&demo_work->real_work);
	kfree(demo_work);

module_exit(demo_thread_exit);

MODULE_LICENSE("GPL");	

运行结果

[    8.500146] [work]=> PID: 37; NAME: kworker/0:1
[    8.500216] [work]=> sleep 1 seconds
[    9.499783] [work]=> data is: 9  test for demo work
[   13.503165] [work]=> PID: 37; NAME: kworker/0:1
[   13.503213] [work]=> sleep 1 seconds
[   14.503122] [work]=> data is: 8  test for demo work
[   18.506493] [work]=> PID: 37; NAME: kworker/0:1
[   18.506534] [work]=> sleep 1 seconds
[   19.506460] [work]=> data is: 7  test for demo work
[   23.509833] [work]=> PID: 37; NAME: kworker/0:1
[   23.509874] [work]=> sleep 1 seconds
[   24.510060] [work]=> data is: 6  test for demo work
[   28.513161] [work]=> PID: 37; NAME: kworker/0:1
[   28.513206] [work]=> sleep 1 seconds
[   29.513121] [work]=> data is: 5  test for demo work
[   33.516502] [work]=> PID: 37; NAME: kworker/0:1
[   33.516545] [work]=> sleep 1 seconds
[   34.516452] [work]=> data is: 4  test for demo work
[   38.519819] [work]=> PID: 37; NAME: kworker/0:1
[   38.519860] [work]=> sleep 1 seconds
[   39.519782] [work]=> data is: 3  test for demo work
[   43.523151] [work]=> PID: 37; NAME: kworker/0:1
[   43.523191] [work]=> sleep 1 seconds
[   44.523117] [work]=> data is: 2  test for demo work
[   48.526495] [work]=> PID: 37; NAME: kworker/0:1
[   48.526542] [work]=> sleep 1 seconds
[   49.526444] [work]=> data is: 1  test for demo work
[   53.539699] [work]=> PID: 37; NAME: kworker/0:1
[   53.539763] [work]=> sleep 1 seconds
[   54.542925] [work]=> data is: 0  test for demo work

参考

https://www.oreilly.com/library/view/understanding-the-linux/0596005652/ch04s08.html

https://kukuruku.co/post/multitasking-in-the-linux-kernel-workqueues/

linux-5.10.13内核完全注释之工作队列

https://gitee.com/rtoax/linux-5.10.13dd80ac94472de4c9a60a3329ccb72345a00f1a3d1a3637116472a48c4ac1b6f822aa5b12d85c452a2bb032d75ac48a5eba04bbf25e7373d8a10490a5f1c3810e292e3254463a38c704802e91a5ae91e2c7c 查看详情

如何将 NAPI 轮询功能卸载到工作队列

...工作队列。我的问题是:1.如何将NAPI轮询函数参数传递给work_struct?2.如何从work_struct中取回NAPI轮询函数参数?(与上述Q 查看详情

驱动学学之驱动学习的准备工作

...的linux的zImage必须是自己编译的,不能是别人编译的。(2)内核源码树,其实就是一个经过了配置编译之后的内核源码。这里使用的是九鼎官方提供的kener,因为这个内核已经是移植好的,所以只需要直接编译即可。首先makedistclean... 查看详情

linux内核数据结构之等待队列

Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。在这个链表中,有两种数据结构:等待队列头(wait_queue_head_t)和等待队列项(... 查看详情

linux——linux驱动之gpio中断的应用实战(下)(中断下文之工作队列的使用,处理非常复杂且耗时的操作)

【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来!《QT开发实战》《嵌入式通用开发实战》《从0到1学习嵌入式Linux开发》 查看详情

工作队列学习

首先要注意本文的两个概念:(1)使用内核提供的工作队列,(2)自己创建工作队列http://blog.csdn.net/fontlose/article/details/8286445工作队列是一种将工作推后执行的形式,交由一个内核线程去执行在进程上下文执行,其不能访问用户空间。... 查看详情

linux内核的基础设施:工作队列等待队列(代码片段)

文章目录1.前言2.内核睡眠机制3.等待队列3.1静态声明:3.2动态声明:3.3阻塞:3.4解除阻塞:4.工作队列5.Demo演示5.1完整源码:test\\_wait.c5.2编译添加:Makefile5.3运行打印6.案例修改:让唤醒条件始终为False6.1源... 查看详情

《linux操作系统分析》之linux系统的理解及学习linux内核的心得

...行第二篇:《Linux操作系统分析》之分析精简的Linux的内核中断和时间片轮询第三篇:《Linux操作系统分析》之跟踪分析Linux内核的启动过程第四篇:《Linux操作系统分析》之使用库函数API和C代码中嵌入汇编代码两种方... 查看详情

linux内核中的软中断tasklet和工作队列详解(超详细~)(代码片段)

...址:https://www.bilibili.com/read/cv17094615本文基于Linux2.6.32内核版本。引言软中断、tasklet和工作队列并不是Linux内核中一直存在的机制,而是由更早版本的内核中的“下半部”(bottomhalf)演变而来。下半部的机制实际上... 查看详情

linux内核数据结构之等待队列

Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。在这个链表中,有两种数据结构:等待队列头(wait_queue_head_t)和等待队列项(... 查看详情

sourceinsight建工程之kernel

不管你是从事于Linux内核工作还是出于兴趣爱好,Linux内核源码都是非常好的学习资源。意味着就要经常的和内核源码大交道,那么软件工具就是少不了的。在Windows系统上确实有着许多好用的软件,这点还是可以肯定的。对于像... 查看详情

《linux内核设计与实现》学习笔记——i/o调度算法

I/O调度子系统用于调度来自多个进程对块设备的I/O请求。电梯调度首先,如果队列中已存在一个对相邻磁盘扇区操作的请求,那么新请求将和这个已经存在的请求合并为一个请求。2.如果队列中存在一个驻留时间过长的请求,那... 查看详情

linux驱动开发-中断分层机制_工作队列笔记7(代码片段)

...1.工作队列概念:a)任务推后执行b)被推后的任务由“内核线程”执行c)允许重新调度和睡眠d)这些被推后的工作组成一个双向链表,既称为工作队列2.运行机制注释:1.每个CPU都有独立的工作队列2.每个工作队列形成一... 查看详情

linux——linux驱动之使用等待队列降低cpu的占用率应用实战(阻塞与非阻塞等待队列的基本概念相关函数代码实战)

【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来!《QT开发实战》《嵌入式通用开发实战》《从0到1学习嵌入式Linux开发》 查看详情

linux驱动开发-中断分层机制_工作队列笔记7(代码片段)

...1.工作队列概念:a)任务推后执行b)被推后的任务由“内核线程”执行c)允许重新调度和睡眠d)这些被推后的工作组成一个双向链表,既称为工作队列2.运行机制注释:1.每个CPU都有独立的工作队列2.每个工作队列形成一... 查看详情

linux网络协议栈之内核锁——进程调度

   学习内核锁之前,我们先了解一下关于内核的基础知识,以便更好的学习内核锁的使用!   下面我们先学习一下进程调度:一、进程调度调度什么时候发生?即:schedule()函数什么时候被调用?调... 查看详情

linux驱动实践:中断处理中的工作队列workqueue是什么鬼?(代码片段)

...两个重要的结构体:工作队列(workqueue_struct)和工作项(work_struct) 查看详情

linux进程管理之进程调度与切换

...关调度相关的结构保存在task_struct中,如下:active_mm是为内核线程而引入的,因为内核线程没有自己的地址空间,为了让内核线程与普通进程具有统一的上下文切换方式,当内核线程进行上下文切换时,让切换进来的线程的active_... 查看详情