LWN:关于preempt_count()的四个小讨论!

浅析Linux进程管理:preempt_count抢占计数器 Linux系统在运行时,总会处于某一种特定的上下文中,例如进程上下文、中断上下文等,为了判断系统当前运行的上下文状态,内核提供了preempt_count变量来记录当前运行的上下文信息。 阅读详情

关注了就能看到更多这么棒的文章哦~

Four short stories about preempt_count()

By Jonathan Corbet
September 18, 2020
https://lwn.net/Articles/831678/
DeepL assisted translation

这个话题是从 Thomas Gleixner 的一个简单 patch 开始的,它对 preemption count(抢占计数)的处理方式做了一个小小的改动。由它引起的讨论迅速蔓延开来,在很少几个邮件中就涵盖了许多与内核开发相关的问题。其中每一个话题都值得看一下,我们从 preemption counter 本身的工作方式开始吧。有时候,一个简单的计数其实也并不像看起来那么简单。

preempt_count()

在像 Linux 这样的多任务系统中,没有任何一个线程可以保证它每次想运行的时候都能独占处理器。内核总是有能力(多数情况下)抢占一个正在运行的线程,而选择一个优先级更高的线程来执行。那个新线程可能是另一个不同的进程,但也可能是一个硬件中断,或者什么其他外部事件。为了正确地协调系统中所有任务能正确运行,内核必须跟踪当前的执行状态(execution state),包括已经被抢占或可能阻止线程被抢占的各种情况。

用来进行这个追踪记录的基础,就是在系统中每个任务里存储的 preemption counter。这个计数器是通过 preempt_count() 函数来访问的,它的通用定义是这样的:

static __always_inline int preempt_count(void)
{
    return READ_ONCE(current_thread_info()->preempt_count);
}

这个 counter 可以用来指示当前线程的状态、它是否可以被抢占,以及它是否被允睡眠。要实现这个功能的话,就必须在这个 counter 里面记录若干种不同状态,因此这个 preempt_count 也被分成了几个字段(sub-fields):

 [preempt_count]

最低位的这个 byte 是用来记录 preempt_disable()嵌套调用的次数,也就是到目前为止 preemption 被 disable 的次数。接下来的三个字段记录了当前正在运行的线程被软中断、硬中断和 NMI(non-maskable interrupt,不可屏蔽中断)打断的次数。这里预留的 bit 所支持的最大值可能大大超过了实际执行中可能会发生的中断次数,不过反正这里 bit 也有富余。最后,最高位表示内核是否已经决定当前进程需要在后面执行时一有机会就马上被调度出去,让给其他任务。

只要看一下 preempt_count 的值,内核就可知道当前的情况如何。比如,preempt_count 是非零值,就表示当前线程不能被 scheduler 抢占:要么是 preemption 已经被明确 disable 了,要么是 CPU 当前正在处理某种中断。同理,非零值也表示当前线程不能睡眠,因为它此刻在运行的上下文必须要持续执行完成。"reschedule needed" 这个 bit 告诉内核,当前有一个优先级较高的进程应该在第一时间获得 CPU。必须要在 preempt_count 为非零值的情况下,才会设置这个 bit。否则的话,内核早就可以直接对这个进程进行 prempt 抢占,而不是设置此 bit 并等待。

内核代码到处都在使用 preempt_count 来决定在某个时刻可以进行哪些操作。不过目前看来这可能是有问题的,原因有以下几点。

Misleading counts

preempt_disable()只适用于线程在 kernel 里运行的情况。而用户空间的代码则总是可以被抢占的。在很久很久以前,内核根本不支持对内核空间代码的抢占。而当增加这个功能的时候(目的是作为改善延迟的一种可选功能),它也是可以配置来打开或者关闭的。因此,仍然有一些系统在运行时完全不需要支持 kernel preemption(内核抢占)。对于一些非常重视吞吐量的工作场景来说,这种配置也是很有道理的。

如果内核代码不能被抢占,那么就没有必要跟踪记录 preempt_disable()了。抢占是永远被关闭。所以内核不会浪费时间去维护这些信息。因此此时 preempt_count 的 preempt-disable 这几个 bit 总是为零。preemptible()函数将总是返回 false,因为内核确实是不可抢占的。这一切似乎都很合理。

不过这会导致一些问题。首先,像 in_atomic()这样的函数(代表内核当前是否在原子上下文中运行)的行为会有变化。在一个配置了 preemption 的内核上,调用 preempt_disable()会导致 in_atomic()返回 true。而如果内核配置中关闭了 preemption,那么 preempt_disable()就是一个 no-op 操作,什么都不做,in_atomic()则会返回 false。在某些情况下,例如当 spinlocks 被持有时(这种情况确实是 atomic context),in_atomic()却会由于这个原因而返回 false。

