linux cpu softirq,linux softirq机制

Copyright © 2003 by 詹荣开

E-mail:zhanrk@sohu.com

Linux-2.4.0

Version 1.0.0,2003-2-14

摘要:本文主要从内核实现的角度分析了Linux 2.4.0内核的Softirq机制。本文是为那些想要了解Linux I/O子系统的读者和Linux驱动程序开发人员而写的。

关键词:Linux、Softirq、软中断、Bottom half、设备驱动程序

申明:这份文档是按照自由软件开放源代码的精神发布的,任何人可以免费获得、使用和重新发布,但是你没有限制别人重新发布你发布内容的权利。发布本文的目的是希望它能对读者有用,但没有任何担保,甚至没有适合特定目的的隐含的担保。更详细的情况请参阅GNU通用公共许可证(GPL),以及GNU自由文档协议(GFDL)。

你应该已经和文档一起收到一份GNU通用公共许可证(GPL)的副本。如果还没有,写信给:

The Free Software Foundation, Inc., 675 Mass Ave, Cambridge,MA02139, USA

欢迎各位指出文档中的错误与疑问。

前言

中断服务程序往往都是在CPU关中断的条件下执行的,以避免中断嵌套而使控制复杂化。但是CPU关中断的时间不能太长,否则容易丢失中断信号。为此,Linux将中断服务程序一分为二,各称作“Top Half”和“Bottom Half”。前者通常对时间要求较为严格,必须在中断请求发生后立即或至少在一定的时间限制内完成。因此为了保证这种处理能原子地完成,Top Half通常是在CPU关中断的条件下执行的。具体地说,Top Half的范围包括:从在IDT中登记的中断入口函数一直到驱动程序注册在中断服务队列中的ISR。而Bottom Half则是Top Half根据需要来调度执行的,这些操作允许延迟到稍后执行,它的时间要求并不严格,因此它通常是在CPU开中断的条件下执行的。

但是,Linux的这种Bottom Half(以下简称BH)机制有两个缺点,也即:(1)在任意一时刻,系统只能有一个CPU可以执行Bottom Half代码,以防止两个或多个CPU同时来执行Bottom Half函数而相互干扰。因此BH代码的执行是严格“串行化”的。(2)BH函数不允许嵌套。

这两个缺点在单CPU系统中是无关紧要的,但在SMP系统中却是非常致命的。因为BH机制的严格串行化执行显然没有充分利用SMP系统的多CPU特点。为此,Linux2.4内核在BH机制的基础上进行了扩展,这就是所谓的“软中断请求”(softirq)机制。

6.1 软中断请求机制

Linux的softirq机制是与SMP紧密不可分的。为此,整个softirq机制的设计与实现中自始自终都贯彻了一个思想:“谁触发,谁执行”(Who marks,Who runs),也即触发软中断的那个CPU负责执行它所触发的软中断,而且每个CPU都由它自己的软中断触发与控制机制。这个设计思想也使得softirq 机制充分利用了SMP系统的性能和特点。

6.1.1 软中断描述符

Linux在include/linux/interrupt.h头文件中定义了数据结构softirq_action,来描述一个软中断请求,如下所示:

/* softirq mask and active fields moved to irq_cpustat_t in

* asm/hardirq.h to get better cache usage. KAO

*/

struct softirq_action

{

void (*action)(struct softirq_action *);

void *data;

};

其中,函数指针action指向软中断请求的服务函数,而指针data则指向由服务函数自行解释的数据。

基于上述软中断描述符,Linux在kernel/softirq.c文件中定义了一个全局的softirq_vec[32]数组:

static struct softirq_action softirq_vec[32] __cacheline_aligned;

在这里系统一共定义了32个软中断请求描述符。软中断向量i(0≤i≤31)所对应的软中断请求描述符就是softirq_vec[i]。这个数组是个系统全局数组,也即它被所有的CPU所共享。这里需要注意的一点是:每个CPU虽然都由它自己的触发和控制机制,并且只执行他自己所触发的软中断请求,但是各个CPU所执行的软中断服务例程却是相同的,也即都是执行softirq_vec[]数组中定义的软中断服务函数。

6.1.2 软中断触发机制

要实现“谁触发,谁执行”的思想,就必须为每个CPU都定义它自己的触发和控制变量。为此,Linux在include/asm- i386/hardirq.h头文件中定义了数据结构irq_cpustat_t来描述一个CPU的中断统计信息,其中就有用于触发和控制软中断的成员变量。数据结构irq_cpustat_t的定义如下:

