Linux内核23-工作队列

简介: Linux内核23-工作队列

1 工作队列


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

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

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


1.1 工作队列数据结构


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

表4-12 cpu_workqueue_struct结构成员

名称 描述
lock 保护数据结构的自旋锁
remove_sequence flush_workqueue()使用的序列号
insert_sequence flush_workqueue()使用的序列号
worklist 挂起函数列表的head
more_work 休眠中的工作线程等待队列
work_done 等待从工作队列中刷新的进程队列
wq 指向包含描述符的workqueue_struct结构
thread 该数据结构的工作线程的进程描述符
run_depth run_workqueue()执行深度

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 总结


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

相关文章
|
20天前
|
Linux C语言
Linux内核队列queue.h
Linux内核队列queue.h
|
1月前
|
存储 Shell Linux
【Shell 命令集合 系统设置 】Linux 生成并更新内核模块的依赖 depmod命令 使用指南
【Shell 命令集合 系统设置 】Linux 生成并更新内核模块的依赖 depmod命令 使用指南
35 0
|
1月前
|
Shell Linux C语言
【Shell 命令集合 系统设置 】⭐Linux 卸载已加载的内核模块rmmod命令 使用指南
【Shell 命令集合 系统设置 】⭐Linux 卸载已加载的内核模块rmmod命令 使用指南
30 1
|
21天前
|
存储 Linux
linux查看系统版本、内核信息、操作系统类型版本
linux查看系统版本、内核信息、操作系统类型版本
55 9
|
29天前
|
Ubuntu Linux
linux查看系统版本及内核信息
在Linux中检查系统版本和内核信息,可使用`uname -r`查看内核版本,`uname -a`获取详细信息,或者查看`/proc/version`。要了解发行版版本,尝试`lsb_release -a`(如果安装了)或查阅`/etc/os-release`。Red Hat家族用`/etc/redhat-release`,Debian和Ubuntu系用`/etc/issue`及相关文件。不同发行版可能需不同命令。
32 3
|
1天前
|
弹性计算 网络协议 Shell
自动优化Linux 内核参数
【4月更文挑战第29天】
5 1
|
2天前
|
弹性计算 网络协议 Linux
自动优化 Linux 内核参数
【4月更文挑战第28天】
7 0
|
13天前
|
算法 Linux 调度
深入理解Linux内核的进程调度机制
【4月更文挑战第17天】在多任务操作系统中,进程调度是核心功能之一,它决定了处理机资源的分配。本文旨在剖析Linux操作系统内核的进程调度机制,详细讨论其调度策略、调度算法及实现原理,并探讨了其对系统性能的影响。通过分析CFS(完全公平调度器)和实时调度策略,揭示了Linux如何在保证响应速度与公平性之间取得平衡。文章还将评估最新的调度技术趋势,如容器化和云计算环境下的调度优化。
|
18天前
|
算法 Linux 调度
深度解析:Linux内核的进程调度机制
【4月更文挑战第12天】 在多任务操作系统如Linux中,进程调度机制是系统的核心组成部分之一,它决定了处理器资源如何分配给多个竞争的进程。本文深入探讨了Linux内核中的进程调度策略和相关算法,包括其设计哲学、实现原理及对系统性能的影响。通过分析进程调度器的工作原理,我们能够理解操作系统如何平衡效率、公平性和响应性,进而优化系统表现和用户体验。
|
25天前
|
负载均衡 算法 Linux
深度解析:Linux内核调度器的演变与优化策略
【4月更文挑战第5天】 在本文中,我们将深入探讨Linux操作系统的核心组成部分——内核调度器。文章将首先回顾Linux内核调度器的发展历程,从早期的简单轮转调度(Round Robin)到现代的完全公平调度器(Completely Fair Scheduler, CFS)。接着,分析当前CFS面临的挑战以及社区提出的各种优化方案,最后提出未来可能的发展趋势和研究方向。通过本文,读者将对Linux调度器的原理、实现及其优化有一个全面的认识。