Gleixner 在他的 patch 中指出了这种不一致的现象所导致的其他一些问题,认为是一个整体问题。

缺乏一个对所有 kernel config 组合都有效的标记方式,这会一直给我们带来麻烦,因为开发人员要么不理解其中的含义,要么试图用奇怪的方式来解决这种不一致。

他的解决方案是去掉 preemption-disable 情况下的条件编译代码,这样一来,这个 counter 即使在不支持 kernel preemption 的内核中也得到了正确的维护。这样一来,在关闭了抢占的机器上会增加一些执行时间,也会增加一些额外代码,但 Gleixner 说,在他的 benchmark 中 "没有测出来可见的变化"。

Linus Torvalds 尚未被说服,他认为,当不可能进行抢占时,spinlock 的代码应该会更加简洁高效。Gleixner 重申,测量中看不出差异。Torvalds 也承认这组 patch 确实让代码变得更简单,"有非常明显的吸引力"。

Using preempt_count

不过 Torvalds 主要的抱怨是关于 preempt_count 会需要根据上下文来改变行为。他说,这样的代码 "根本上就是错误的"。他说,所有根据上下文改变其行为的代码,都应该将上下文作为一个参数传递进来,这样调用者才知道会发生什么。因此,给内存分配函数加上 GFP_ATOMIC 标志是可以接受的,但根据 in_atomic()的返回值来改变行为是不可取的。

在某种程度上,人们普遍同意这个看法。Gleixner 的 patch 邮件中包含了一段信息,介绍说未来计划对 in_atomic()等函数的调用者进行审查和修复,他说,"使用了错误用法的地方显然占绝大多数"。Daniel Vetter 补充说,根据他的经验,"试图根据运行的上下文巧妙地调整其行为的代码更难理解,而且会导致各种有趣的问题"。

Paul McKenney 则认为,有些代码必须能够在不同的上下文中正常运行,否则会导致需要太多的 API:

现在也许你喜欢在可以进行调度的上下文中使用 call_rcu()、在禁用抢占时使用 call_rcu_nosched()、在禁用中断时使用 call_rcu_irqs_are_disabled()、在持有 raw spinlock 的上下文中使用 call_rcu_raw_atomic()等等。然而,从我看到的情况来看,大多数人反而一致倾向于 RCU API 能被整合起来。

对此,Torvalds 澄清说他认为 core-kernel 的代码的要求与其他代码不同。核心代码要处理多个上下文,应该总要能正确工作。而驱动中的代码,则不应该根据上下文来改变自己的行为。

在这个讨论分支中,没有得出任何切实的结论。不过很有可能,今后这些具有上下文依赖行为的代码会得到更多的注意。

Questioning high memory

在讨论的早期阶段,Gleixner 指出了一个在加密(crypto)代码中使用 preempt_count 的值得商榷的例子。这部分代码,如果判断出当前是不可抢占的环境,那么它会以很奇怪的方式来改变内存分配模式。经过一番讨论,据 Ard Biesheuvel 说,原来主要目的是尽可能避免使用 kmap_atomic()。

