前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Linux内核23-工作队列

Linux内核23-工作队列

作者头像
Tupelo
发布于 2022-08-15 07:54:54
发布于 2022-08-15 07:54:54
1.2K00
代码可运行
举报
文章被收录于专栏:嵌入式ARM和Linux嵌入式ARM和Linux
运行总次数:0
代码可运行

1 工作队列

Linux2.6版本中引入了工作队列概念,代替Linux2.4版本中的任务队列。用以实现注册激活某些函数,留待稍后由工作线程执行(与tasklet的处理类似)。

虽然,tasklet之类的可延时函数和工作队列处理流程类似,但是却大有不同。主要的差别是可延时函数运行在中断上下文中,而工作队列中的函数运行在进程上下文中。在进程上下文运行是执行阻塞函数的唯一方式,因为中断上下文中不能发生进程切换。不论是可延时函数还是工作队列中的函数都不能访问进程的用户态地址空间,它们都运行在内核态。事实上,可延时函数并不知道当前正在运行的进程。另一方面,工作队列中函数由内核线程执行,所以也就没有用户态地址可以访问。

也就是说,工作队列的出现就是解决tasklet不能处理可阻塞函数的弊端。并且它们都是运行在内核态的程序,不能访问用户态地址空间。

1.1 工作队列数据结构

工作队列的主要数据结构workqueue_struct,其中,包含一个具有NR_CPUS个元素的数组。每个元素都是一个类型为cpu_workqueue_struct的描述符,其成员如下表所示:

表4-12 cpu_workqueue_struct结构成员

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
保护数据结构的自旋锁

worklist是一个双向链表,用来保存工作队列的待处理任务。每个待处理任务使用work_struct数据结构表示,成员如下表所示:

表4-13 work_struct成员

名称

描述

pending

1,表示处理函数已经在工作队列列表中

entry

指向函数列表中下一项或前一项

func

函数的地址

data

传给函数的数据

wq_data

指向父cpu_workqueue_struct描述符

timer

软件定时器,用于函数的延时执行

1.2 工作队列操作函数

我们已经了解了工作队列的原理,以及其数据结构。那么,当我们想要使用工作队列的时候,如何创建呢?

使用create_workqueue("foo")创建一个工作队列。foo是工作队列的名称,函数返回新创建的workqueue_struct的地址。该函数还会创建n个工作线程,n是CPU的数量,这些线程命令方式就是在传递的字符串foo后面加数字n表示:比如foo/0foo/1等等。create_singlethread_workqueue()只创建一个工作线程,其余一样。销毁工作队列使用destroy_workqueue()函数,参数是一个指向workqueue_struct结构的指针。

queue_work()函数插入一个函数到工作队列中(该函数已经被包含在work_struct描述符中了)。它的参数是指向workqueue_struct类型描述符的指针wq和指向work_struct描述符的指针work。它所执行的主要工作是:

  1. 检查待插入的函数是否已经在工作队列中。
  2. 添加work_struct描述符到工作队列列表中,设置work->pending为1。
  3. 唤醒more_work等待队列中休眠的工作线程。

queue_delayed_work()函数与queue_work()类似,除了接收第3个参数-延时时间(单位是系统嘀嗒-tick)之外。这个时间用来保证挂起函数执行之前最小延时时间。queue_delayed_work()依赖于work_struct描述符中的timer软件定时器,推迟将work_struct描述符插入到工作队列列表的时间。cancel_delayed_work()取消之前插入到工作队列中的函数,前提是work_struct描述符还没有被插入到工作队列中。

每个工作线程执行worker_thread函数,循环处理挂起的函数。但是,大部分时候,线程正在休眠并且需要处理的工作。一旦被唤醒,工作线程调用run_workqueue()函数,其实就是把work_struct描述符从该线程的工作队列列表中删除,并执行相应的函数。因为工作队列函数可以阻塞,所以工作线程可以休眠且当其被恢复执行时,可以切换到其它CPU上运行。

有时候,可能需要执行完所有的工作队列函数。可以调用flush_workqueue()函数,直到所有的函数执行完。但是,这个函数不会理会在它之后被加入工作队列的函数;对于新旧添加的函数可以通过cpu_workqueue_struct描述符中的remove_sequenceinsert_sequence成员标识。

2 预定义工作队列

大部分情况下,为了运行某个函数而创建一组工作线程是多余的。因此,内核提供了一个称为events的预定义工作队列,内核开发者可以自由使用。预定义工作队列不过就是一个标准工作队列,包含不同内核和驱动层的函数。它的workqueue_struct描述符存储在keventd_wq数组中。为了使用预定义工作队列,内核提供了一些辅助函数:

表4-14 预定义工作队列辅助函数

预定义工作队列函数

等价的标准工作队列函数

schedule_work(w)

