精华内容
下载资源
问答
  • 软中断的实现原理及使用.ppt
    2021-05-18 06:20:15

    软中断的实现原理及使用

    软中断的实现原理及使用 主讲: 张琰 主要内容 下半部概述 软中断 Tasklet 工作队列 下半部机制的选择 下半部之间的同步 下半部概述 为什么叫下半部? 下半部是针对中断处理流程而言,下半部的任务就是执行与中断处理密切相关但中断处理程序本身不执行的工作。 中断处理的局限性 异步触发,可随时打断其他代码的执行 执行中断处理程序时,最好的情况下也会锁定当时中断线,最坏的情况是禁止所有本地中断。 往往对硬件进行操作,所以时限要求高 不在进程上下文件中运行,不能阻塞 下半部概述 下半部的引入 中断处理程序(上半部):快速、异步、简单的对硬件做出响应,完成那些对时间要求很严格的操作。 下半部:其他对时间要求相对宽松的任务。 下半部的工作 理想情况下,最好所有的工作都由下半部完成,因为中断处理程序中完成的工作越少越好,越快越好,这样断程序可以尽可能快的返回。 现实情况:中断处理程序响应中断,对中断的到达进行确认或其他一些对时间要求很严格的工作;其他全部由下半部完成。 下半部概述 上半部和下半部的工作划分 对时间非常敏感的任务在中断处理程序中执行 任务与硬件相关,在中断处理程序中执行 任务要保证不被其他中断(特别是相同中断)打断,在中断处理程序中执行 除以上所述以外的所有任务,在下半部中执行 下半部的实现机制 软中断(Softirq) Tasklet 工作队列(Work queues) 软中断(Softirq) 软中断的实现 软中断是在编译期间静态分配的 软中断由softirq_action结构表示,它在中定义。 static struct softirq_action softirq_vec[32]定义了软中断数组,每个被注册的软中断都占据该数据的一项。kernel/softirq.c 一个软中断不会抢占另一个软中断,唯一可抢占软中断的是中断处理程序。但其他软中断(包括相同类型的软中断)可以在其他处理器上同时运行。 一个注册的软中断必须在被标记后才会执行,称之为触发软中断(raising the softirq)。 软中断(Softirq) 软中断的执行时机 处理完一个硬件中断以后,在irq_exit()调用。kernel/irq/handle.c 在ksoftirqd内核线程中。kernel/softirq.c 在显示检查和执行待处理的软中断的代码中,如网络子系统。 无论哪一种方式唤起软中断处理,软中断都要在do_softirq()中执行。 kernel/softirq.c 软中断(Softirq) 软中断的使用 软中断使用介绍 软中断是保留给系统中对时间要求最严格以及最重要的下半部使用的。 目前只有网络子系统和SCSI子系统直接使用软中断 内核定时器和tasklet都是建立在软中断上的 只有当对时间要求严格并能自己高效地完成加锁工作时,才推荐使用软中断 添加新的软中断 分配索引 通过在中定义的一个枚举类型来静态地声明软中断。 索引表示一种相对优先级,索引号小的软中断先执行。 建立新的软中断必须在此枚举类型中加入新的项,根据你所期望的优先级来决定加入的位置。 使用习惯:HI_SOFTIRQ做为第一项;TASKLET_SOFTIRQ做为最后一项; 添加新的软中断 注册软中断处理程序 调用open_softirq()注册软中断处理程序 open_softirq()三个参数的含义 软中断索引号 软中断处理函数 Data域存放的值 软中断处理程序的特点: 允许响应中断,但其本身不能休眠 其执行时,当前处理器的软中断被禁止,但其他处理器仍可执行,所以需要考虑多处理器情况下的同步。 其内部使用的全局变量也需要严格的锁保护 不可通过互斥的加锁方法来防止其自身的并发 添加新的软中断 触发软中断 通过raise_softirq()将软中断设置为挂起状态 raise_softirq()参数为软中断号 在触发软中断前禁止中断,触发后再恢复回原来的状态。 raise_softirq_irqoff() 当中断本来就已经被禁止时,用此函数将软中断置为挂起状态。 内核执行完中断处理程序以后,马上会调用do_softirq(),让软中断去完成中断处理程序未完成的工作。 Tasklet Tasklet的实现 Tasklet是由软中断实现的 Tasklet有两种软中断 HI_SOFTIRQ TASKLET_SOFTIRQ Tasklet结构在中定义 Tasklet处理程序 tasklet_action() tasklet_hi_action() Tasklet Tasklet的使用 初始化tasklet 静态定义: DECLARE_TASKLET

    更多相关内容
  • Linux软中断原理浅析

    2021-05-16 17:31:59
    软中断原理浅析Linux中的软中断机制用于系统中对时间要求最严格以及最重要的中断下半部进行使用。在系统设计过 程中,大家都清楚中断上下文不能处理太多的事情,需要快速的返回,否则很容易导致中断事件的丢失,所以...

    软中断原理浅析Linux中的软中断机制用于系统中对时间要求最严格以及最重要的中断下半部进行使用。在系统设计过 程中,大家都清楚中断上下文不能处理太多的事情,需要快速的返回,否则很容易导致中断事件的丢失,所以这就产生了一个问题:中断发生之后的事务

    软中断原理浅析

    Linux中的软中断机制用于系统中对时间要求最严格以及最重要的中断下半部进行使用。在系统设计过 程中,大家都清楚中断上下文不能处理太多的事情,需要快速的返回,否则很容易导致中断事件的丢失,所以这就产生了一个问题:中断发生之后的事务处理由谁来 完成?在前后台程序中,由于只有中断上下文和一个任务上下文,所以中断上下文触发事件,设置标记位,任务上下文循环扫描标记位,执行相应的动作,也就是中 断发生之后的事情由任务来完成了,只不过任务上下文采用扫描的方式,实时性不能得到保 证。在Linux系统和Windows系统中,这个不断循环的任务就是本文所要讲述的软中断daemon。在Windows中处理耗时的中断事务称之为中 断延迟处理,在Linux中称之为中断下半部,显然中断上半部处理清中断之类十分清闲的动作,然后在退出中断服务程序时触发中断下半部,完成具体的功能。

    在Linux中,中断下半部的实现基于软中断机制。所以理清楚软中断机制的原理,那么中断下半部的实现也就非常简单了。通过上述的描述,大家也应该 清楚为什么要定义软中断机制了,一句话就是为了要处理对时间要求苛刻的任务,恰好中断下半部就有这样的需求,所以其实现采用了软中断机制。  www.cit.cn

    构成软中断机制的核心元素包括:

    1、  软中断状态寄存器soft interrupt state(irq_stat)

    2、  软中断向量表(softirq_vec)

    3、  软中断守护daemon

    www.cit.cn

    软中断的工作工程模拟了实际的中断处理过程,当某一软中断时间发生后,首先需要设置对应的中断标记位,触发中断事务,然后唤醒守护线程去检测中断状 态寄存器,如果通过查询发现某一软中断事务发生之后,那么通过软中断向量表调用软中断服务程序action()。这就是软中断的过程,与硬件中断唯一不同 的地方是从中断标记到中断服务程序的映射过程。在CPU的硬件中断发生之后,CPU需要将硬件中断请求通过向量表映射成具体的服务程序,这个过程是硬件自 动完成的,但是软中断不是,其需要守护线程去实现这一过程,这也就是软件模拟的中断,故称之为软中断。

    一个软中断不会去抢占另一个软中断,只有硬件中断才可以抢占软中断,所以软中断能够保证对时间的严格要求。

    Linux中软中断实现分析

    在Linux中最多可以注册32个软中断,目前系统用了6个软中断,他们为:定时器处理、SCSI处理、网络收发处理以及Tasklet机制,这里的tasklet机制就是用来实现下半部的,

    描述软中断的核心数据结构为中断向量表,其定义如下:

    struct softirq_action

    {

    void (*action)(struct softirq_action *); /* 软中断服务程序 */

    void *data;                                         /* 服务程序输入参数 */

    www.cit.cn

    };

    软中断守护daemon是软中断机制的实现核心,其实现过程也比较简单,通过查询软中断状态irq_stat来判断事件是否发生,如果发生,那么映 射到软中断向量表,调用执行注册的action函数就可以了。从这一点分析可以看出,软中断的服务程序的执行上下文为软中断daemon。在Linux中 软中断daemon线程函数为do_softirq()。

    触发软中断事务通过raise_softirq()来实现,该函数就是在中断关闭的情况下设置软中断状态位,然后判断如果不在中断上下文,那么直接唤醒守护daemon。

    常用的软中断函数列表如下:

    1、  Open_softirq,注册一个软中断,将软中断服务程序注册到软中断向量表。2、  Raise_softirq,设置软中断状态bitmap,触发软中断事务。

    Tasklet机制实现分析

    Tasklet为一个软中断,考虑到优先级问题,分别占用了向量表中的0号和5号软中断。

    www.cit.cn

    当tasklet的软中断事件发生之后,执行tasklet-action的软中断服务程序,该服务程序会扫描一个tasklet的任务列表,执行该任务中的具体服务程序。在这里举一个例子加以说明:

    当用户读写USB设备之后,发生了硬件中断,硬件中断服务程序会构建一个tasklet_struct,在该结构中指明了完成该中断任务的具体方法 函数(下半部执行函数),然后将tasklet_struct挂入tasklet的tasklet_struct链表中,这一步可以通过 tasklet_schedule函数完成。最后硬件中断服务程序退出并且CPU开始调度软中断daemon,软中断daemon会发现tasklet发 生了事件,其会执行tasklet-action,然后tasklet-action会扫描tasklet_struct链表,执行具体的USB中断服务 程序下半部。这就是应用tasklet完成中断下半部实现的整个过程。

    www.cit.cn

    Linux中的tasklet实现比较简单,其又封装了一个重要数据结构tasklet_struct,使用tasklet主要函数列表如下:

    1、  tasklet_init,初始化一个tasklet_struct,当然可以采用静态初始化的方法,宏为:DECLARE_TASKLET。

    2、  tasklet_schedule,调度一个tasklet,将输入的tasklet_struct添加到tasklet的链表中。

    Linux中的软中断机制就是模拟了硬件中断的过程,其设计思想完全可以在其他嵌入式OS中得以应用。

    作者 llg521208

    展开全文
  • 实验2 Linux软中断通信 1.实验目的 通过本实验掌握软中断的基本原理掌握中断信号的使用进程的创建以及系统计时器的使用 2.实验内容上交的实验2统一取名为test2) 由父进程创建两个子进程通过终端输入Crtl+\组合键向父...
  • linux软中断与硬中断实现原理概述。

    千次阅读 2014-09-15 20:01:45
    软中断通过open_softirq注册一个软中断处理函数,即在软中断向量表

    1、软中断通过open_softirq注册一个软中断处理函数,即在软中断向量表softirq_vec数组中添加新的软中断处理action函数。

    2、调用raised_softirq软中断触发函数,即把软中断标记为挂起状态。

    内核会在一些位置检查是否有挂起状态的软中断,如果有的话会调用do_softirq函数来执行软中断处理函数。

    3、do_softirq函数做的一个重要的工作是切换到软中断请求栈。切换到软中断请求栈就代表了此时已经处于了软中断上下文中了。

                     (1)切换到软中断请求栈(2)调用__do_softirq函数

    4、__do_softirq函数

    (1)开始执行软中断处理函数

    (2)如果还有更多挂起的软中断没有处理,则调用wakeup_softirq函数唤醒内核线程来处理本地CPU的软中断。该线程会循环调用do_softirq函数而不是在内核那些检查软中断是否挂起的位置进行检查,节省了时间。


    所以软中断的本质就是在内核某些位置检查是否有挂起的软中断(local_software_pending()不为0则表示有挂起的软中断),若有则调用do_softirq函数,在do_softirq函数中切换到软中断请求栈后,调用__do_softirq软中断回调函数。

    硬中断的本质是接收到中断信号后,跳转到公共段代码执行do_IRQ,并切换到硬中断请求栈,执行中断回调函数。

            如果在每次中断的时候才检查是否有软中断需要执行的话,那么由于两次软中断的间隔的等待时间过长,这样对于用软中断处理收发网络包是不可取的,所以do_softirq可以连续检查10次是否有需要执行的软中断,这样处理软中断效率会比较高,之所以没有让do_softirq执行更多次的检查是因为由于软中断优先级比较高所以会导致用户程序长时间得不到执行。解决这个平衡的方法是如果有超过10次的软中观需要处理则唤醒ksoftirqd线程,该线程会循环检查是否有挂起的软中断,由于ksoftirqd线程的优先级低于用户进程,所以不会影响用户进程的执行。


    《深入理解linux内核》 -P179,P180


    以下文章转自http://bbs.chinaunix.net/thread-2333484-1-1.html

    作者:独孤九贱
    平台:2.6.31.13 + x86 32位
    供仅讨论学习之处,不对错误之处负责,转载请注明出处。

    1、软中断

    软中断的原理就略过了,讲内核的书上都有,此处省略1500字。。。。。。

    1.1 注册
    还是以我最熟悉的两个老朋友做为开篇:

            open_softirq(NET_TX_SOFTIRQ, net_tx_action);
            open_softirq(NET_RX_SOFTIRQ, net_rx_action);

    open_softirq向内核注册一个软中断,其实质是设置软中断向量表相应槽位,注册其处理函数:
    1. void open_softirq(int nr, void (*action)(struct softirq_action *))
    2. {
    3.         softirq_vec[nr].action = action;
    4. }
    复制代码
    softirq_vec是整个软中断的向量表:
    1. struct softirq_action
    2. {
    3.         void        (*action)(struct softirq_action *);
    4. };

    5. static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
    复制代码
    NR_SOFTIRQS是最大软中断向量数,内核支持的所有软中断如下:
    1. enum
    2. {
    3.         HI_SOFTIRQ=0,
    4.         TIMER_SOFTIRQ,
    5.         NET_TX_SOFTIRQ,
    6.         NET_RX_SOFTIRQ,
    7.         BLOCK_SOFTIRQ,
    8.         TASKLET_SOFTIRQ,
    9.         SCHED_SOFTIRQ,
    10.         HRTIMER_SOFTIRQ,
    11.         RCU_SOFTIRQ,        /* Preferable RCU should always be the last softirq */

    12.         NR_SOFTIRQS
    13. };
    复制代码
    好像后为为RPS新增了一个,不过这我的内核版本偏低。

    1.2 激活 

    当需要调用软中断时,需要调用raise_softirq函数激活软中断,这里使用术语“激活”而非“调用”,
    是因为在很多情况下不能直接调用软中断。所以只能快速地将其标志为“可执行”,等待未来某一时刻调用。
    为什么“在很多情况下不能直接调用软中断”?试想一下下半部引入的理念,就是为了让上半部更快地执行。
    如果在中断程序代码中直接调用软中断函数,那么就失去了上半部与下半部的区别,也就是失去了其存在的意义。

    内核使用一个名为__softirq_pending的位图来描述软中断,每一个位对应一个软中断,位图包含在结构irq_stat中:
    1. typedef struct {
    2.         unsigned int __softirq_pending;
    3.         ……
    4. } ____cacheline_aligned irq_cpustat_t;

    5. DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
    复制代码
    宏or_softirq_pending用于设置相应的位(位或操作):
    1. #define or_softirq_pending(x)        percpu_or(irq_stat.__softirq_pending, (x))
    复制代码
    local_softirq_pending用于取得整个位图(而非某一位):
    1. #define local_softirq_pending()        percpu_read(irq_stat.__softirq_pending)
    复制代码
    宏__raise_softirq_irqoff是or_softirq_pending的包裹:
    1. #define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0)
    复制代码
    raise_softirq_irqoff通过调用__raise_softirq_irqoff实现激活软中断,它的参数nr即位软中断对应的位图槽位:
    1. /*
    2. * This function must run with irqs disabled!
    3. */
    4. inline void raise_softirq_irqoff(unsigned int nr)
    5. {
    6.         //置位图,即标记为可执行状态
    7.         __raise_softirq_irqoff(nr);

    8.         /*
    9.          * If we're in an interrupt or softirq, we're done
    10.          * (this also catches softirq-disabled code). We will
    11.          * actually run the softirq once we return from
    12.          * the irq or softirq.
    13.          *
    14.          * Otherwise we wake up ksoftirqd to make sure we
    15.          * schedule the softirq soon.
    16.          */
    17.         //设置了位图后,可以判断是否已经没有在中断上下文中了,如果没有,则是一个立即调用软中断的好时机。
    18.         //in_interrupt另一个作用是判断软中断是否被禁用。
    19.         //wakeup_softirqd唤醒软中断的守护进程ksoftirq。
    20.         if (!in_interrupt())
    21.                 wakeup_softirqd();
    22. }
    复制代码
    现在可以来看"激活"软中断的所有含义了,raise_softirq函数完成这一操作:
    1. void raise_softirq(unsigned int nr)
    2. {
    3.         unsigned long flags;

    4.         //所有操作,应该关闭中断,避免嵌套调用
    5.         local_irq_save(flags);
    6.         raise_softirq_irqoff(nr);
    7.         local_irq_restore(flags);
    8. }
    复制代码
    可见,激活的操作,主要是两点:
    <1>、最重要的,就是置相应的位图,等待将来被处理;
    <2>、如果此时已经没有在中断上下文中,则立即调用(其实是内核线程的唤醒操作),现在就是将来;

    2、调度时机
    是的,除了raise_softirq在,可能会(嗯,重要的是“可能”)通过wakeup_softirqd唤醒ksoftirqd外,还得明白软中断的其它调用时机。

    A、当do_IRQ完成了I/O中断时调用irq_exit:
    1. #ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED
    2. # define invoke_softirq()        __do_softirq()
    3. #else
    4. # define invoke_softirq()        do_softirq()
    5. #endif

    6. void irq_exit(void)
    7. {
    8.         account_system_vtime(current);
    9.         trace_hardirq_exit();
    10.         sub_preempt_count(IRQ_EXIT_OFFSET);
    11.         if (!in_interrupt() && local_softirq_pending())
    12.                 invoke_softirq();                //调用软中断
    复制代码
    B、如果系统使用I/O APIC,在处理完本地时钟中断时:
    1. void __irq_entry smp_apic_timer_interrupt(struct pt_regs *regs)
    2. {
    3.         ……
    4.         irq_exit();
    5.         ……
    6. }
    复制代码
    C、local_bh_enable

    local_bh_enable就是打开下半部,当然重中之中就是软中断了:
    1. void local_bh_enable(void)
    2. {
    3.         _local_bh_enable_ip((unsigned long)__builtin_return_address(0));
    4. }

    5. static inline void _local_bh_enable_ip(unsigned long ip)
    6. {
    7.         ……

    8.         if (unlikely(!in_interrupt() && local_softirq_pending()))
    9.                 do_softirq();

    10.         ……
    11. }
    复制代码
    D、在SMP中,当CPU处理完被CALL_FUNCTION_VECTOR处理器间中断所触发的函数时:
    唔,对多核中CPU的之间的通信不熟,不太清楚这个机制…… 

    3、do_softirq

    不论是哪种调用方式,最终都会触发到软中断的核心处理函数do_softirq,它处理当前CPU上的所有软中断。
    内核将软中断设计尽量与平台无关,但是在某些情况下,它们还是会有差异,先来看一个x86 32位的do_softirq版本:
    1. asmlinkage void do_softirq(void)
    2. {
    3.         unsigned long flags;
    4.         struct thread_info *curctx;
    5.         union irq_ctx *irqctx;
    6.         u32 *isp;

    7.         //软中断不能在中断上下文内嵌套调用。中断处理程序或下半部采用的是"激活"方式。
    8.         if (in_interrupt())
    9.                 return;

    10.         //禁止中断,保存中断标志
    11.         local_irq_save(flags);
    12.         //内核使用一个CPU位图,确实几个软中断可以同时在不同的CPU上运行,包括相同的软中断。例如,
    13.         //NET_RX_SOFTIRQ可以同时跑在多个处理器上。
    14.         //local_softirq_pending用于确定当前CPU的所有位图是否被设置。即是否有软中断等待处理。
    15.         //回想一下经常发生的网卡接收数据处理:当网卡中断落在哪一个CPU上时,与之相应的软中断函数就会在其上执行。
    16.         //从这里来看,实质就是哪个网卡中断落在相应的CPU上,CPU置其软中断位图,这里做相应的检测(这里local_softirq_pending只
    17.         //是一个总的判断,后面还有按位的判断),检测到有相应的位,执行之
    18.         if (local_softirq_pending()) {
    19.                 //取得线程描述符
    20.                 curctx = current_thread_info();
    21.                 //构造中断上下文结构,softirq_ctx是每个CPU的软中断上下文
    22.                 //static DEFINE_PER_CPU(union irq_ctx *, softirq_ctx);
    23.                 //这里先取得当前CPU的软中断上下文,然后为其赋初始值——保存当前进程和栈指针
    24.                 irqctx = __get_cpu_var(softirq_ctx);
    25.                 irqctx->tinfo.task = curctx->task;
    26.                 irqctx->tinfo.previous_esp = current_stack_pointer;

    27.                 /* build the stack frame on the softirq stack */
    28.                 //构造中断栈帧
    29.                 isp = (u32 *) ((char *)irqctx + sizeof(*irqctx));

    30.                 //call_on_stack切换内核栈,并在中断上下文上执行函数__do_softirq
    31.                 call_on_stack(__do_softirq, isp);
    32.                 /*
    33.                  * Shouldnt happen, we returned above if in_interrupt():
    34.                  */
    35.                 WARN_ON_ONCE(softirq_count());
    36.         }

    37.         //恢复之
    38.         local_irq_restore(flags);
    39. }
    复制代码
    当配置了CONFIG_4KSTACKS,每个进程的thread_union只有4K,而非8K。发生中断时,内核栈将不使用进程的内核栈,而使用每个 cpu的中断请求栈。
    内核栈将使用每个 cpu的中断请求栈,而非进程的内核栈来执行软中断函数:
    1. static void call_on_stack(void *func, void *stack)
    2. {
    3.         asm volatile("xchgl        %%ebx,%%esp        \n"                                //交换栈指针,中断栈帧的指针stack做为传入参数(%ebx),交换后esp是irq_ctx的栈顶,ebx是进程内核栈的栈
    4.                      "call        *%%edi                \n"                                        //调用软中断函数
    5.                      "movl        %%ebx,%%esp        \n"                                        //恢复之,直接使用movl,而非xchgl是因为函数执行完毕,中断的栈帧指针已经没有用处了
    6.                      : "=b" (stack)
    7.                      : "0" (stack),
    8.                        "D"(func)
    9.                      : "memory", "cc", "edx", "ecx", "eax");
    10. }
    复制代码
    PS:所有的这些执行,应该都是在定义4K栈的基础上的:
    1. #ifdef CONFIG_4KSTACKS
    2. /*
    3. * per-CPU IRQ handling contexts (thread information and stack)
    4. */
    5. union irq_ctx {
    6.         struct thread_info      tinfo;
    7.         u32                     stack[THREAD_SIZE/sizeof(u32)];
    8. } __attribute__((aligned(PAGE_SIZE)));

    9. static DEFINE_PER_CPU(union irq_ctx *, hardirq_ctx);
    10. static DEFINE_PER_CPU(union irq_ctx *, softirq_ctx);
    11. ……

    12. static void call_on_stack(void *func, void *stack)
    13. ……
    复制代码
    是的,这个版本相对复杂,但是如果看了复杂的,再来看简单的,就容易多了,当平台没有定义do_softirq函数时(__ARCH_HAS_DO_SOFTIRQ),
    内核提供了一个通用的:
    1. #ifndef __ARCH_HAS_DO_SOFTIRQ

    2. asmlinkage void do_softirq(void)
    3. {
    4.         __u32 pending;
    5.         unsigned long flags;

    6.         if (in_interrupt())
    7.                 return;

    8.         local_irq_save(flags);

    9.         pending = local_softirq_pending();

    10.         if (pending)
    11.                 __do_softirq();

    12.         local_irq_restore(flags);
    13. }

    14. #endif
    复制代码
    无需更多的解释,它非常的简洁。

    不论是哪个版本,都将调用__do_softirq函数:
    1. asmlinkage void __do_softirq(void)
    2. {
    3.         struct softirq_action *h;
    4.         __u32 pending;
    5.         int max_restart = MAX_SOFTIRQ_RESTART;
    6.         int cpu;

    7.         //保存位图
    8.         pending = local_softirq_pending();
    9.         //进程记帐
    10.         account_system_vtime(current);

    11.         //关闭本地CPU下半部。为了保证同一个CPU上的软中断以串行方式执行。
    12.         __local_bh_disable((unsigned long)__builtin_return_address(0));
    13.         lockdep_softirq_enter();

    14.         //获取本地CPU
    15.         cpu = smp_processor_id();
    16. restart:
    17.         /* Reset the pending bitmask before enabling irqs */
    18.         //清除位图
    19.         set_softirq_pending(0);

    20.         //锁中断,只是为了保持位图的互斥,位图处理完毕。后面的代码可以直接使用保存的pending,
    21.         //而中断处理程序在激活的时候,也可以放心地使用irq_stat.__softirq_pending。
    22.         //所以,可以开中断了
    23.         local_irq_enable();

    24.         //取得软中断向量
    25.         h = softirq_vec;

    26.         //循环处理所有的软中断
    27.         do {
    28.                 //逐步取位图的每一位,判断该位上是否有软中断被设置。若有,处理之
    29.                 if (pending & 1) {
    30.                         //保存抢占计数器
    31.                         int prev_count = preempt_count();
    32.                         kstat_incr_softirqs_this_cpu(h - softirq_vec);

    33.                         trace_softirq_entry(h, softirq_vec);
    34.                         //调用软中断
    35.                         h->action(h);
    36.                         trace_softirq_exit(h, softirq_vec);
    37.                         //判断软中断是否被抢占,如果是,则输出一段错误信息
    38.                         if (unlikely(prev_count != preempt_count())) {
    39.                                 printk(KERN_ERR "huh, entered softirq %td %s %p"
    40.                                        "with preempt_count %08x,"
    41.                                        " exited with %08x?\n", h - softirq_vec,
    42.                                        softirq_to_name[h - softirq_vec],
    43.                                        h->action, prev_count, preempt_count());
    44.                                 preempt_count() = prev_count;
    45.                         }
    46.                         //??qsctr,这个是啥东东
    47.                         rcu_bh_qsctr_inc(cpu);
    48.                 }
    49.                 //指向下一个软中断槽位
    50.                 h++;
    51.                 //移位,取下一个软中断位
    52.                 pending >>= 1;
    53.         } while (pending);

    54.         //当软中断处理完毕后,因为前面已经开了中断了,所以有可能新的软中断已经又被设置,
    55.         //软中断调度程序会尝试重新软中断,其最大重启次数由max_restart决定。
    56.         //所以,这里必须再次关闭中断,再来一次……
    57.         local_irq_disable();

    58.         //取位图
    59.         pending = local_softirq_pending();
    60.         //有软中断被设置,且没有超过最大重启次数,再来一次先
    61.         if (pending && --max_restart)
    62.                 goto restart;

    63.         //超过最大重启次数,还有软中断待处理,调用wakeup_softirqd。其任处是唤醒软中断守护进程ksoftirqd。
    64.         if (pending)
    65.                 wakeup_softirqd();

    66.         lockdep_softirq_exit();

    67.         account_system_vtime(current);
    68.         //恢复下半部
    69.         _local_bh_enable();
    70. }
    复制代码
    中断跟踪
    如果中断跟踪CONFIG_TRACE_IRQFLAGS被定义,lockdep_softirq_enter/lockdep_softirq_exit用于递增/递减当前进程的软中断上下文计数器softirq_context:
    1. # define lockdep_softirq_enter()        do { current->softirq_context++; } while (0)
    2. # define lockdep_softirq_exit()        do { current->softirq_context--; } while (0)
    复制代码
    trace_softirq_entry与trace_softirq_exit配合使用,可以用于判断软中断的延迟。

    展开全文
  • 1、简介 2、中断处理 3、软中断

    目录

    1、中断简介

    1.1 作用

    1.2 物理实现

    1.3 中断请求线IRQ

    1.4 异常

    2、中断处理程序

    2.1 作用

    2.2 上半部和下半部

    2.3 中断上下文

    3、中断系统

    3.1 中断机制的实现

    3.2 中断控制

    4、下半部和软中断

    4.1 下半部简介

    4.2 软中断

    4.3 tasklet

    4.4 工作队列


    1、中断简介

    1.1 作用

        中断机制,是操作系统用来实现处理器和外部设备协同工作的方案,让硬件在需要的时间主动向内核发出信号。

    1.2 物理实现

        中断是一种特殊的电信号,由硬件生成。中断生成和处理的主要流程:

    1. 硬件生成中断电信号,并直接送入中断控制器的输入引脚;
    2. 中断控制器接收到一个中断信号后,向处理器发送一个电信号;
    3. 处理器检测到这个电信号后,会立即中断自己当前的工作,并处理这个中断(向操作系统反映此信号的到来);
    4. 操作系统处理这个中断请求

        硬件设备在产生中断的时候,并不会考虑与处理器的时钟同步问题,也就是说,中断会随时产生,内核随时可能因为新到来的中断而被打断。

    1.3 中断请求线IRQ

        中断请求线指的是中断的唯一数字标志。不同设备对应的中断不同,操作系统通过IRQ,来区分中断的来源是什么硬件设备,以提供相应的中断处理程序。

        IRQ的值是和硬件相关的。在经典的PC机上,IRQ 0是时钟中断,IRQ 1是键盘中断;而在PCI总线上的设备,中断的IRQ则是动态分配的。其他非PC的体系结构里,也有动态分配可用动态的特性。

    1.4 异常

        异常也被称为同步中断,因为异常产生的时候,必须考虑和处理器时钟同步的问题。

        在处理器执行到编程失误导致的错误之类(如被0除),或者执行期间出现特殊情况(如缺页),必须靠内核来处理的时候,处理器就会产生一个异常。所以,很多处理器体系结构处理异常和处理中断的方式非常类似。

    2、中断处理程序

    2.1 作用

        在响应一个特定中断的时候,内核会执行一个函数:中断处理程序,或者叫中断服务例程(ISR,Interrupt Service Routine)。中断处理程序,是一个设备的驱动的一部分,一方面负责通知硬件设备中断已经被接收,另外还有其他大量和设备相关的工作。例如对网卡的中断,还需要把来自网络的数据包拷贝到内存,并处理然后再交给合适的协议栈和应用程序。

    2.2 上半部和下半部

        由于中断随时可能发生,所以必须保证中断处理程序可以快速执行;但是中断处理程序可能又会处理大量的任务,两者之间存在矛盾,所以一般会把中断处理的过程分成两部分:上半部和下半部。

        上半部也叫硬中断,是通常意义上的中断处理程序,用来接收中断,和简单的、有时限的处理工作,例如对中断接收后进行应答或者复位硬件等需要在所有中断被禁止的情况下完成的工作。而其他的允许稍后完成的工作,则会推迟到下半部在合适的时间完成。Linux有多种机制来实现下半部,其中一种就是软中断。

        对于网卡的中断处理,上半部会执行 通知硬件、拷贝网络数据报到内存并继续读取新数据包,这些重要、紧急且与硬件相关的工作,因为网卡接收的网络数据包的缓存大小通常是固定的、有限的,一旦被延迟可能造成缓存溢出。而数据包的处理等操作,则由下半部来完成。

    2.3 中断上下文

        中断处理程序运行在中断上下文中。中断上下文也被称为原子上下文,因为该上下文中执行的代码是不可阻塞的。

        中断上下文不可以睡眠,所以中断上下文中的代码中,不允许使用睡眠函数。

        中断上下文的执行,是打断了其他代码,甚至是其他中断线上的中断处理程序的执行,所以是有严格的时间限制的,所以中断上下文中的代码必须简洁迅速。尽量把工作分离出来放到下半部执行,因为下半部可以在更合适的实现来执行。

        在Linux 2.6版本中,内核为每个中断处理程序分部增加了一个中断栈,大小为1页(32位系统是4KB,64位系统是8KB)。

    3、中断系统

    3.1 中断机制的实现

    1. 设备产生中断,通过总线将电信号发送给中断控制器;
    2. 如果中断线是激活的(中断线允许被屏蔽),中断控制器将中断信号发送给处理器。在大多数体系结构里,这一步是通过电信号给处理器的特定管脚发送一个信号;
    3. 处理器如果没有禁止这个中断,则立即停止正在进行的事情,关闭中断系统,并跳转到内存中预定义的位置,执行那里的代码。这个预定义的位置,是内核设置的,中断程序的入口。对于每条中断线,内核都会跳转到一个唯一的位置,内核以此获取中断的IRQ号;
    4. 内核首先在栈中保存中断的IRQ号,并保存寄存器的值,这些值是被中断任务的;
    5. 内核调用do_IRQ()方法响应中断:
      1. 对接收的中断进行应答,并禁止这条线的中断传递;
      2. do_IRQ()检查这条中断线是否有一个有效的处理程序,而且这条程序已经启动,但是当前没有执行;
      3. 如果符合条件,do_IRQ()调用handle_IRQ_event()运行这条中断线的中断处理程序;
      4. 如果不符合条件,或者handle_IRQ_event()执行完成后,do_IRQ()会调用ret_from_intr()方法,结束中断过程,返回中断前执行的用户地址空间。

    3.2 中断控制

        Linux内核提供了一套用来操作中断的系统调用,可以用来屏蔽处理器的中断,或者屏蔽掉一部分、甚至某一条中断线的中断。这些可以在<asm/system.h>和<asm/irq.h>中找到。

        Linux之所以要实现中断控制系统,主要是因为处理器需要同步。通过禁止中断,可以防止某个或者某些中断处理程序不会抢占当前内核执行的代码;此外,禁止中断还可以禁止内核抢占。但是禁止中断和禁止内核抢占,并不能防止其他处理器并发访问。想要防止其他处理器的并发访问,需要Linux的锁保护机制来实现。

    1、禁止和激活全部中断

        linux内核提供了接口,用于禁止或激活全部中断线的中断请求:

    1. local_irq_disable():禁止全部中断线的中断请求;
    2. local_irq_enable():激活全部中断线的中断请求。

        这两个接口会有一些问题:它们会无条件的禁止和激活所有的中断线。如果有部分中断线本来就是被禁止的,local_irq_enable()也会将它们恢复。在很多场景激活所有的中断线可能会带来一些问题。

    2、禁止中断和恢复

      由于禁止和激活全部中断会带来一些问题,Linux内核提供了禁止中断并恢复禁止前状态的调用:

    1. unsigned long flags:值传递的一些参数
    2. local_irq_save(flags):保存本地中断的当前状态,然后禁止这些中断线的中断请求;
    3. local_irq_restore(flags):恢复本地中断控制状态到保存的状态。

    3、禁止指定中断线的中断

        一些情况下,操作系统只需要禁止某条中断线的中断,即屏蔽这条中断线。例如在执行某条中断线的中断处理程序时,一般会屏蔽这条中断线。

        Linux提供了相应的系统调用:

    1. disable_irq(unsigned long irq):在当前中断线的中断处理程序执行完成后,屏蔽这条中断线;
    2. disable_irq_nosync(unsigned long irq):立即屏蔽这条中断线;
    3. enable_irq(unsigned long irq):激活某条中断线;
    4. synchronize_irq(unsigned long irq):等待某条中断线的中断处理程序退出,如果该处理程序正在执行,则处理程序退出后方法才会返回。

    4、下半部和软中断

        根据上面的内容可知:

    • 中断处理过程的上半部就是硬中断,主要负责处理中断过程中和硬件相关的、对时间敏感的操作;
    • 下半部主要用来处理一些比较耗时的操作,linux有很多中实现方式,包括tasklet、软中断、工作队列。即软中断是下半部的一种实现方式。

    4.1 下半部简介

    1、为什么要使用下半部

    1. 如果所有任务都放到上半部执行,会导致处理器较长时间不能处理一部分甚至全部中断:当一个中断正在处理的时候,这个中断线在所有处理器上都会被屏蔽;甚至如果一个处理程序是IRQF_DISABLED类型的,它在执行的过程中会屏蔽所有中断
    2. 中断处理器不在进程上下文中执行,所以它们不能被阻塞,这在很多场景会有限制;
    3. 中断处理程序是异步执行的。

        所以应该尽量缩短中断处理程序的执行时间。这样,我们会将和硬件不相干的、对时间不太敏感的操作,尽量放到下半部执行

    2、什么任务会放到下半部

        Linux操作系统目前没有办法强制要求哪些任务放到下半部,目前完全取决于开发者自己去判断。但是有一些规则提供借鉴:

    1. 如果一个任务,对时间非常敏感,则应该放到上半部;
    2. 如果一个任务,和硬件相关,则应该放到上半部;
    3. 如果一个任务,要求不被其他任务打断,尤其是不被同类型的中断打断,则应该放到上半部;
    4. 其他的任务都应该放到下半部。

    3、下半部的实现方式

        对于现在的Linux 2.6来说,主要有三种方式实现下半部:

    1. 软中断:有些地方会混淆软中断和下半部的概念,实际上软中断是下半部的实现方式之一。
    2. tasklet:tasklet实际上是基于软中断实现的,是在性能和易用性之间寻求平衡的产物。对于大部分软中断,使用tasklet即可。
    3. 工作队列:和上面两种不同,工作队列可以把任务退后,交给内核线程去执行。工作队列是工作在进程上下文中的,用于进程的各种特性,比如睡眠等。

    4.2 软中断

        软中断是编译期间动态分配的,不像tasklet一样可以动态的注册和销毁。软中断最多只有32个软中断,大部分下半部都是通过tasklet来实现的,目前只用了9个左右。

        软中断运行在软中断上下文中,软中断的执行会抢占进程上下文。软中断执行过程中,不允许重新调度和睡眠。

    1、资源抢占

    1. 软中断不会被另一个软中断打断,只有硬件的中断处理程序才能打断软中断;
    2. 另一个软中断可以在其他处理器上执行;
    3. 同一种软中断,也可以在另一个处理器上执行

    2、什么时候应该使用软中断实现下半部

        软中断应该保留给系统中对时间要求最严格的下半部使用。目前只有两个子系统直接使用软中断:网络和SCSI。此外,内核定时器和tasklet也是基于软中断实现的。由于同一个软中断可以在不同处理器上同时执行,所以软中断要求对共享资源的处理要非常严格,锁保护的要求很高。

    3、软中断的触发和执行

        一个注册的软中断被标记后才会被执行,这被称为软中断的触发。通常中断处理程序会在返回前标记相应的软中断,使其稍后会被执行。

        待处理的软中断,被检查和执行的时机包括:

    1. 从一个硬件中断代码处返回;
    2. 在ksoftirqd内核线程中;
    3. 在显式检查和执行待处理的软中断的代码中,如网络子系统中。

    4.3 tasklet

        tasklet是基于软中断实现的一种下半部机制,所以也是运行在软中断上下文的。

        相比于直接使用软中断,tasklet的接口更简单,锁保护的要求也更低。tasklet可以静态定义,也可以动态初始化。

        tasklet由两类软中断代表:HI_SOFTIRQ和TASKLET_SOFTIRQ。这两者唯一的区别在于,HI_SOFTIRQ类型的软中断先于TASKLET_SOFTIRQ类型的软中断执行。

    1、资源抢占

    1. tasklet不会被其他tasklet打断,只有硬件的中断处理程序才能打断tasklet;
    2. 如果一个tasklet正在一个处理器上执行,则这个tasklet不允许在本处理器和其他处理器上再次触发;
    3. 不同的tasklet可以同时在不同的处理器上执行。

    4.4 工作队列

        工作队列可以把任务推后,交由一个内核线程来完成。工作队列的任务是运行在进程上下文中的,这一点和软中断、tasklet不同,工作队列可以使用进程的特性,最重要的是可以重新调度和睡眠。

        一般来说,如果任务需要睡眠或者重新调度,就需要使用工作队列;但是如果不需要,一般使用tasklet来实现。使用工作队列的场景一般包括:需要获取大量内存的场景、需要获取信号量或者锁的场景、需要阻塞式的执行IO的场景等。

        理论上可以用创建内核线程的方式来代替工作队列,但是由于随便创建内核线程会带来其他问题,所以实际上并不建议直接创建内核线程,而应使用工作队列的形式。

    展开全文
  • 因此期望让中断处理程序运行得快,并想让它完成的工作量多,这两个目标相互制约,诞生——顶/底半部机制,本文主要介绍中断机制底半部的软中断的详细执行过程。 如需了解中断的整体过程请点击链接:****Linux中断...
  • 实验六Linux进程间通信24课时 实验目的 理解进程通信原理掌握进程中信号量共享内存消息队列相关的函数的使用 实验原理 Linux下进程通信相关函数除上次实验所用的几个还有 信号量 信号量又称为信号灯它是用来协调不同...
  • 为此, Linux中断服务程序一分为二,各称作“Top Half”和“Bottom Half”。前者通常对时间要求较为严格,必须在中断请求发生后立即或至少在一定的时间限制内完成。因此为了保证这种处理能原子地完成,Top Half...
  • Linux中断下半部处理有三种方式:软中断、tasklet、工作队列。  曾经有人问我为什么要分这几种,该怎么用。当时用书上的东西蒙混了过去,但是自己明白自己实际上是不懂的。近有时间了,于是试着整理一下linux的...
  • 软中断分析

    2019-04-16 18:51:25
    为什么要软中断? 编写驱动的时候,一个中断产生之后,内核在中断处理函数中可能需要完成很多 工作。但是中断处理函数的处理是关闭了中断的。也就是说在响应中断时,系统 不能再次响应外部的其它中断。这样的后果会...
  • 实验三 软中断通信

    千次阅读 2021-05-17 21:02:40
    实验三 软中断通信实验目的1、了解什么是信号2、熟悉LINUX系统中进程之间软中断通信的基本原理实验内容1、编写程序:用fork( )创建两个子进程,再用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按^c键);...
  • Linux中软中断实现分析在Linux中最多可以注册32个软中断,目前系统用了6个软中断,他们为:定时器处理、SCSI处理、网络收发处理以及Tasklet机制,这里的tasklet机制就是用来实现下半部的,描述软中断的核心数据结构...
  • 用一个不可中断进程的案例,带你学习了 iowait(也就是等待 I/O 的 CPU 使用率)升高时的分析方法。这里你要记住,进程的不可中断状态是系统的一种保护机制,可以保证硬件的交互过程不被意外打断。所以,短时间的不...
  • Tasklet作为一种新机制,显然可以承担更多的优点。正好这时候SMP越来越火了,因此又在tasklet中加入了SMP机制,保证同种中断只能在一个cpu上...    Linux中断下半部处理有三种方式:软中断、tasklet、工作队列。
  • 操作系统实验模板,实验环境是linux,实验内容是进程间的软中断通信。
  • 本文主要内容:硬中断 / 软中断原理和实现 内核版本:2.6.37 Author:zhangskd @ csdn blog 概述 从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断...
  • 加深对进程概念的理解,明确进程和程序的区别。进一步认识并发执行的实质,并了解Linux系统中进程通信的基本原理
  • Linux内核:中断、软中断、tasklet

    千次阅读 2017-10-11 11:23:30
    在之前我所写的Linux驱动程序中,会经常使用到中断机制,像CC1100高频驱动、倒车雷达驱动等等。但所用到的中断机制都基本上是用到中断的顶半部,即:编写中断处理函数,通过request_irq函数申请中断,这样当中断来临...
  • 查看linux 中断

    千次阅读 2021-05-14 17:22:13
    涉及linux软中断原理部分请参考:http://book.51cto.com/art/200912/168622.htm一、利用sysstat 中的工具包mpstatps:图片是截取个人电脑,所以没什么压力:16时32分48秒 CPU %usr %nice %sys %iowait %irq %soft %...
  • Linux软中断通信实验报告实验2 Linux软中断通信1.实验目的通过本实验,掌握软中断的基本原理;掌握中断信号的使用、进程的创建以及系统计时器的使用。2.实验内容(上交的实验2统一取名为:test2由父进程创建两个子...
  • 中断是系统用来响应硬件设备请求的一种机制,它会打断进程的正常调度和执行,然后调用内核中的中断处理程序来响应设备的请求。 一、为什么要有中断 举个生活中的例子,让你感受一下中断的魅力。比如说你订了一份...
  • Linux进程间软中断通信

    千次阅读 2020-05-14 09:58:09
    用系统调用signal()让父进程捕捉键盘上的中断信号(Ctrl+C或者Ctrl + ) 捕捉到中断信号后,父进程调用系统调用Kill杀死两个子进程 #include<unistd.h> #include<string.h> #include<stdlib.h> #...
  • 2、熟悉LINUX系统中进程之间软中断通信的基本原理 二、实验内容: 1、编写程序:用fork( )创建两个子进程,再用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按^c键);捕捉到中断信号后,父进程用系统调用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 18,849
精华内容 7,539
关键字:

linux软中断原理