/* entry.S is sensitive to the offsets of these fields */

typedef struct {

unsigned int __softirq_active;

unsigned int __softirq_mask;

unsigned int __local_irq_count;

unsigned int __local_bh_count;

unsigned int __syscall_count;

unsigned int __nmi_count; /* arch dependent */

} ____cacheline_aligned irq_cpustat_t;

结构中每一个成员都是一个32位的无符号整数。其中__softirq_active和__softirq_mask就是用于触发和控制软中断的成员变量。

①__softirq_active变量:32位的无符号整数,表示软中断向量0~31的状态。如果bit[i](0≤i≤31)为1,则表示软中断向量i在某个CPU上已经被触发而处于active状态;为0表示处于非活跃状态。

②__softirq_mask变量:32位的无符号整数,软中断向量的屏蔽掩码。如果bit[i](0≤i≤31)为1,则表示使能(enable)软中断向量i,为0表示该软中断向量被禁止(disabled)。

根据系统中当前的CPU个数(由宏NR_CPUS表示),Linux在kernel/softirq.c文件中为每个CPU都定义了它自己的中断统计信息结构,如下所示:

/* No separate irq_stat for s390, it is part of PSA */

#if !defined(CONFIG_ARCH_S390)

irq_cpustat_t irq_stat[NR_CPUS];

#endif /* CONFIG_ARCH_S390 */

这样,每个CPU都只操作它自己的中断统计信息结构。假设有一个编号为id的CPU,那么它只能操作它自己的中断统计信息结构irq_stat [id](0≤id≤NR_CPUS-1),从而使各CPU之间互不影响。这个数组在include/linux/irq_cpustat.h头文件中也作了原型声明。

l 触发软中断请求的操作函数

函数__cpu_raise_softirq()用于在编号为cpu的处理器上触发软中断向量nr。它通过将相应的__softirq_active成员变量中的相应位设置为1来实现软中断触发。如下所示(include/linux/interrupt.h):

static inline void __cpu_raise_softirq(int cpu, int nr)

{

softirq_active(cpu) |= (1<

}

为了保证“原子”性地完成软中断的触发过程,Linux在interrupt.h头文件中对上述内联函数又作了高层封装,也即函数 raise_softirq()。该函数向下通过调用__cpu_raise_softirq()函数来实现软中断的触发,但在调用该函数之前,它先通过 local_irq_save()函数来关闭当前CPU的中断并保存标志寄存器的内容,如下所示:

/* I do not want to use atomic variables now, so that cli/sti */

static inline void raise_softirq(int nr)

{

unsigned long flags;

local_irq_save(flags);

__cpu_raise_softirq(smp_processor_id(), nr);

local_irq_restore(flags);

}

6.1.3 Linux对软中断的预定义分类

在软中断向量0~31中,Linux内核仅仅使用了软中断向量0~3,其余被留待系统以后扩展。Linux在头文件include/linux/interrupt.h中对软中断向量0~3进行了预定义:

/* PLEASE, avoid to allocate new softirqs, if you need not _really_ high

frequency threaded job scheduling. For almost all the purposes

tasklets are more than enough. F.e. all serial device BHs et

al. should be converted to tasklets, not to softirqs.

*/

enum

{

HI_SOFTIRQ=0,

NET_TX_SOFTIRQ,

NET_RX_SOFTIRQ,

TASKLET_SOFTIRQ

};

其中,软中断向量0(即HI_SOFTIRQ)用于实现高优先级的软中断,如:高优先级的tasklet(将在后面详细描述)。软中断向量1和2 则分别用于网络数据的发送与接收。软中断向量3(即TASKLET_SOFTIRQ)则用于实现诸如tasklet这样的一般性软中断。关于 tasklet我们将在后面详细描述。NOTE!Linix内核并不鼓励一般用户扩展使用剩余的软中断向量,因为它认为其预定义的软中断向量 HI_SOFTIRQ和TASKLET_SOFTIRQ已经足够应付绝大多数应用。

6.1.4 软中断机制的初始化

函数softirq_init()完成softirq机制的初始化。该函数由内核启动例程start_kernel()所调用。函数源码如下所示(kernel/softirq.c):

void __init softirq_init()

{

int i;

for (i=0; i<32; i++)

tasklet_init(bh_task_vec+i, bh_action, i);

open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);

open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);

}

初始化的过程如下:

(1)先用一个for循环来初始化用于实现BH机制的bh_task_vec[32]数组。这一点我们将在后面详细解释。