queue_work(keventd_wq,w)

schedule_delayed_work(w,d)

queue_delayed_work(keventd_wq,w,d)(任何CPU)

schedule_delayed_work_on(cpu,w,d)

queue_delayed_work(keventd_wq,w,d)(给定CPU)

flush_scheduled_work()

flush_workqueue(keventd_wq)

预定义工作队列节省了系统资源。但是另一方面,在预定义工作队列中的函数不应该阻塞较长时间:因为每个CPU中的工作队列的函数执行都是串行化的,所以,长时间的阻塞耽误其它用户的使用。

除了通用的events队列,在Linux2.6内核中还可以发现一些特定的工作队列。最重要的是kblockd工作队列,由阻塞设备层使用。

3 总结

工作队列的场合比较适用于驱动程序开发。比如说阻塞设备驱动程序(硬盘写一块数据等),这样的驱动写操作不需要立即响应,但是需要阻塞操作,其它的写硬盘动作等待这次操作完成。就可以将这样的任务放入到工作队列中,等待系统不忙的时候再进行处理。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 嵌入式ARM和Linux 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Linux内核中的软中断、tasklet和工作队列具体解释
软中断、tasklet和工作队列并非Linux内核中一直存在的机制,而是由更早版本号的内核中的“下半部”(bottom half)演变而来。
全栈程序员站长
2022/07/20
2.3K0
Linux内核中的软中断、tasklet和工作队列具体解释
Linux设备驱动workqueue(工作队列)案例实现
工作队列(work queue)是另外一种将工作推后执行的形式,tasklet(小任务机制)有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。
杨源鑫
2019/07/04
5.4K0
Linux驱动开发-内核共享工作队列
工作队列常见的使用形式是配合中断使用,在中断的服务函数里无法调用会导致休眠的相关函数代码,有了工作队列机制以后,可以将需要执行的逻辑代码放在工作队列里执行,只需要在中断服务函数里触发即可,工作队列是允许被重新调度、睡眠。
DS小龙哥
2022/04/08
2.1K0
工作队列
API 路径:kernel/kernel/workqueue.c; kernel/include/linux/workqueue.h
开源519
2020/07/28
1K0
Linux驱动实践:中断处理中的【工作队列】 workqueue 是什么鬼?
大家好,我是道哥,今天我为大伙儿解说的技术知识点是:【中断处理中的下半部分机制-工作队列】。
IOT物联网小镇
2021/12/28
2K0
Linux驱动实践:中断处理中的【工作队列】 workqueue 是什么鬼?
workqueue
在之前的softirq中提到过,内核在中断的bottom half引入了softirq, tasklet, workqueue。 而softirq和tasklet只能用在中断上下文中,而且不可以睡眠。所以内核引入了workqueue,工作队列运行在进程上下文,同时可以睡眠。在以前版本的内核中workqueue的代码比较简单。在linux2.6.30代码量在1000行左右,而在linux3.18代码量在5000行左右。其中巨大的变化就是引入了Concurrency Managed Workqueue (cmwq)概念。不过本篇先学习以前版本的workqueue,因为它简单。在了解了简单版本的存在问题之后在学习cmwq就有更好的认识。
DragonKingZhu
2020/03/24
1.2K0
异步处理的强力助手:Linux Workqueue 机制详解
“ 在 Linux 内核中,workqueue 是一个重要的机制,用于处理那些不紧急且适合异步执行的任务。本文将详细介绍 workqueue 的基本概念及其在中断处理中的作用,揭示其如何通过后台线程提高系统的响应速度和性能。探讨 workqueue 的实现原理、创建与销毁过程,并讨论如何通过该机制优化系统任务调度。”
Lion Long
2024/08/01
6430
异步处理的强力助手:Linux Workqueue 机制详解
Linux的中断下半部机制的对比
 中断服务程序一般都是在中断请求关闭的条件下执行的,以避免嵌套而使中断控制复杂化。但是,中断是一个随机事件,它随时会到来,如果关中断的时间太长,CPU就不能及时响应其他的中断请求,从而造成中断的丢失。因此,Linux内核的目标就是尽可能快的处理完中断请求,尽其所能把更多的处理向后推迟。例如,假设一个数据块已经达到了网线,当中断控制器接受到这个中断请求信号时,Linux内核只是简单地标志数据到来了,然后让处理器恢复到它以前运行的状态,其余的处理稍后再进行(如把数据移入一个缓冲区,接受数据的进程就可以在缓冲区找到数据)。因此,内核把中断处理分为两部分:上半部(tophalf)和下半部(bottomhalf),上半部(就是中断服务程序)内核立即执行,而下半部(就是一些内核函数)留着稍后处理。
