linux 内核软中断介绍
在介绍软中断之前,先来介绍一个概念:中断下半部:
为了避免处理复杂的中断嵌套,中断处理程序是在关闭中断的情况下执行的。可是,如果关闭中断的时间太长,可能导致中断请求丢失。例如周期时钟每隔 10 毫秒发送一个中断请求,如果执行某个中断处理程序花费的时间超过 10 毫秒,在这段时间里时钟发送了两个中断请求,但是处理器只认为收到一个时钟中断请求。
最激进的解决办法是中断线程化,但是常用的解决办法是:把中断处理程序分为两部分,上半部(top half, th)在关闭中断的情况下执行,只做对时间非常敏感、与硬件相关或者不能被其他中断打断的工作;下半部(bottom half,bh)在开启中断的情况下执行,可以被其他中断打断。
上半部称为硬中断(hardirq),下半部有 3 种:软中断(softirq)、小任务(tasklet)和工作队列(workqueue)。 3 种下半部的区别如下。
(1)软中断和小任务是工作在中断上下文不允许睡眠;工作队列是使用内核线程实现的是工作在进程上下文,处理函数可以睡眠。
(2)软中断的种类是编译时静态定义的,在运行时不能添加或删除;小任务可以在运行时添加或删除。
(3)同一种软中断的处理函数可以在多个处理器上同时执行,处理函数必须是可以重入的,需要使用锁保护临界区;一个小任务同一时刻只能在一个处理器上执行,不要求处理函数是可以重入的。
软中断(softirq)是中断处理程序在开启中断的情况下执行的部分,可以被硬中断抢占。
内核定义了一张软中断向量表,每种软中断有一个唯一的编号,对应一个 softirq_action实例, softirq_action 实例的成员 action 是处理函数。
kernel/softirq.c
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
include/linux/interrupt.h
struct softirq_action
{
void (*action)(struct softirq_action *);
};
2.1 软中断的种类
目前内核定义了 10 种软中断,各种软中断的编号如下:
include/linux/interrupt.h
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
IRQ_POLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ, /* 没有使用,但是保留,因为有些工具依赖这个编号 */
RCU_SOFTIRQ, /* RCU软中断应该总是最后一个软中断 */
NR_SOFTIRQS
};
(1) HI_SOFTIRQ:高优先级的小任务。
(2) TIMER_SOFTIRQ:定时器软中断。
(3) NET_TX_SOFTIRQ:网络栈发送报文的软中断。
(4) NET_RX_SOFTIRQ:网络栈接收报文的软中断。
(5) BLOCK_SOFTIRQ:块设备软中断。
(6) IRQ_POLL_SOFTIRQ:支持 I/O 轮询的块设备软中断。
(7) TASKLET_SOFTIRQ:低优先级的小任务。
(8) SCHED_SOFTIRQ:调度软中断,用于在处理器之间负载均衡。
(9) HRTIMER_SOFTIRQ:高精度定时器,这种软中断已经被废弃,目前在中断处理程序的上半部处理高精度定时器。
(10) RCU_SOFTIRQ: RCU 软中断。
软中断的编号形成了优先级顺序,编号小的软中断优先级高。
注册软中断的处理函数
函数 open_softirq()用来注册软中断的处理函数,在软中断向量表中为指定的软中断编号设置处理函数。
kernel/softirq.c
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
同一种软中断的处理函数可以在多个处理器上同时执行,处理函数必须是可以重入的,需要使用锁保护临界区。
触发软中断
函数 raise_softirq 用来触发软中断,参数是软中断编号。
void raise_softirq(unsigned int nr);
在已经禁止中断的情况下可以调用函数 raise_softirq_irqoff 来触发软中断。
void raise_softirq_irqoff(unsigned int nr);
函数 raise_softirq 在当前处理器的待处理软中断位图中为指定的软中断编号设置对应的位,如下所示:
raise_softirq() -> raise_softirq_irqoff() -> __raise_softirq_irqoff()
kernel/softirq.c
void __raise_softirq_irqoff(unsigned int nr)
{
or_softirq_pending(1UL << nr);
}
把宏 or_softirq_pending 展开以后是:
irq_stat[smp_processor_id()].__softirq_pending |= (1UL << nr);
执行软中断
内核执行软中断的地方如下。
(1)在中断处理程序的后半部分执行软中断,对执行时间有限制:不能超过 2 毫秒,并且最多执行 10 次。
(2)每个处理器有一个软中断线程,调度策略是 SCHED_NORMAL,优先级是 120。
(3)开启软中断的函数 local_bh_enable()。
如果开启了强制中断线程化的配置宏 CONFIG_IRQ_FORCED_THREADING,并且在引导内核的时候指定内核参数“ threadirqs”,那么所有软中断由软中断线程执行。
(1)中断处理程序执行软中断。
在中断处理程序的后半部分,调用函数 irq_exit()以退出中断上下文,处理软中断,其代码如下:
kernel/softirq.c
void irq_exit(void)
{
…
preempt_count_sub(HARDIRQ_OFFSET);
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
…
}
如果 in_interrupt()为真,表示在不可屏蔽中断、硬中断或软中断上下文,或者禁止软中断。
这里我们提一下本地中断的开启与关闭,当中断上来时,为了防止中断的嵌套,硬件会自动关闭本地中断,那本地中断什么时候打开呢?分两种情况:
1. 退出中断上下文时若有待处理的软中断,在执行软中断前__do_softirq->local_irq_enable会打开本地中断,即软中可以被硬件中断的打断。
2. 没有要处理的软中断,那么在中断完全退出时,会恢复被中断进程的寄存器上下文,系统状态寄存器一但被恢复,本地中断自然也就开了。
如果正在处理的硬中断没有抢占正在执行的软中断,没有禁止软中断,并且当前处理器的待处理软中断位图不是空的,那么调用函数 invoke_softirq()来处理软中断。
函数 invoke_softirq 的代码如下:
kernel/softirq.c
1 static inline void invoke_softirq(void)
2 {
3 if (ksoftirqd_running())
4 return;
5
6 if (!force_irqthreads) {
7 __do_softirq();
8 } else {
9 wakeup_softirqd();
10 }
11 }
第 3 行代码,如果软中断线程处于就绪状态或运行状态,那么让软中断线程执行软中断。
第 6 行和第 7 行代码,如果没有强制中断线程化,那么调用函数__do_softirq()执行软中断。
第 8 行和第 9 行代码,如果强制中断线程化,那么唤醒软中断线程执行软中断。
函数__do_softirq 是执行软中断的核心函数,其主要代码如下:
kernel/softirq.c
1 #define MAX_SOFTIRQ_TIME msecs_to_jiffies(2)
2 #define MAX_SOFTIRQ_RESTART 10
3 asmlinkage __visible void __softirq_entry __do_softirq(void)
4 {
5 unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
6 unsigned long old_flags = current->flags;
7 int max_restart = MAX_SOFTIRQ_RESTART;
8 struct softirq_action *h;
9 bool in_hardirq;
10 __u32 pending;
11 int softirq_bit;
12
13 …
14 pending = local_softirq_pending();
15 …
16 __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
17 …
18
19 restart:
20 set_softirq_pending(0);
21
22 local_irq_enable();
23
24 h = softirq_vec;
25
26 while ((softirq_bit = ffs(pending))) {
27 …
28 h += softirq_bit - 1;
29 …
30 h->action(h);
31 …
32 h++;
33 pending >>= softirq_bit;
34 }
35
36 …
37 local_irq_disable();
38
39 pending = local_softirq_pending();
40 if (pending) {
41 if (time_before(jiffies, end) && !need_resched() &&
42 --max_restart)
43 goto restart;
44
45 wakeup_softirqd();
46 }
47
48 …
49 __local_bh_enable(SOFTIRQ_OFFSET);
50 …
51}
第 14 行代码,把局部变量 pending 设置为当前处理器的待处理软中断位图。
第 16 行代码,把抢占计数器的软中断计数加 1。
第 20 行代码,把当前处理器的待处理软中断位图重新设置为 0。
第 22 行代码,开启硬中断。
第 26~34 行代码,从低位向高位扫描待处理软中断位图,针对每个设置了对应位的软中断编号,执行软中断的处理函数。
第 37 行代码,禁止硬中断。
第 40 行代码,如果软中断的处理函数又触发软中断,处理如下。
a) 第 41~43 行代码,如果软中断的执行时间小于 2 毫秒,不需要重新调度进程,并且软中断的执行次数没超过 10,那么跳转到第 19 行代码继续执行软中断。
b) 第 45 行代码,唤醒软中断线程执行软中断。
第 49 行代码,把抢占计数器的软中断计数减 1。
(2)软中断线程
每个处理器有一个软中断线程,名称是“ ksoftirqd/”后面跟着处理器编号,调度策略是 SCHED_NORMAL,优先级是 120。
软中断线程的核心函数是 run_ksoftirqd(),其代码如下:
kernel/softirq.c
static void run_ksoftirqd(unsigned int cpu)
{
local_irq_disable();
if (local_softirq_pending()) {
__do_softirq();
local_irq_enable();
…
return;
}
local_irq_enable();
}
(3)开启软中断时执行软中断。
当进程调用函数 local_bh_enable()开启软中断的时候,如果是开启最外层的软中断,并且当前处理器的待处理软中断位图不是空的,那么执行软中断。
local_bh_enable() -> __local_bh_enable_ip()
kernel/softirq.c
void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
{
…
preempt_count_sub(cnt - 1);
if (unlikely(!in_interrupt() && local_softirq_pending())) {
do_softirq();
}
preempt_count_dec();
…
}
抢占计数器
在介绍“禁止/开启软中断”之前,首先了解一下抢占计数器这个背景知识。
每个进程的 thread_info 结构体有一个抢占计数器: int preempt_count,它用来表示当前进程能不能被抢占。
抢占是指当进程在内核模式下运行的时候可以被其他进程抢占,如果优先级更高的进程处于就绪状态,强行剥夺当前进程的处理器使用权。
但是有时候进程可能在执行一些关键操作,不能被抢占,所以内核设计了抢占计数器。如果抢占计数器为 0,表示可以被抢占;如果抢占计数器不为 0,表示不能被抢占。
当中断处理程序返回的时候,如果进程在被打断的时候正在内核模式下执行,就会检查抢占计数器是否为 0。如果抢占计数器是 0,可以让优先级更高的进程抢占当前进程。
虽然抢占计数器不为 0 意味着禁止抢占,但是内核进一步按照各种场景对抢占计数器的位进行了划分,如下图所示。
其中第 0~7 位是抢占计数,第 8~15 位是软中断计数,第 16~19 位是硬中断计数,第 20 位是不可屏蔽中断( Non Maskable Interrupt, NMI)计数。
include/linux/preempt.h
/*
* PREEMPT_MASK: 0x000000ff
* SOFTIRQ_MASK: 0x0000ff00
* HARDIRQ_MASK: 0x000f0000
* NMI_MASK: 0x00100000
*/
#define PREEMPT_BITS 8
#define SOFTIRQ_BITS 8
#define HARDIRQ_BITS 4
#define NMI_BITS 1
各种场景分别利用各自的位禁止或开启抢占。
(1)普通场景( PREEMPT_MASK):对应函数 preempt_disable()和 preempt_enable()。
(2)软中断场景( SOFTIRQ_MASK):对应函数 local_bh_disable()和 local_bh_enable()。
(3)硬中断场景( HARDIRQ_MASK):对应函数 __irq_enter()和__irq_exit()。
(4)不可屏蔽中断场景( NMI_MASK):对应函数 nmi_enter()和 nmi_exit()。
反过来,我们可以通过抢占计数器的值判断当前处在什么场景:
include/linux/preempt.h
#define in_irq() (hardirq_count())
#define in_softirq() (softirq_count())
#define in_interrupt() (irq_count())
#define in_serving_softirq() (softirq_count() & SOFTIRQ_OFFSET)
#define in_nmi() (preempt_count() & NMI_MASK)
#define in_task() (!(preempt_count() & \
(NMI_MASK | HARDIRQ_MASK | SOFTIRQ_OFFSET)))
#define hardirq_count() (preempt_count() & HARDIRQ_MASK)
#define softirq_count() (preempt_count() & SOFTIRQ_MASK)
#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \
| NMI_MASK))
in_irq()表示硬中断场景,也就是正在执行硬中断。
in_softirq()表示软中断场景,包括禁止软中断和正在执行软中断。
in_interrupt()表示正在执行不可屏蔽中断、硬中断或软中断,或者禁止软中断。
in_serving_softirq()表示正在执行软中断。
in_nmi()表示不可屏蔽中断场景。
in_task()表示普通场景,也就是进程上下文。
禁止/开启软中断
如果进程和软中断可能访问同一个对象, 那么进程和软中断需要互斥, 进程需要禁止软中断。
禁止软中断的函数是 local_bh_disable(),注意:这个函数只能禁止本处理器的软中断,不能禁止其他处理器的软中断。该函数把抢占计数器的软中断计数加 2,其代码如下:
include/linux/bottom_half.h
static inline void local_bh_disable(void)
{
__local_bh_disable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET);
}
static __always_inline void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
{
preempt_count_add(cnt);
barrier();
}
include/linux/preempt.h
#define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET)
开启软中断的函数是 local_bh_enable(),该函数把抢占计数器的软中断计数减 2。
为什么禁止软中断的函数 local_bh_disable()把抢占计数器的软中断计数加 2, 而不是加1 呢?目的是区分禁止软中断和正在执行软中断这两种情况。执行软中断的函数__do_softirq()把抢占计数器的软中断计数加 1。如果软中断计数是奇数,可以确定正在执行软中断。
注意:local_bh_enable() 在硬中断或者关闭硬中断时使用有可能出现问题,会有警告提醒。
挂起的软中断
另一个跟软中断相关的字段是每个CPU都有一个32位掩码的字段
typedef struct {
unsigned int __softirq_pending;
unsigned int ipi_irqs[NR_IPI];
} ____cacheline_aligned irq_cpustat_t;
irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;
EXPORT_SYMBOL(irq_stat);
他描述挂起的软中断。每一位对应相应的软中断。比如0位代表HI_SOFTIRQ.一个注册软中断必须在被标记后才会执行。
宏local_softirq_pending();来获取该字段的值。
宏set_softirq_pending(0);来设置该字段的值。
使用函数raise_softirq()来激活软中断。即把响应的软中断号对应的__softirq_pending中的位置1。表示该软中断被挂起。如果当前CPU不在中断上下文中,唤醒内核线程ksoftirqd来检查被挂起的软中断,然后执行相应软中断处理函数。
内核在如下几个点上检查被挂起的软中断:
1、当do_IRQ() 完成硬中断处理时调用irq_exit()时调用do_softirq()来处理软中断。
2、当一个特殊内核线程ksoftirqd/n被唤醒时,处理软中断。
3、当调用local_bh_enable()函数激活本地CPU的软中断时。条件满足就调用do_softirq() 来处理软中断。
相关文章:

