微信公众号:嵌入式Linux中文站

Table of Contents

概述

(1) 硬中断

(2) 软中断

(3) 中断嵌套

(4) 软中断指令

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

开关

(1) 硬中断的开关

(2) 软中断的开关

(3) 判断中断状态

硬中断

(1) 注册中断处理函数

(2) 注销中断处理函数

软中断

(1) 定义

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

(3) 触发软中断

(4) ksoftirqd内核线程


概述

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

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

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

(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);

中断处理函数本身:

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. 1. 从一个硬件中断代码处返回时
  2. 2. 在ksoftirqd内核线程中
  3. 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;
}

Linux 硬中断和软中断相关推荐

  1. 硬中断、软中断和信号

    硬中断: 1. 硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等.每个设备或设备集都有它自己的IRQ(中断请求).基于IRQ,CPU可以将相应的请求分发到对应的硬件驱动上(注:硬件驱动通常是内核 ...

  2. 硬中断、软中断、中断上半部、中断下半部

    中断从外设到内核的DataPath 概念 从物理学的角度看,中断是一种电信号,由硬件设备生成,并直接送入中断控制器的输入引脚上. 然后再由中断控制器向处理器发送相应的信号.处理器一经检测到此信号,便中 ...

  3. linux内核网络收包过程—硬中断与软中断

    目录 硬中断处理 软中断处理 数据通过网络发送过来 硬中断处理 数据帧首先到达网卡的接收队列,分配RingBuffer DMA把数据搬运到网卡关联的内存 网卡向CPU发起硬中断,通知CPU有数据 调用 ...

  4. 硬中断与软中断的区别!

    硬中断: 1. 硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等.每个设备或设备集都有它自己的IRQ(中断请求).基于IRQ,CPU可以将相应的请求分发到对应的硬件驱动上(注:硬件驱动通常是内核 ...

  5. 硬中断与软中断的区别!!!

    硬中断: 1. 硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等.每个设备或设备集都有它自己的IRQ(中断请求).基于IRQ,CPU可以将相应的请求分发到对应的硬件驱动上(注:硬件驱动通常是内核 ...

  6. *33.硬中断和软中断是什么?区别?

    1.硬中断-硬件中断.像磁盘,网卡,键盘,时钟等 2.软中断-软件中断.进程产生. 区别:(产生机制.处理顺序.可否屏蔽.中断信号由来) 1.软中断的发生的时间是由程序控制的,硬中断的发生的时间是随机 ...

  7. 硬中断与软中断的区别

    摘自:https://blog.51cto.com/noican/1361087 硬中断: 1. 硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等.每个设备或设备集都有它自己的IRQ(中断请求) ...

  8. Linux内核中断系统结构——软中断

    在 Linux异常(中断)处理体系结构 这篇文章,我们详细描写了内核如何进行中断(异常)向量表的初始化.如何初始化硬件中断(IRQ)的操作. 在这篇文章中,我们将重心放在软件中断上.也就是 CPU 本 ...

  9. [Linux内核]软中断与硬中断

    转自:http://blog.csdn.net/zhangskd/article/details/21992933 本文主要内容:硬中断 / 软中断的原理和实现 内核版本:2.6.37 Author: ...

最新文章

  1. 如何确定Windows服务器上是否打开了端口? [关闭]
  2. Python语言程序设计之urllib.request抓取页面,网易公开课之《麻省理工学院公开课:算法导论》
  3. 局部图像描述子——SIFT(尺度不变特征变换)
  4. 2018高中计算机会考知识点,2018高中物理会考知识点总结
  5. 2021-2025年中国准分子飞秒眼科激光器行业市场供需与战略研究报告
  6. GAN能生成3D图像啦!朱俊彦团队公布最新研究成果
  7. 学习笔记-Linux从入门到秃头
  8. 电商帝国阿里巴巴起跑中国量子产业
  9. 笔记本电脑没有外放声音,但是插上耳机有声音的问题解决方法
  10. 怎么禁用笔记本的键盘
  11. ES分片UNASSIGNED解决方案(ALLOCATION_FAILED,REPLICA_ADDED,NODE_LEFT,REINITIALIZED,CLUSTER_RECOVERED等等)
  12. 《黑白团团队》第八次团队作业:Alpha冲刺 第四天
  13. drcom运行在路由器上
  14. Thinkpad E450c WIN8 重装系统 如何U盘启动
  15. 为什么手机信号满格,但网速却还是那么慢?这4点原因是关键
  16. 超级马里奥代码_任天堂源代码泄露,引出《超级马里奥64》隐藏24年的角色
  17. CorelDRAW VBA - 段落文本内容的导出 ExportToFile 方法
  18. C++语言判断一个数是否为素数1/2
  19. python计算派的值_使用 Python 计算 π 值
  20. 在Linux系统(CentOS 8.2)腾讯云服务器下部署原神云崽机器人(Yunzai-Bot)【保姆入门级】

热门文章

  1. 基于 SOA 的组件化业务基础平台
  2. FPGA开平方的实现
  3. NPM Unexpected end of JSON input while parsing near
  4. XPath 轴 Axes
  5. JDK API文档下载
  6. php输出文件,数组
  7. HDU 1698 Just a Hook 线段树
  8. ENC28J60 驱动开发要点
  9. 2008年.Net编程人员工具参照
  10. broadcast receiver 接收设备重启意图( boot_completed Broadcast Intent)而重启定时器