硬中断和软中断_软中断和硬中断的优先级

硬中断和软中断_软中断和硬中断的优先级本文主要内容:硬中断/软中断的原理和实现内核版本:2.6.37Author:zhangskd@csdnblog 概述 从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事,跳到中断处理程序的入口点,进行

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

本文主要内容:硬中断 / 软中断的原理和实现

内核版本:2.6.37

Author:zhangskd @ csdn blog

 

概述

 

从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。

如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事,

跳到中断处理程序的入口点,进行中断处理。

硬中断和软中断_软中断和硬中断的优先级

 

(1) 硬中断

由与系统相连的外设(比如网卡、硬盘)自动产生的。主要是用来通知操作系统系统外设状态的变化。比如当网卡收到数据包

的时候,就会发出一个中断。我们通常所说的中断指的是硬中断(hardirq)。

 

(2) 软中断

为了满足实时系统的要求,中断处理应该是越快越好。linux为了实现这个特点,当中断发生的时候,硬中断处理那些短时间

就可以完成的工作,而将那些处理事件比较长的工作,放到中断之后来完成,也就是软中断(softirq)来完成。

 

(3) 中断嵌套

Linux下硬中断是可以嵌套的,但是没有优先级的概念,也就是说任何一个新的中断都可以打断正在执行的中断,但同种中断

除外。软中断不能嵌套,但相同类型的软中断可以在不同CPU上并行执行。

 

(4) 软中断指令

int是软中断指令。

中断向量表是中断号和中断处理函数地址的对应表。

int n – 触发软中断n。相应的中断处理函数的地址为:中断向量表地址 + 4 * n。

 

(5)硬中断和软中断的区别

软中断是执行中断指令产生的,而硬中断是由外设引发的。

硬中断的中断号是由中断控制器提供的,软中断的中断号由指令直接指出,无需使用中断控制器。

硬中断是可屏蔽的,软中断不可屏蔽。

硬中断处理程序要确保它能快速地完成任务,这样程序执行时才不会等待较长时间,称为上半部。

软中断处理硬中断未完成的工作,是一种推后执行的机制,属于下半部。 

 

开关

 

(1) 硬中断的开关

简单禁止和激活当前处理器上的本地中断:

local_irq_disable();

local_irq_enable();

保存本地中断系统状态下的禁止和激活:

unsigned long flags;

local_irq_save(flags);

local_irq_restore(flags);

 

(2) 软中断的开关

禁止下半部,如softirq、tasklet和workqueue等:

local_bh_disable();

local_bh_enable();

需要注意的是,禁止下半部时仍然可以被硬中断抢占。

 

(3) 判断中断状态

#define in_interrupt() (irq_count()) // 是否处于中断状态(硬中断或软中断)

#define in_irq() (hardirq_count()) // 是否处于硬中断

#define in_softirq() (softirq_count()) // 是否处于软中断

 

硬中断

 

(1) 注册中断处理函数

注册中断处理函数:

/**
 * irq: 要分配的中断号
 * handler: 要注册的中断处理函数
 * flags: 标志(一般为0)
 * name: 设备名(dev->name)
 * dev: 设备(struct net_device *dev),作为中断处理函数的参数
 * 成功返回0
 */

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, 
    const char *name, void *dev);

Jetbrains全家桶1年46,售后保障稳定

 

中断处理函数本身:

typedef irqreturn_t (*irq_handler_t) (int, void *);

/**
 * enum irqreturn
 * @IRQ_NONE: interrupt was not from this device
 * @IRQ_HANDLED: interrupt was handled by this device
 * @IRQ_WAKE_THREAD: handler requests to wake the handler thread
 */
enum irqreturn {
    IRQ_NONE,
    IRQ_HANDLED,
    IRQ_WAKE_THREAD,
};
typedef enum irqreturn irqreturn_t;
#define IRQ_RETVAL(x) ((x) != IRQ_NONE)

 

(2) 注销中断处理函数

/**
 * free_irq - free an interrupt allocated with request_irq
 * @irq: Interrupt line to free
 * @dev_id: Device identity to free
 *
 * Remove an interrupt handler. The handler is removed and if the
 * interrupt line is no longer in use by any driver it is disabled.
 * On a shared IRQ the caller must ensure the interrupt is disabled
 * on the card it drives before calling this function. The function does
 * not return until any executing interrupts for this IRQ have completed.
 * This function must not be called from interrupt context.
 */