灯珑LoGin
2024/02/06
4360
Linux的中断下半部机制的对比
扒开 Linux 中断的底裤之 workqueue
workqueue 是除了 softirq 和 tasklet 以外最常用的下半部机制之一。workqueue 的本质是把 work 交给一个内核线程,在进程上下文调度的时候执行。因为这个特点,所以 workqueue 允许重新调度和睡眠,这种异步执行的进程上下文,能解决因为 softirq 和 tasklet 执行时间长而导致的系统实时性下降等问题。
刘盼
2021/08/25
2.3K0
扒开 Linux 中断的底裤之 workqueue
万字整理 | 深入理解工作队列
伟林,中年码农,从事过电信、手机、安全、芯片等行业,目前依旧从事Linux方向开发工作,个人爱好Linux相关知识分享,个人微博CSDN pwl999,欢迎大家关注! 1.1 worker_pool 1.1.1 normal worker_pool 1.1.2 unbound worker_pool 1.2 worker 1.2.1 worker处理work 1.2.2 worker_pool动态管理worker 1.2.3 cpu hotplug处理 1.3 workqueue 1.3.1 系统workq
刘盼
2022/08/26
1.8K0
万字整理 | 深入理解工作队列
zephyr 工作队列(work queue)
zephyr中工作队列是基于线程的,简单来说,就是有一个线程一直在等待工作队列的api发来的工作项,当有工作项时(一个待 执行的函数)就处理(把函数调用了),当有多个工作项时就按顺序处理,没有工作项时就休眠。
无限之生
2020/07/01
1.4K0
zephyr笔记 2.1.5 工作队列线程
我正在学习 Zephyr,一个很可能会用到很多物联网设备上的操作系统,如果你也感兴趣,可点此查看帖子zephyr学习笔记汇总。
twowinter
2020/04/17
8370
Linux内核22-软中断和tasklet
在之前的文章中,讲解中断处理相关的概念的时候,提到过有些任务不是紧急的,可以延后一段时间执行。因为中断服务例程都是顺序执行的,在响应一个中断的时候不应该被打断。相反,这些可延时任务执行时,可以使能中断。那么,将这些任务从中断处理程序中剥离出来,可以有效地保证内核对于中断响应时间尽可能短。这对于时间苛刻的应用来说,这是一个很重要的属性,尤其是那些要求中断请求必须在毫秒级别响应的应用。
Tupelo
2022/08/15
1.6K0
Linux内核中断顶半部和底半部的理解
  设备的中断会打断内核进程中的正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽量短小精悍。但是,这个良好的愿望往往与现实并不吻合。在大多数真实的系统中,当中断到来时,要完成的工作往往并不会是短小的,它可能要进行较大量的耗时处理。   下图描述了Linux内核的中断处理机制。为了在中断执行时间尽量短和中断处理需完成的工作尽量大之间找到一个平衡点,Linux将中断处理程序分解为两个半部:顶半部和底半部。
嵌入式与Linux那些事
2021/05/20
1.9K0
Linux内核中断顶半部和底半部的理解
Linux内核37-内核数据的同步访问
每一种技术的出现必然是因为某种需求。正因为人的本性是贪婪的,所以科技的创新才能日新月异。
Tupelo
2022/08/15
9300
[ Linux驱动炼成记 ] 12 -音频驱动TAS5754添加EQ参数
每一个带有音频播放的产品,设备初期的时候都会调试设备的EQ参数。EQ通过将声音中各频率的组成泛音等级加以修改,专为某一类音乐进行优化,增强人们的感觉。常见包括:正常、摇滚、流行、舞曲、古典、柔和、爵士、金属、重低音和自定义。1
程序手艺人
2019/02/20
1.4K0
《Essential Linux Dev
wq = create_singlethread_workqueue("mydrv");
py3study
2020/01/08
2.2K0
linux大量kworker_linux load average 非常高
最近遇到一个kworker问题,callstack如下,线程adas的陷入kernel space后会schedule_work调用一个while(1)的worker,kill adas后重新启动adas后adas线程会在调用dma_alloc_coherent的时候block住
全栈程序员站长
2022/11/08
3.8K0
linux大量kworker_linux load average 非常高
吐血整理 | 肝翻 Linux 中断所有知识点
GIC,Generic Interrupt Controller。是ARM公司提供的一个通用的中断控制器。主要作用为:接受硬件中断信号,并经过一定处理后,分发给对应的CPU进行处理。
刘盼
2021/08/25
4K1
吐血整理 | 肝翻 Linux 中断所有知识点
linux内核nfs rpc task处理分析
本文以centos7 3.10.0-957.21.3.el7.x86_64内核为例介绍linux内核nfs v4.0 处理状态为RPC_TASK_ASYNC的async rpc task的工作机制。
cdh
2023/12/20
6700
相关推荐
Linux内核中的软中断、tasklet和工作队列具体解释
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文