【精通内核】Linux内核中断控制原理

511 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情

前言

📫作者简介小明java问道之路,专注于研究 Java/ Liunx内核/ C++及汇编/计算机底层原理/源码,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。

📫热衷分享,喜欢原创~ 关注我会给你带来一些不一样的认知和成长

🏆InfoQ签约作者、CSDN专家博主/后端领域优质创作者/内容合伙人、阿里云专家/签约博主、51CTO专家🏆

🔥如果此文还不错的话,还请👍关注 、点赞 、收藏三连支持👍一下博主~


本文导读

CPU 对于任务切换是通过时钟中断来控制的,只要我们将中断屏蔽,就可以保证在当前CPU中的所有操作都不会被中断,从而保证了原子性。在单核CPU上, 通过操作 EFLAGS 寄存器相当于保存EFLAGS表示中断

一、Linux内核中断控制

如果是在单核CPU上,情况又当如何在中断程序中也要获取自旋锁,那么可能造成和应用程序自旋锁相互争用,从而造成死锁。然而,能否将本地的中断禁止,以完成在单核CPU上的原子性操作,当我们完成操作后再打开中断。

同时,如果进程在内核中工作,当有高优先级的任务需要被紧急处理时,那么能否在内核态中对其进行抢占呢?

本节将详细描述Linux 内核中断控制与内核抢占原理。下面看看实现原理。

二、中断原理

从前述理论得知,CPU 对于任务切换是通过时钟中断来控制的,只要我们将中断屏蔽,就可以保证在当前CPU中的所有操作都不会被中断,从而保证了原子性。

可能有读者会想,既然有了关中断,为什么还需要信号量、互斥量、自旋锁?

这很简单,在 SMP 即对称多处理器架构中,多个CPU 共享内存。由于不可以将全部CPU的中断都关闭,因此为了支撑 SMP架构,除了本地中断被关闭之外,还可以通过自旋锁来保证任务操作原子性。

上前述内容在讲解理论时,已经介绍了关中断和开中断的汇编指令 CLI、STI。现在我们来看看所有与中断相关的操作。

三、Linux内核源码解析

首先将 EFLAGS 寄存器入栈,然后弹出放入所传入的x变量中,相当于保存EFLAGS;将传入的x变量入栈,然后弹出放入EFLAGS寄存器中。

相当于恢复EFLAGS到中,禁止本地CPU中断请求,打开本地CPU中断请求

判断是否已经禁止了中断请求。可以看到,这里取 EFLAGS的第九位,看看是否为0,保存本地EFLAGS且禁止中断。这里与local save flags不同之处在于,最后的cli,相当于保存了 EFLAGS,且禁止本地CPU中断,local_save flags只保存了EFL AGS并没有禁止中断

// 将 EFLAGS 寄存器入栈,然后弹出放入所传入的x变量中
// 相当于保存EFLAGS
#define local_save_flags(x)	
do {
    typecheck(unsigned longx);
    _asm__volatile_("pushfl;popl %O":"=g	(x): /* no input */); 
} while (0)

// 将传入的x变量入栈,然后弹出放入EFLAGS寄存器中。相当于恢复EFLAGS到中
#define local irq_restore(x)	
do {
    typecheck(unsigned long,x);
    _asm__volatile__("push %O;popfl": /* no output */ : "g" (x): "memory", "cc"); 
} while (0)

//禁止本地CPU中断请求
#define local irq_disable() _asm__volatile("cli":::"memory")	

// 打开本地CPU 中断请求
#define local irq_enable() _asm__volatile_("sti":::"memory")

// 判断是否已经禁止了中断请求。可以看到,这里取 EFLAGS的第九位,看看是否为0
#define irqs_disabled()({
    unsigned long flags; 

    // //保存本地EFLAGS且禁止中断。这里与local save flags不同之处在于,最后的cli,相当于保存了 EFLAGS
    local_save_flags(flags);
    !(flags & (1<<9));
})

// 且禁止本地CPU中断,local_save flags只保存了EFL AGS并没有禁止中断
#define local_irq_save(x) __asm__volatile ("pushfl; popl %0;cli": "=g" (x): /* no input */ : "memory")