void free_irq(unsigned int irq, void *dev_id);

 

软中断

 

(1) 定义

软中断是一组静态定义的下半部接口,可以在所有处理器上同时执行,即使两个类型相同也可以。

但一个软中断不会抢占另一个软中断,唯一可以抢占软中断的是硬中断。

 

软中断由softirq_action结构体表示:

struct softirq_action {
    void (*action) (struct softirq_action *); /* 软中断的处理函数 */
};

 

目前已注册的软中断有10种,定义为一个全局数组:

static struct softirq_action softirq_vec[NR_SOFTIRQS];

enum {
    HI_SOFTIRQ = 0, /* 优先级高的tasklets */
    TIMER_SOFTIRQ, /* 定时器的下半部 */
    NET_TX_SOFTIRQ, /* 发送网络数据包 */
    NET_RX_SOFTIRQ, /* 接收网络数据包 */
    BLOCK_SOFTIRQ, /* BLOCK装置 */
    BLOCK_IOPOLL_SOFTIRQ,
    TASKLET_SOFTIRQ, /* 正常优先级的tasklets */
    SCHED_SOFTIRQ, /* 调度程序 */
    HRTIMER_SOFTIRQ, /* 高分辨率定时器 */
    RCU_SOFTIRQ, /* RCU锁定 */
    NR_SOFTIRQS /* 10 */
};

 

(2) 注册软中断处理函数

/**
 * @nr: 软中断的索引号
 * @action: 软中断的处理函数
 */

void open_softirq(int nr, void (*action) (struct softirq_action *))
{
    softirq_vec[nr].action = action;
}

例如:

open_softirq(NET_TX_SOFTIRQ, net_tx_action);

open_softirq(NET_RX_SOFTIRQ, net_rx_action);

 

(3) 触发软中断 

调用raise_softirq()来触发软中断。

void raise_softirq(unsigned int nr)
{
    unsigned long flags;
    local_irq_save(flags);
    raise_softirq_irqoff(nr);
    local_irq_restore(flags);
}

/* This function must run with irqs disabled */
inline void rasie_softirq_irqsoff(unsigned int nr)
{
    __raise_softirq_irqoff(nr);

    /* If we're in an interrupt or softirq, we're done
     * (this also catches softirq-disabled code). We will
     * actually run the softirq once we return from the irq
     * or softirq.
     * Otherwise we wake up ksoftirqd to make sure we
     * schedule the softirq soon.
     */
    if (! in_interrupt()) /* 如果不处于硬中断或软中断 */
        wakeup_softirqd(void); /* 唤醒ksoftirqd/n进程 */
}

Percpu变量irq_cpustat_t中的__softirq_pending是等待处理的软中断的位图,通过设置此变量

即可告诉内核该执行哪些软中断。

static inline void __rasie_softirq_irqoff(unsigned int nr)
{
    trace_softirq_raise(nr);
    or_softirq_pending(1UL << nr);
}

typedef struct {
    unsigned int __softirq_pending;
    unsigned int __nmi_count; /* arch dependent */
} irq_cpustat_t;

irq_cpustat_t irq_stat[];
#define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)
#define or_softirq_pending(x) percpu_or(irq_stat.__softirq_pending, (x))
#define local_softirq_pending() percpu_read(irq_stat.__softirq_pending)

 

唤醒ksoftirqd内核线程处理软中断。

static void wakeup_softirqd(void)
{
    /* Interrupts are disabled: no need to stop preemption */
    struct task_struct *tsk = __get_cpu_var(ksoftirqd);

    if (tsk && tsk->state != TASK_RUNNING)
        wake_up_process(tsk);
}

 

在下列地方,待处理的软中断会被检查和执行:

1. 从一个硬件中断代码处返回时

2. 在ksoftirqd内核线程中

3. 在那些显示检查和执行待处理的软中断的代码中,如网络子系统中

 

而不管是用什么方法唤起,软中断都要在do_softirq()中执行。如果有待处理的软中断,

do_softirq()会循环遍历每一个,调用它们的相应的处理程序。