linux 内核软中断介绍
在介绍软中断之前,先来介绍一个概念:中断下半部: 为了避免处理复杂的中断嵌套,中断处理程序是在关闭中断的情况下执行的。可是,如果关闭中断的时间太长,可能导致中断请求丢失。例如周期时钟每隔 10 毫秒发送…...

软考:2024年软考高级:软件工程
软考:2024年软考高级: 提示:系列被面试官问的问题,我自己当时不会,所以下来自己复盘一下,认真学习和总结,以应对未来更多的可能性 关于互联网大厂的笔试面试,都是需要细心准备的 (1…...

Kubernetes(K8s)_15_CNI
Kubernetes(K8s)_15_CNI CNI网络模型UnderlayMAC VLANIP VLANDirect Route OverlayVXLAN CNI插件FlannelCalico CNI配置内置实现 CNI CNI(Container Network Interface): 实现容器网络连接的规范 Kubernetes将网络通信可分为: Pod内容器、Pod、Pod与Se…...

python 生成器的作用
1. 生成器 参考: https://www.cainiaojc.com/python/python-generator.html 1.1. 什么是生成器? 在 python 中,一边循环一边计算的机制,称为生成器:generator. 1.2. 生成器有什么优点? 1、节约内存。p…...

第十五届蓝桥杯(Web 应用开发)模拟赛 2 期-大学组(详细分析解答)
目录 1.相不相等 1.1 题目要求 1.2 题目分析 1.3 源代码 2.三行情书 2.1 题目要求 2.2 题目分析 2.3 源代码 3.电影院在线订票 3.1 题目要求 3.2 题目分析 3.3 源代码 4.老虎坤(不然违规发不出来) 4.1 题目要求 4.2 题目分析 4.3 源代码 …...

图解系列--HTTPS,认证
确保 Web 安全的HTTPS 1.HTTP 的缺点 1.1.通信使用明文可能会被窃听 加密处理防止被窃听 加密的对象可以有这么几个。 (1).通信的加密 HTTP 协议中没有加密机制,但可以通过和 SSL(Secure Socket Layer,安全套接层)或TLSÿ…...

element plus中表格的合计属性和例子
在 element plus 表格中,您可以使用 summary-method 属性来指定一个函数,计算表格中列的合计或平均值等。该函数应该返回一个对象,其中包含每个列的合计值。例如,如果您的表格数据是这样的: [{ name: John, age: 20, …...

计网Lesson1笔记
文章目录 几个简单概念计网的发展史阿帕网和RFCTCP/IP 协议互联网协议计网设计OSI 的七层架构TCP/IP 协议簇 几个简单概念 主机(host):指单个计算机,比如PC,或者其他电子设备。端系统(end system):指一块区域内的多个主机&#x…...

指针数组以及利用函数指针来实现简易计算器及typedef关键字(指针终篇)
文章目录 🚀前言🚀两段有趣的代码✈️typedef关键字 🚀指针数组🚀简易计算器的实现 🚀前言 基于阿辉前两篇博客指针的基础篇和进阶篇对于指针的了解,那么今天阿辉将为大家介绍C语言的指针剩下的部分&#…...

josef JZ-7Y-33静态中间继电器 电压DC220V 板前接线
系列型号: JZ-7Y-201X静态中间继电器;JZ-7J-201X静态中间继电器; JZ-7L-201X静态中间继电器;JZ-7D-201X静态中间继电器; JZ-7Y-201静态中间继电器;JZ-7J-201静态中间继电器; JZ-7L-201静态中…...

Java第二十章 ——多线程
本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 在这之前,首先让我们来了解下在操作系统中进程和线程的区别: 进程:每个进程都有独立的代码和数据空间(进程上下文…...

【超强笔记软件】Obsidian实现免费无限流量无套路云同步
【超强笔记软件】Obsidian如何实现免费无限流量无套路云同步? 目录 一、简介 软件特色演示: 二、使用免费群晖虚拟机搭建群晖Synology Drive服务,实现局域网同步 1 安装并设置Synology Drive套件 2 局域网内同步文件测试 三、内网穿透群…...

【Linux小项目】实现自己的bash
0. bash原理介绍 bash实际上就是一个负责解析输入字符串工具. 我们需要做的事是这些: 手动分割出输入的字符串判断哪些变量是内建命令(自己执行),哪些命令是普通命令(创建子进程执行)实现的功能有: echo export cd 常规指令 输入、输出流重定向 #include<stdio.h> #i…...

客户案例:EDLP助力金融行业打造高效数据防泄露体系
客户背景 某金融机构是一家以金融科技为核心,致力于为客户提供全方位、智能化、便捷化金融服务的综合性企业。公司总部位于南京,业务范围覆盖全国,拥有强大的技术研发团队和优秀的业务精英,为客户提供全方位的金融服务解决方案。 …...

【JavaFX漏扫开发基础】stage窗口/模式/模态
文章目录 stage一、stage窗口二、stage窗口,模式,模态stage模式(5种样式)模态化窗口stage stage其实就是一个窗口,它啥也不是,打开所有windows的程序都会有一个窗口,这个窗口就是javafx里的stage。里面的内容不属于stage,stage就是一个窗口,就这么简单。 Stage is a…...

MySQL进阶知识:锁
目录 前言 全局锁 表级锁 表锁 元数据锁(MDL) 意向锁 行级锁 行锁 行锁演示 间隙锁/临界锁 演示 前言 MySQL中的锁,按照锁的粒度分,分为以下三类 全局锁:锁定数据库中的所有表。表级锁:每次操…...

linux下的工具---gdb
一、gdb简介 GDB,是The GNU Project Debugger 的缩写,是 Linux 下功能全面的调试工具。 GDB支持断点、单步执行、打印变量、观察变量、查看寄存器、查看堆栈等调试手段。 程序的发布方式有两种,debug模式和release模式 Linux gcc/g出来的二进制程序&am…...

ESP32-Web-Server编程-JS 基础 2
ESP32-Web-Server编程-JS 基础 2 概述 上节介绍了 JS 编程的基础。如前所述,在 HTML 中,可以通过下述 两种方式使用 JS 程序: 直接在 HTML 文件中通过 script 标签中嵌入 JavaScript 代码。通过 src 元素引入外部的 JavaScript 文件。 在…...

Java Web基础教程
Java Web基础教程 1. Servlet基础 1.1 什么是Servlet Servlet是JavaEE中的标准组件之一,专门用于处理客户端的HTTP请求。并且它必须依赖于Servlet容器(Tomcat就是一个标准的Servlet容器)才能运行。因为Servlet实例的创建和销毁都是由容器负…...

BUUCTF john-in-the-middle 1
BUUCTF:https://buuoj.cn/challenges 题目描述: 注意:得到的 flag 请包上 flag{} 提交 密文: 下载附件,解压得到john-in-the-middle.pcap文件。 解题思路: 1、双击文件,打开wireshark。 看到很多http流…...

HashMap的死循环及数据覆盖问题
目录 一,HashMap 线程不安全的原因 二,HashMap 死循环问题 死循环发生的条件 死循环的具体过程 死循环执行步骤1 死循环执行步骤2 死循环执行步骤3 三,HashMap 数据覆盖问题 数据覆盖执行流程1 数据覆盖执行流程2 数据覆盖执行流…...

数据库数据恢复—MongoDB数据库文件拷贝出现错误的数据恢复案例
MongoDB数据库数据恢复环境: 一台Windows Server操作系统的虚拟机,虚拟机上部署有MongoDB数据库。 MongoDB数据库故障&检测: 在未关闭MongoDB服务的情况下,工作人员将MongoDB数据库文件拷贝到其他分区,然后将原数…...

2023年11月个人工作生活总结
本文为 2023 年 11 月工作生活总结。 研发编码 GIS 模仿了一些有名的地图服务商的网站,将离线地图页面做成全屏,对于大屏幕更加好友。再美化一下全区的边界和区内地域的边界。不过主要工作量还是绘制路线,而绘线作为内部工作,还…...

Spark-06:Spark 共享变量
目录 1.广播变量(broadcast variables) 2.累加器(accumulators) 在分布式计算中,当在集群的多个节点上并行运行函数时,默认情况下,每个任务都会获得函数中使用到的变量的一个副本。如果变量很…...

Spring整合web环境
目录 Javaweb三大组件及环境特点 Spring整合web环境的思路及实现 Spring的web开发组件spring-web MVC框架思想及其设计思路 Javaweb三大组件及环境特点 Spring整合web环境的思路及实现 package com.xfy.listener;import com.xfy.config.SpringConfig; import org.springfra…...

分享从零开始学习网络设备配置--任务4.3 使用动态路由RIPng实现网络连通
任务描述 某公司使用IPv6技术搭建企业网络,由于静态路由需要管理员手工配置,在网络拓扑发生变化时,也不会自动生成新的路由,因此采用IPv6动态路由协议RIPng实现网络连通,实现任意两个节点之间的通信,并降低…...

vue2.0+elementui集成file-loader之后图标失效问题
背景 跑vue2elementUI项目时,由于前端这边需要在本地存放xlsx模板文件,供用户下载模板文件,所以需要在webpack构建的时候增加file-loader进行解析xlsx文件打包。 vue版本2.x element-ui 版本 2.13.x 注意 npm i -D file-loader版本号给vue项…...

C# 文件帮助类(FileHelper)
引言 在研究程序反射的时候我们往往需要获取当前运行程序所引用的dll文件,按照传统的方式我们可以维护一个这样的列表,但是这样维护成本实在是太高,而且不利于团队合作开发,在高版本的.net 4.6.2之后官方出了专门的dll帮我们做这个事情Microsoft.Extensions.DependencyMod…...

WordPress 外链跳转插件
WordPress 外链跳转插件是本站开发的一款WordPress插件,能对文中外链添加一层过滤,有效防止追踪,以及提醒用户。 类似于知乎、CSDN打开其他链接的提示。 后台可以设置白名单 学习资料源代码:百度网盘 密码:123...

算法的10大排序
10大排序算法--python 一颗星--选择排序一颗星--冒泡排序一颗星--插入排序两颗星--归并排序(递归-难)三颗星--桶排序三颗星--计数排序四颗星--基数排序四颗星--快速排序,寻找标志位(递归-难)四颗星--又是比较难的希尔排…...