设为首页 收藏本站
查看: 254|回复: 0

[经验分享] linux 内核软中断详解

[复制链接]
累计签到:77 天
连续签到:1 天
发表于 2014-5-27 10:12:43 | 显示全部楼层 |阅读模式
中断的作用:当一个中断信号到达时,CPU必须停止它当前正做的工作,转而去做中断要求其做的事情。

中断分为同步中断和异步中断两种。

1、同步中断又称异常,是由CPU执行指令时由CPU控制单元产生的。异常又分两种:

(1)、 一种是由程序执行出错造成的,内核通过发送一个unix的信号来处理异常。

(2)、一种是由内核必须处理的异常条件产生的,比如缺页异常,内核执行恢复异常的所有步骤。


2、异步中断,通常我们就叫中断。由其他硬件设备按照CPU时钟信号随机产生。


中断处理程序的一般步骤:
一个中断处理程序的几个中断服务例程之间是串行执行的,并且在一个中断处理程序结束前,不应该再次出现这个中断,所以一般中断处理程序是先禁止该中断,然后处理中断,处理完成后在使能该中断。

有一些中断是可以延迟处理的,这种可延迟中断可以在开中断的情况下执行,执行时允许其他中断抢占他。把可延迟中断从中断处理程序中抽出来有助于使内核保持较短的响应时间。

Linux内核使用三种方法来处理这种可延迟的中断任务:可延迟函数(软中断和tasklets)以及工作队列。工作队列是工作在进程上下文中,可以睡眠,软中断和tasklets 是工作在中断上下文,不可以睡眠。本节只讨论软中断和tasklets。
软中断:
Linux 2.6 版本使用如下几个软中断,不同版本之间略有差异。但一下几个不同版本都包含。

HI_SOFTIRQ=0,   处理高优先级的tasklet

TIMER_SOFTIRQ,  时钟中断相关的tasklet.

NET_TX_SOFTIRQ,  内核把数据报文传送给网卡。

NET_RX_SOFTIRQ,  内核从网卡接收数据报文。

TASKLET_SOFTIRQ, 处理常规tasklet。

低下标代表高优先级。

内核中定义了softirq_vec数组来存放各种软中断。定义如下:

static struct softirq_action softirq_vec[32]__cacheline_aligned_in_smp;

数组元素为 softirq_action,一个元素代码一个软中断。不同的软中断号对应不同的数组的下标。

struct softirq_action
{
    void (*action)(struct softirq_action *); //软中断发生时执行软中断的处理函数。
    void *data; //软中断的处理函数的参数指针。
};


初始化软中断时调用函数 open_softirq().如下代码。
void open_softirq(int nr, void (*action)(struct softirq_action*),void *data)
{
    softirq_vec[nr].data = data;
    softirq_vec[nr].action = action;
}
另外一个跟软中断相关的关键字段是 32 位的 preempt_counte字段,用它来跟踪内核抢占和内核控制路径的嵌套,该字段放在每个进程描述符的 thread_info 字段中。用函数preempt_count()来返回该字段的值。

preempt_count字段
位     描述
0~7     抢占计数器,记录显示禁用本地cpu内核抢占的次数,值为0时代表内核允许抢占。
8~15     软中断计数器。记录软中断被禁用的次数,0表示软中断被激活。
16~27     硬中断计数器。记录硬中断嵌套的层数。irq_entry()增加它的值,irq_exit()递减它的值。
28     


当内核明确不允许发生抢占或内核正在中断上下文中运行时,必须禁止内核的抢占功能。为了确定当前进程是否能够被抢占,内核快速检查preempt_counte字段是否等于零。

另一个跟软中断相关的字段是每个CPU都有一个32位掩码的字段

typedef struct {

unsigned int __softirq_pending;

} ____cacheline_aligned irq_cpustat_t;

他描述挂起的软中断。每一位对应相应的软中断。比如0位代表HI_SOFTIRQ.

宏local_softirq_pending()来获取该字段的值。

使用函数raise_softirq()来激活软中断。即把响应的软中断号对应的__softirq_pending中的位置1.表示该软中断被挂起。如果当前CPU不在中断上下文中,唤醒内核线程ksoftirqd来检查被挂起的软中断,然后执行相应软中断处理函数。

内核在如下几个点上检查被挂起的软中断:

1、当调用local_bh_enable()函数激活本地CPU的软中断时。条件满足就调用do_softirq() 来处理软中断。

2、当do_IRQ()完成硬中断处理时调用irq_exit()时调用do_softirq()来处理软中断。

3、当一个特殊内核线程ksoftirq/n被唤醒时,处理软中断。

软中断处理函数详解:


    asmlinkage void do_softirq(void)

    {

        __u32 pending;

        unsigned long flags;

        /*如果当前处于硬中断中,在硬中断处理函数退出时会调用irq_exit()函数来处理软中断,
          或当前软中断被禁用.所以in_interrupt()返回不为1 就没必要处理软中断,直接返回*/

        if (in_interrupt())

           return;

        /*保持中断寄存器的状态并禁用本地CPU的中断*/

        local_irq_save(flags);

        /*取得当前cpu上__softirq_pending字段,获取本地CPU上挂起的软中断*/

        pending = local_softirq_pending();

        /*如果当前CPU上有挂起的软中断,执行__do_softirq()来处理软中断*/

        if (pending)

        {

            __do_softirq();
            }

        /*恢复中断寄存器的状态*/

        local_irq_restore(flags);

    }



    asmlinkage void __do_softirq(void)

    {

        struct softirq_action *h;

        __u32 pending;

        int max_restart = MAX_SOFTIRQ_RESTART; //10

        int cpu;

          /*取得当前cpu上__softirq_pending字段,获取本地CPU上挂起的软中断*/

        pending = local_softirq_pending();

        /*debug 用,不讨论*/

        account_system_vtime(current);

        /*禁止本地cpu的软中断,现在本地cpu上挂起的软中断已经存入pending临时变量中了*/

        __local_bh_disable((unsigned long)__builtin_return_address(0));

        /*debug 用,不讨论*/

        trace_softirq_enter();

        /*取本地cpu id 号*/

        cpu = smp_processor_id();

    restart:

        /* Reset the pending bitmask before enabling irqs */

        /*清空本地cpu的__softirq_pending字段*/

        set_softirq_pending(0);

        /*开启本地cpu的硬中断*/

        local_irq_enable();

        /*循环执行被挂起的软中断处理函数。相应的软中断的处理函数存在数组softirq_ver[nr]中的元素 softirq_action->action中*/

        h = softirq_vec;

        do {
               if (pending & 1) {
                 h->action(h);
                 rcu_bh_qsctr_inc(cpu);
               }

             h++;

             pending >>= 1;

        } while (pending);

        /*禁止本地CPU的硬中断*/

        local_irq_disable();

        /*取本地CPU的__softirq_pending,查看是否还有新的被挂起的软中断并且检查被挂起软中断的次数小于10次,如果条件满足,
          继续处理新的被挂起的软中断*/

        pending = local_softirq_pending();

        if (pending && --max_restart)
              goto restart;

        /*如果有新的挂起的软中断并且处理循环次数已经够了10次,
          唤醒ksoftirq内核线程来处理软中断*/

        if (pending)
              wakeup_softirqd();

        /*debug 用,不讨论*/
          trace_softirq_exit();

        account_system_vtime(current);

        /*使能本地CPU的软中断*/
          _local_bh_enable();

    }




运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-19740-1-1.html 上篇帖子: wireshark抓取某IP/端口网络包 下篇帖子: linux 内核tasklet详解
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表