在中断处理程序中触发软中断是最常见的形式。中断处理程序执行硬件设备的相关操作,

然后触发相应的软中断,最后退出。内核在执行完中断处理程序以后,马上就会调用

do_softirq(),于是软中断开始执行中断处理程序完成剩余的任务。

 

下面来看下do_softirq()的具体实现。

asmlinkage void do_softirq(void)
{
    __u32 pending;
    unsigned long flags;

    /* 如果当前已处于硬中断或软中断中,直接返回 */
    if (in_interrupt()) 
        return;

    local_irq_save(flags);
    pending = local_softirq_pending();
    if (pending) /* 如果有激活的软中断 */
        __do_softirq(); /* 处理函数 */
    local_irq_restore(flags);
}
/* We restart softirq processing MAX_SOFTIRQ_RESTART times,
 * and we fall back to softirqd after that.
 * This number has been established via experimentation.
 * The two things to balance is latency against fairness - we want
 * to handle softirqs as soon as possible, but they should not be
 * able to lock up the box.
 */
asmlinkage void __do_softirq(void)
{
    struct softirq_action *h;
    __u32 pending;
    /* 本函数能重复触发执行的次数,防止占用过多的cpu时间 */
    int max_restart = MAX_SOFTIRQ_RESTART;
    int cpu;

    pending = local_softirq_pending(); /* 激活的软中断位图 */
    account_system_vtime(current);
    /* 本地禁止当前的软中断 */
    __local_bh_disable((unsigned long)__builtin_return_address(0), SOFTIRQ_OFFSET);
    lockdep_softirq_enter(); /* current->softirq_context++ */
    cpu = smp_processor_id(); /* 当前cpu编号 */

restart:
    /* Reset the pending bitmask before enabling irqs */
    set_softirq_pending(0); /* 重置位图 */
    local_irq_enable();
    h = softirq_vec;
    do {
        if (pending & 1) {
            unsigned int vec_nr = h - softirq_vec; /* 软中断索引 */
            int prev_count = preempt_count();
            kstat_incr_softirqs_this_cpu(vec_nr);

            trace_softirq_entry(vec_nr);
            h->action(h); /* 调用软中断的处理函数 */
            trace_softirq_exit(vec_nr);

            if (unlikely(prev_count != preempt_count())) {
                printk(KERN_ERR "huh, entered softirq %u %s %p" "with preempt_count %08x,"
                    "exited with %08x?\n", vec_nr, softirq_to_name[vec_nr], h->action, prev_count,
                    preempt_count());
            }
            rcu_bh_qs(cpu);
        }
        h++;
        pending >>= 1;
    } while(pending);

    local_irq_disable();
    pending = local_softirq_pending();
    if (pending & --max_restart) /* 重复触发 */
        goto restart;

    /* 如果重复触发了10次了,接下来唤醒ksoftirqd/n内核线程来处理 */
    if (pending)
        wakeup_softirqd(); 

    lockdep_softirq_exit();
    account_system_vtime(current);
    __local_bh_enable(SOFTIRQ_OFFSET);
}

 

(4) ksoftirqd内核线程

内核不会立即处理重新触发的软中断。

当大量软中断出现的时候,内核会唤醒一组内核线程来处理。

这些线程的优先级最低(nice值为19),这能避免它们跟其它重要的任务抢夺资源。

但它们最终肯定会被执行,所以这个折中的方案能够保证在软中断很多时用户程序不会

因为得不到处理时间而处于饥饿状态,同时也保证过量的软中断最终会得到处理。

 

每个处理器都有一个这样的线程,名字为ksoftirqd/n,n为处理器的编号。

static int run_ksoftirqd(void *__bind_cpu)
{
    set_current_state(TASK_INTERRUPTIBLE);
    current->flags |= PF_KSOFTIRQD; /* I am ksoftirqd */

    while(! kthread_should_stop()) {
        preempt_disable();

        if (! local_softirq_pending()) { /* 如果没有要处理的软中断 */
            preempt_enable_no_resched();
            schedule();
            preempt_disable():
        }

        __set_current_state(TASK_RUNNING);

        while(local_softirq_pending()) {
            /* Preempt disable stops cpu going offline.
             * If already offline, we'll be on wrong CPU: don't process.
             */
             if (cpu_is_offline(long)__bind_cpu))/* 被要求释放cpu */
                 goto wait_to_die;

            do_softirq(); /* 软中断的统一处理函数 */

            preempt_enable_no_resched();
            cond_resched();
            preempt_disable();
            rcu_note_context_switch((long)__bind_cpu);
        }

        preempt_enable();
        set_current_state(TASK_INTERRUPTIBLE);
    }

    __set_current_state(TASK_RUNNING);
    return 0;