(2)调用open_softirq()函数开启使用软中断向量TASKLET_SOFTIRQ和HI_SOFTIRQ,并将它们的软中断服务函数指针分别指向tasklet_action()函数和tasklet_hi_action()函数。函数open_softirq()的主要作用是初始化设置软中断请求描述符softirq_vec[nr]。

6.1.5 开启一个指定的软中断向量

函数open_softirq()用于开启一个指定的软中断向量nr,也即适当地初始化软中断向量nr所对应的软中断描述符 softirq_vec[nr]。它主要做两件事情:(1)初始化设置软中断向量nr所对应的软中断描述符softirq_vec[nr]。(2)将所有 CPU的软中断屏蔽掩码变量__softirq_mask中的对应位设置为1,以使能该软中断向量。该函数的源码如下所示(kernel/softirq.c):

void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)

{

unsigned long flags;

int i;

spin_lock_irqsave(&softirq_mask_lock, flags);

softirq_vec[nr].data = data;

softirq_vec[nr].action = action;

for (i=0; i

softirq_mask(i) |= (1<

spin_unlock_irqrestore(&softirq_mask_lock, flags);

}

6.1.6 软中断服务的执行函数do_softirq()

函数do_softirq()负责执行数组softirq_vec[32]中设置的软中断服务函数。每个CPU都是通过执行这个函数来执行软中断服务的。由于同一个CPU上的软中断服务例程不允许嵌套,因此,do_softirq()函数一开始就检查当前CPU是否已经正出在中断服务中,如果是则 do_softirq()函数立即返回。举个例子,假设CPU0正在执行do_softirq()函数,执行过程产生了一个高优先级的硬件中断,于是 CPU0转去执行这个高优先级中断所对应的中断服务程序。总所周知,所有的中断服务程序最后都要跳转到do_IRQ()函数并由它来依次执行中断服务队列中的ISR,这里我们假定这个高优先级中断的ISR请求触发了一次软中断,于是do_IRQ()函数在退出之前看到有软中断请求,从而调用 do_softirq()函数来服务软中断请求。因此,CPU0再次进入do_softirq()函数(也即do_softirq()函数在CPU0上被重入了)。但是在这一次进入do_softirq()函数时,它马上发现CPU0此前已经处在中断服务状态中了,因此这一次do_softirq()函数立即返回。于是,CPU0回到该开始时的do_softirq()函数继续执行,并为高优先级中断的ISR所触发的软中断请求补上一次服务。从这里可以看出,do_softirq()函数在同一个CPU上的执行是串行的。

函数源码如下(kernel/softirq.c):

asmlinkage void do_softirq()

{

int cpu = smp_processor_id();

__u32 active, mask;

if (in_interrupt())

return;

local_bh_disable();

local_irq_disable();

mask = softirq_mask(cpu);

active = softirq_active(cpu) & mask;

if (active) {

struct softirq_action *h;

restart:

/* Reset active bitmask before enabling irqs */

softirq_active(cpu) &= ~active;

local_irq_enable();

h = softirq_vec;

mask &= ~active;

do {

if (active & 1)

h->action(h);

h++;

active >>= 1;

} while (active);

local_irq_disable();

active = softirq_active(cpu);

if ((active &= mask) != 0)

goto retry;

}

local_bh_enable();

/* Leave with locally disabled hard irqs. It is critical to close

* window for infinite recursion, while we help local bh count,

* it protected us. Now we are defenceless.

*/

return;

retry:

goto restart;

}

结合上述源码,我们可以看出软中断服务的执行过程如下:

(1)调用宏in_interrupt()来检测当前CPU此次是否已经处于中断服务中。该宏定义在hardirq.h,请参见5.7节。

(2)调用local_bh_disable()宏将当前CPU的中断统计信息结构中的__local_bh_count成员变量加1,表示当前CPU已经处在软中断服务状态。

(3)由于接下来要读写当前CPU的中断统计信息结构中的__softirq_active变量和__softirq_mask变量,因此为了保证这一个操作过程的原子性,先用local_irq_disable()宏(实际上就是cli指令)关闭当前CPU的中断。

(4)然后,读当前CPU的__softirq_active变量值和__softirq_mask变量值。当某个软中断向量被触发时(即 __softirq_active变量中的相应位被置1),只有__softirq_mask变量中的相应位也为1时,它的软中断服务函数才能得到执行。因此,需要将__softirq_active变量和__softirq_mask变量作一次“与”逻辑操作。

(5)如果active变量非0,说明需要执行软中断服务函数。因此:①先将当前CPU的__softirq_active中的相应位清零,然后用local_irq_enable()宏(实际上就是sti指令)打开当前CPU的中断。②将局部变量mask中的相应位清零,其目的是:让 do_softirq()函数的这一次执行不对同一个软中断向量上的再次软中断请求进行服务,而是将它留待下一次do_softirq()执行时去服务,从而使do_sottirq()函数避免陷入无休止的软中断服务中。③用一个do{}while循环来根据active的值去执行相应的软中断服务函数。 ④由于接下来又要检测当前CPU的__softirq_active变量,因此再一次调用local_irq_disable()宏关闭当前CPU的中断。⑤读取当前CPU的__softirq_active变量的值,并将它与局部变量mask进行与操作,以看看是否又有其他软中断服务被触发了(比如前面所说的那种情形)。如果有的话,那就跳转到entry程序段(实际上是跳转到restart程序段)重新执行软中断服务。如果没有的话,那么此次软中断服务过程就宣告结束。

(6)最后,通过local_bh_enable()宏将当前CPU的__local_bh_count变量值减1,表示当前CPU已经离开软中断服务状态。宏local_bh_enable()也定义在include/asm-i386/softirq.h头文件中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/300214.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

复盘:我的三个月远程办公实践,有自由,也有代价

这是头哥侃码的第244篇原创有人说&#xff0c;人生就是一个不断尝试的过程。我觉得&#xff0c;有时候这个词其实不准确&#xff0c;因为每个人的性格不同&#xff0c;成长经历及运势不同&#xff0c;所以对 “尝试” 俩字的理解也就不同。在我还是孩子的时候&#xff0c;几乎所…

信息网络传播权保护条例(2006)

信息网络传播权保护条例(2006)[url]http://www.ncac.gov.cn/GalaxyPortal/inner/bqj/include/detail.jsp?articleid9400&boardpid175&boardid11501010111602[/url]转载于:https://blog.51cto.com/dgcnn/20682

Silverlight专题(10)- WatermarkedTextBox使用

问题&#xff1a; 之前的Silverlight版本都有一个WatermarkedTextBox控件 但是到了Silverlight 2 Beta2版本&#xff0c;由于和WPF兼容的考虑 WatermarkedTextBox被移除了 虽然之前我有看到消息说Silverlight 2正式Release的时候会给TextBox一个Watermark属性 但是Silverlight …

asp.net ajax检查用户名是否存在代码

原文 asp.net ajax检查用户名是否存在代码 用户注册时&#xff0c;我们经常需要检查用户名是否存在&#xff0c;本文就是实现无刷新验证用户名 打开开发环境VS 2005,新建项目(或打开现有项目),新建一个Web窗体,命名为 Default.aspx 创建 XMLHttpRequest 对象所有现代浏览器 (I…

90后一代人还能通过攒钱改变现状吗?

全世界只有3.14 % 的人关注了青少年数学之旅每次打开公号&#xff0c;扑面而来一阵阵焦虑&#xff1a;95后毕业3个月就买房&#xff0c;你的同龄人正在抛弃你毕业3年&#xff0c;年薪超100万&#xff1a;赚钱&#xff0c;是一种修行一线城市财务自由门槛2.9亿&#xff0c;看看你…

linux中人脸识别不了,虹软人脸识别在 linux中so文件加载不到的问题

其实是可以加载到的&#xff0c;不过是so文件放的位置不一对&#xff0c;最简单的方式是放在 /usr/lib64 目录下&#xff0c;也可自己设置。 so文件加载不到会报这个错误&#xff1a;.lang.UnsatisfiedLinkError: no arcsoft_face_engine_jni in java.library.path] with root …

从高德侯军到《李嘉诚:商者无域》

从高德侯军到《李嘉诚&#xff1a;商者无域》 【编者按】转载这篇文章是因为看到了业内著名企业高德董事长侯军跻身2008胡润排行榜&#xff0c;让人不禁联想起高德在业内一贯的潜行风格&#xff0c;而侯军先生也颇有点“忍者神龟”的隐喻&#xff0c;在业内企业家当中属闷声发大…

测试龙芯 LoongArch .NET之 使用 FastTunnel 做内网穿透远程计算机

龙芯3A5000 已经上市&#xff0c;从老伙计哪里搞来一台3A5000 机器&#xff0c;安装统信UOS。使用体验上看还可以&#xff0c;就是软件生态急需建设&#xff0c;软件生态的建设上自然有我dotnet 的一份力量。龙芯团队已经完成了LoongArch 的.NET Core 3.1版本的研发&#xff0c…

利用jquery给指定的table动态添加一行、删除一行

今天在项目中&#xff0c;刚好用到给指定的table添加一行、删除一行&#xff0c;就直接找google&#xff0c;搜出来的东西不尽如人意&#xff0c;不是功能不好就是千篇一律&#xff0c;简直浪费时间还不讨好&#xff0c;于是乎就自己动手封装个&#xff0c;现就把代码分享出来&…

求求你把输入法调小一点... | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅

LoadRunner Interview Questions

摘自&#xff1a;网络1. What is load testing? Ans. Load testing is to test that if the application works fine with the loads that result from large number of simultaneous users, transactions and to determine weather it can handle peak usage periods.2. Wha…

linux安装卷管理,Linux安装管理ISCSI卷(initiator端)

Internet SCSI(iSCSI)是一种网络协议&#xff0c;使用TCP/IP网络来传输SCSI协议。它是代替FC(Fibre Channel-based&#xff0c;光纤通道&#xff1f;) SAN的很好选Internet SCSI(iSCSI)是一种网络协议&#xff0c;使用TCP/IP网络来传输SCSI协议。它是代替FC(Fibre Channel-base…

SQL server 2000安装时提示我”以前的某个程序安装已在安装计算机上创建挂起的文件操作....”...

在运行窗口输入regedit&#xff0c;打开注册表编辑器&#xff0c;在HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager中找到PendingFileRenameOperations&#xff0c;删除该键值&#xff08;这个键值是安装程序暂挂项目&#xff0c;只要找到对应的应用程序清除…

Blazor 事件处理开发指南

翻译自 Waqas Anwar 2021年3月25日的文章 《A Developer’s Guide To Blazor Event Handling》 [1]如果您正在开发交互式 Web 应用程序&#xff0c;根据不同的应用程序事件和用户操作动态更新用户界面是十分常见的做法。这些操作会触发事件&#xff0c;而作为开发人员&#xff…

android 开源组件合集-UI篇(2013-11-07更新)

其实也算不上合集,只是将我经常用到的部分整理一下,如果您有好东西,也可以留言补充 1.actionbar http://actionbarsherlock.com/ https://github.com/JakeWharton/ActionBarSherlock (推荐) 2.下拉刷新pulltorefresh https://github.com/chrisbanes/Android-PullToRefresh 支持…

改变世界的5大常数,学过数学的人,这一辈子都不会忘记!

全世界只有3.14 % 的人关注了青少年数学之旅何谓数学&#xff1f;数学家Eduardo曾这样回答“数学是永恒&#xff0c;是真理&#xff0c;是一切的答案。”回首往昔数学始终伴随我们左右纵横交错的几何、繁琐复杂的运算难以求解的方程、无从下手的猜想......尽管在数学道路上有多…

创维linux进入工厂模式,创维电视怎么进入工厂模式?

满意答案zrwemshwt54推荐于 2019.11.03创维彩电进入与退出工厂模式方法的汇总一&#xff0e; D系列5D01机芯&#xff1a;进入&#xff1a;在遥控器屏显键的正下方&#xff0c;加装一个按键(SERVICE键)&#xff0c;按该键即可进入工厂模式。退出&#xff1a; 按遥控器上的TV/AV键…

收到在微软商店购买的商品

今天收到了在微软商店购买的商品&#xff0c;送货速度真快&#xff0c;20号下的订单&#xff0c;今天就拿到了&#xff0c;这么快就从美国通过UPS快递到国内&#xff0c;现在的物流越来越发达了。我购买的商品是&#xff1a;1、WM USB Powered Speakers(USB扬声器)2、LifeCam V…

[导入]纹理拼接后的Wrap寻址

拼接后的纹理:正常的草地(不进行WRAP寻址):文章来源:http://blog.csdn.net/xoyojank/archive/2008/11/03/3212425.aspx转载于:https://www.cnblogs.com/xoyojank/archive/2008/11/04/1343671.html

自定义EventSource(一)EventCounter

之前的Counters都是系统内置的&#xff0c;我们只需在进程外&#xff0c;或进程内采集&#xff0c;然后交给专门的展示指标工具即可。本篇说一下自定义EventSource&#xff0c;来采集自己业务中&#xff0c;或自己产品中的指标收集方式。自定义EventSource是以EventCounters作为…