对于那些对 kmap_atomic()不是很熟悉的人来说,看看这篇关于 high memory 的文章(https://lwn.net/Articles/813201/ )会有帮助。简而言之:32 位机器只能将有限的内存映射到内核的地址空间中,在大多数架构和配置中,都只映射了不超过 1GB 的空间。任何没有被直接映射的内存都被认为是 "high memory",high memory 中的任何 page 都必须在内核访问它的内容之前被显式(临时地)映射到内核中。函数 kmap()和 kmap_atomic()就是用来进行这种映射的。

这两个函数有一些区别,首先,只有 kmap_atomic() 可以在 atomic context 中调用。此外,kmap_atomic()的效率更高,因此被认为在任何可以使用它的情况下都是强烈推荐优先使用它的,无论调用者在调用前是否在 atomic context 中运行(在创建 mapping 的时候 CPU 始终都是在 atomic context 中运行的)。不过正如 Biesheuvel 所指出的,文档中并没有指出这种建议,而是鼓励使用 kmap(),所以他就是这么做的。

他补充说,kmap() 还有一个优势:调用 kmap_atomic()的话,哪怕在 64 位架构上也会禁用抢占,而 64 位架构上不存在 high memory,也就不需要临时创建 mapping。使用 kmap_atomic()将导致 WireGuard VPN 的大部分代码运行都关闭了 preemption,这是完全没有必要的。Torvalds 指出,这么做是有原因的:那些在带有 high memory 的 32 位机器上无法工作的代码,这样在 64 位机器上也就同样会报出错误了。这本质上是一种调试辅助手段,可以减少因为很少有开发者在用 32 位机器而遗漏的 bug。

Gleixner 说,在 64 位系统上优化 kmap_atomic()的方法之一,是让 kmap_atomic()可以被 preempt,换句话说,也就是不再是作为原子操作。他说,这种方法已经在 realtime kernel 中得到采用,"这并没有导致太大问题"。这里付出的代价仅仅是导致 kmap_atomic()在使用 high memory 的系统上会慢一点。

这种代价,在开发社区中似乎越来越多人愿意付出了。Torvalds 回答说,他希望能开始彻底取消 kmap()的工作。32-Bit 系统还将存在一段时间,但它们越来越不可能被用在需要大量内存的情况下。或者,正如 Torvalds 所说 "并不是 32 位系统不重要,而是 32 位系统天生不适合大内存的场景"。每当人们意识到支持 high memory 的成本(它给内存管理子系统增加了大量的复杂性)时,就会越来越强烈地希望把它拿掉。

当然,没有人会马上来删除 high memory 的代码。但那些让 high memory 系统更难用、而有利于现在使用的主流系统的改动(比如让 kmap_atomic()不再是原子的),越来越有可能被接受合并入 mainline。同时,围绕 preempt_count 的其他问题大多仍未解决,但最终很有可能这些带来正确性并降低复杂性的改动会能够取得胜利。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

VMware VSAN 高级参数介绍(测试用) VSAN 的策略驱动让我们看到了VSAN无穷的潜力,其设计思路相当新颖,在一个基础资源池上使用策略就能实现不同对象的区分服务,而目前没有其他厂家能到VMware这样的高度。 VSAN 呈现给用户配置很简单,但实际上设计及后台比较复杂。笔者无意间发现了VSAN的隐藏高级参数设置,有很多好玩的选项想分享出来。这些参数全部是针对每主机进行设置的。VSAN 中有很多默认设置,这些设置应该都是研发人员多次测试得出的最优值,修改可能导致环境异常,并不建议修改! 目录 参数1:全闪配置下 SSD 写缓存最大. 阅读详情

相关推荐

树莓派驱动步进电机

步进电机一般由电机、驱动器构成,树莓派连接驱动器控制电机。 一、接线 DIR+/- 用于控制方向,PUL+/- 用于控制转动; 正极DIR+ 和 PUL+ 接到GPIO口,两个负极接到GPIO口或地线均可,控制的时候不使用。 实验中,DIR+ 接35口,PUL+ 接38口。 树莓派4B GPIO口如图所示 远离树莓派USB的一侧 是 1、2、3号口 二、驱动 DIR控制方向,使用高电平或低电平控制; PUL控制转动,使用脉冲驱动。 三、代码 import RPi.GPI.

weixin_42337765的博客 1万+

preempt_count

定义 /* * low level task data that entry.S needs immediate access to. * __switch_to() assumes cpu_context follows immediately after cpu_domain. */ struct thread_info { unsigned long flags; /* low

半月旋空 5625

流形优化: Manifold Optimization 的 全网最通俗版本详解 (一)

前言 之前忙于论文, 停更了很久。 接下来的一段时间,出于各种原因, 决定在优快云平台上做 最近在 混合波束成形 及 智能反射面 领域 非常常见的流形优化 (Manifold Optimization) 算法 的专题介绍。尽管目前已有了不少的中文博客, 但出于其各种弊端, 使得本篇的成文仍有其不可或缺的意义。 而如果要盘点最核心的意义, 就在于这篇是对读者的 数学能力 与 阅读理解能力 要求最低的 流形优化讲解了。 唯一参考的文献为 Manopt 工具包作者 Boumal 的 新书: An introd

B417科研笔记 2万+

preempt_count说明

linux系统在运行时,总会处于某一种特定的上下文中,例如,进程上下文,中断上下文等。为了判断系统当前运行的上下文状态,内核提供了preempt_count变量来记录当前系统运行的上下文信息。 注意,对于preempt_count的操作一般都是在最外层包了一层皮来关闭中断的情况下进行的,操作完成后开启中断。

Amelio Ming 611

全球、中国各省市县等行政区划(含十段线)shape及geosjon数据免费下载

免费下载全球、中国各省市县等行政区划(含十段线)shape及geosjon数据

mhf403445651的博客 3458

T分布和T检验的理解,Python代码实现T检验的计算

每天学习一点,每天进步一点。 声明:本人所有的原创,都是自己在学习过程中的记录点滴,不一定都是对的,肯定也会有一些错误的想法。所以大家看一看就好,不可尽信。当然也欢迎指出。 T分布 定义,有来自标准正态分布的样本X ~ N(0,1),和来自卡方n分布的Y ~ X2(n), 那么有 Z= X/√(Y/n) 成为符合自由度为n的T分布 我们用Python代码画一下他的图形 # 给一个自由度,返回这个自由度的卡方分布 X²(n) def product(n): n = np.ceil(n).astype

PY_smallH的博客 8485

Linux中的preempt_count

在中断上下文中,调度是关闭的,不会发生进程的切换,这属于一种隐式的禁止调度,而在代码中,也可以使用preempt_disable()来显示地关闭调度,关闭次数由第0到7个bits组成的preemption count(注意不是preempt count)来记录。preempt_count中的第8到15个bit表示softirq count,它记录了进入softirq的嵌套次数,如果softirq count的值为正数,说明现在正处于softirq上下文中。的宏专门用来判断当前是否在中断上下文中。...

szembed的博客 543

PoP(Package on Package)封装技术探讨

PoP(Package on Package)封装技术摘要 PoP是一种3D封装技术,通过垂直堆叠已完成封装的器件(如SoC+DRAM)实现高密度集成,典型应用于智能手机等空间受限场景。其核心优势包括:节省主板面积(垂直堆叠)、优化高速信号完整性(短互连路径)、提升供应链灵活性(独立测试与选型)。PoP结构分为底封装(通常为Flip-Chip BGA的SoC)和顶封装(如FBGA存储芯片),通过焊球阵列互连,一次回流焊完成组装。

sinat_15150363的博客 366

中断上下文中的preempt count

参考博客: Linux上下文切换[转]_thonmin的博客-优快云博客_linux 上下文切换 中断子系统 1. 背景 <linux kernel的中断子系统之八:softirq>中讲到: softirq在同一个CPU上是串行的,这点体现在代码的哪里呢? ./kernel/softirq.c wowo科技的文章中讲了两种场景,其中中断嵌套由于Linux不支持,所以暂时不讨论,只讨论下面的一个场景: softirq中的中断 假设一个中断下半部softirq(随便什么..

liaojunwu的博客 566

基于 FPGA 的 CNN 卷积神经网络之池化层 Verilog 实现

池化层是卷积神经网络(CNN)中的关键组件,用于降低特征图的维度,减少计算量和防止过拟合。常见的池化操作包括最大池化和平均池化。FPGA 提供了高效的并行处理能力,可以加速这些操作。池化层在 CNN 中扮演重要角色,通过降低数据维度提升计算效率。在 FPGA 上实现池化层,可以充分利用硬件资源实现高效的并行计算,有助于提高实时应用中的推理速度。

走向CTO的路上... 1256

【人工智能】向量数据库全生命周期数据安全防护体系:破解向量化与检索双环节泄露风险

大模型时代,向量数据库已成为RAG(检索增强生成)架构、语义检索、多模态应用的核心基础设施,解决了大模型幻觉、长上下文窗口限制、私有知识落地等核心痛点。但行业普遍存在认知误区:将Embedding向量等同于“无害的语义压缩乱码”,忽视了向量数据原生的泄露风险。

奶爸的编程之路,也就一周冷个三天 157

深入理解Linux网络技术内幕 P1-P2

细品

西门吹雪 539

Android SystemUI——初始化流程(三)

1、Android 系统在启动之后,首先会Zygote进程,Zygote进程又会进一步启动 SystemServer进程,它会启动各种系统所需要的服务,在监听AMS启动完毕之后,还会启动SystemUIService。2、在启动SystemUIService服务之前,系统首先会创建SystemUI对应的进程,获取SystemUIAppComponentFactory实例对象,调用instantiateApplicationCompat创建SystemUIApplication实例对象并设置回调方法。

小旭的专栏 1157

关于preempt count的疑问

另外的7bit为了记录,防止进程被softirq所抢占,关闭/禁止softirq的次数,比如每使用一次local_bh_disable(),softirq count高7个bits(bit 9到bit 15)的值就会加1,使用local_bh_enable()则会让softirq count高7个bits的的值减1。因此为了分清楚是正在处理软中断还是说进程里面显示禁止了软中断,我们只需要判断bit8就行(进入softirq是在softirq上下文,关闭softirq抢占也是在softirq上下文如何区分)

qq_42693685的博客 469

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Linux内核抢占:preempt_count计数器

在Linux系统中,当一个进程正在内核态运行时,另一个进程能否打断它?这就是内核抢占(Preemption)要解决的问题。内核抢占允许高优先级进程打断低优先级进程的内核态执行,是实现实时性的关键机制。而这一切的核心,都由`preempt_count`计数器控制。 ## preempt_count计数器的工作原理 `preempt_count`是每个进程`task_struct`中的一个整数计数...

gitblog_00093的博客 416

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Linux内核抢占机制详解:preempt_count与抢占点设计全解析

Linux内核抢占机制是现代操作系统实现快速响应的核心技术之一,它允许高优先级任务在特定条件下中断当前正在执行的内核代码。本文将深入解析**preempt_count**变量的工作原理以及**抢占点**的设计思路,帮助你全面理解Linux内核的抢占机制。 ## 🔍 什么是内核抢占? 内核抢占是指在内核态执行过程中,允许更高优先级的任务中断当前正在执行的内核代码。这种机制对于实时系统、多媒体处

gitblog_00228的博客 1074

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Linux内核抢占计数:preempt_count机制深度解析与调试指南

Linux内核抢占机制是操作系统调度的核心组成部分,而`preempt_count`作为抢占计数器,在确保内核稳定性和实时性方面发挥着关键作用。本文将深入解析Linux内核抢占计数的工作原理、应用场景和调试技巧。🚀 ## 什么是preempt_count机制? **preempt_count**是Linux内核中用于跟踪当前上下文抢占状态的重要计数器。它位于`thread_info`结构中,

gitblog_00200的博客 473

内核抢占实现(preempt) 【转】

转自:http://blog.chinaunix.net/uid-12461657-id-3353217.html 一、什么叫抢占所谓抢占,说白了就是进程切换。linux的用户空间,进程A在执行中,来(硬?)中断打断A,从中断处理程序返回时,如果有更高优先级进程B在排队的话,那么执行进程B。 用户空间下进程总是可抢占的在linux的内核空间就不一定了,linux 2.4是不可抢占的,实时性就会降...

weixin_34175509的博客 523

kkkkkkk33

【代码】kkkkkkk33。

aa111sadsa的博客 174

preempt_count详解

当从内核态返回到用户态的时候,要检查是否进行调度,而调度要看两个条件: 1.preempt_count是否为0 2.rescheduled是否置位   ret_from_exception:  preempt_stop(CLBR_ANY) ret_from_intr:  GET_THREAD_INFO(%ebp) check_userspace:  mov

evenness的专栏 9183

linux 内核同步互斥技术之禁止内核抢占

linux 内核同步互斥技术之禁止内核抢占

一叶知秋的博客 693

实时Linux之PREEMPT_RT篇

我测试 Voluntary Kernel Preemption (Desktop)、Preemptible Kernel (Low-Latency Desktop)、Fully Preemptible Kernel (Real-Time) 三种抢占模型在无负载、一般负载、超重负载下的实时性。

szembed的博客 6337

内核锁机制

一. 原子操作 原子操作,就是代码执行不会在执行完毕前被任何其他任务或事件打断. 原子操作需要硬件的支持,因此是架构相关的, 它们都使用内嵌汇编语言实现,因为C语言并不能实现这样的操作. 内核原子操作的理解: a. 资源的使用情况计数, atomic_t的counter是int型的,可以记录资源的使用情况; b. 资源使用的互斥, 当 atomic_t的counter的取值情况...

wd_miracle 572

Linux优化-Preempt RT关键点

      By:Ailson Jack       Date:2016.04.02       个人博客:www.only2fire.com       本文在我博客的地址是:http://www.only2fire.com/archives/58.html,排版更好,便于学习。       最近看了看preempt-rt的一些材料,觉得网友的总结还是可以,这里就分享出来供大家学习之用,...

Ailson Jack的专栏 7261

【实时性】实时性优化的一些参数设置和心得

从BIOS、实时内核参数配置、操作系统环境配置三个方面介绍了如何优化实时任务

从善若水的博客 5995

preempt_count分析

kernel通过preempt_count变量来判断是否处于中断(soft/hard/nmi)上下文,是否可以抢占,先看示例 #define in_irq() (hardirq_count()) #define hardirq_count() (preempt_count() & HARDIRQ_MASK) static __always_inline int preempt_cou...

Android开发 854
上一篇: LWN:OpenWrt与SELinux!
下一篇: LWN:Collabora Online从Document基金会移出去了!
LinuxNews搬运工
博客等级 码龄7年 438粉丝 20原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值