wait_to_die:
    preempt_enable();
    /* Wait for kthread_stop */
    set_current_state(TASK_INTERRUPTIBLE);
    while(! kthread_should_stop()) {
        schedule();
        set_current_state(TASK_INTERRUPTIBLE);
    }

    __set_current_state(TASK_RUNNING);
    return 0;
}

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/201132.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • dz论坛数据库php网址,discuz论坛更换域名,搬家步骤

    dz论坛数据库php网址,discuz论坛更换域名,搬家步骤搬家步骤具体如下:1、打包数据库文件保存到本地。2、打包网站根目录所有程序(discuz)保存到本地。3、上传网站程序包和数据库包到新的空间,将数据库上传到新的服务器中。4、找到config\config_global.php文件,修改以下3处内容:$_config[‘db‘][‘1‘][‘dbuser‘]=‘数据库用户名‘;$_config[‘db‘][‘1‘][‘dbpw‘]=‘数…

  • 6种不同画法画平行线_平行线的画法_中小学试题|家庭教育题库|辅导习题「中国戏曲学院附属中等戏曲学校」…

    6种不同画法画平行线_平行线的画法_中小学试题|家庭教育题库|辅导习题「中国戏曲学院附属中等戏曲学校」…平行线怎么画第五章《数学活动1》—-你有几种画平行线的方法【活动理念】通过让学生积极参与此次活动,获得成功的体验,感受活动课的乐趣.通过观察、操作、推理归纳,让学生进一步知道相交线、平行线以及垂线的概念,利用平行线的判定解决一些实际问题,利用平移可以绘制一些优美的图案等.【活动目标】一、知识与技能1、两直线平行的条件,掌握两种以上最快捷的画平行线的方法.2、进一步理解相交线、平行线以及垂线的概…

  • 【转】java对象转JSONObject、JSONObject转java对象及String转JSONObject

    【转】java对象转JSONObject、JSONObject转java对象及String转JSONObjectJSONObjectjo=(JSONObject)JSONObject.toJSON(javaBean);Studentstu=JSONObject.parseObject(jo,Student.class);JSONObjectjo=JSON.parseObject(str);原文:https://blog.csdn.net/qq_42407917/article/details/100151588

  • navicat激活码linux[最新免费获取]

    (navicat激活码linux)本文适用于JetBrains家族所有ide,包括IntelliJidea,phpstorm,webstorm,pycharm,datagrip等。IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.html…

  • 集合:映射,单射,满射,双射

    集合:映射,单射,满射,双射这些概念太重要了,虽然很不起眼,但是几乎所有地方都要用,一旦不清楚,会对一个问题审视错误。集合:元素汇聚在一起,就构成了一个集合。假设有集合A和集合B。映射:将A中的每一个元素,根据一个规则,对应到B中的一个元素去,比如A中是一些儿女,B中是他们的父亲,那么每一个A中的元素都可以对应到B中的唯一一个元素,这就是一种规则,即映射。因为一个人的爸爸只有一个,但是反之未必,因为一个爸爸可能有多个儿女。我们发现,这个和函数的概念非常相似,集合A相当于定义域,映射相当于f。但是集合的概念更加宽泛,正如前面所有

  • android bioset 进程,kthrotlds(WatchDogs变种)查杀方法「建议收藏」

    android bioset 进程,kthrotlds(WatchDogs变种)查杀方法「建议收藏」virus&period;win32&period;parite&period;H病毒的查杀方法virus.win32.parite.H病毒的查杀方法昨天电脑中了virus.win32.parite.H病毒,搞了2个多小时最终搞定了.以下记录下我的解决方法.第一步:下载Win32.Parit…Linux系统木马后门查杀方法详解木马和后门的查杀是系统管理员一项长期需…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号