关键词:
前言
编写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_... 查看详情