Linux 인터럽트(IRQ/softirq) 기본: 원리 및 커널 구현

인터럽트(IRQ), 특히 Softirq의 중요한 애플리케이션 시나리오는 네트워크에서 패킷을 보내고 받는 것이지만 이것이 유일한 애플리케이션 시나리오는 아닙니다. 이 글은 IRQ/softirq에 대한 일반적인 기본 지식을 정리한 것입니다. 이 글의 목적은 네트워크 패킷 송수신에 대한 이해를 돕기 위한 것이지만, 네트워크 패킷 송수신과 직접적인 관련은 없습니다.
방해란 무엇인가요?
CPU는 하드웨어 작업(예: 디스크 읽기 및 쓰기, 키보드 입력) 및 소프트웨어 작업(예: 네트워크 패킷 처리)을 포함하여 시분할 다중화를 통해 여러 작업을 처리합니다. 언제든지 CPU는 하나의 작업만 처리할 수 있습니다. 현재 하드웨어나 소프트웨어 작업이 실행되고 있지 않지만 CPU에 의해 즉시 처리될 것으로 예상되는 경우 CPU가 현재 작업을 일시 중지하고 작업을 먼저 처리하기를 바라며 CPU에 인터럽트 요청을 보냅니다. . 인터럽트는 이벤트 형태로 CPU에 알리므로 "XX 조건에서 XX 인터럽트 이벤트가 발생합니다"라는 설명을 자주 볼 수 있습니다.
두 가지 유형:
- 키보드 키 누르기와 같은 외부 또는 하드웨어 생성 인터럽트.
- 소프트웨어에 의해 생성된 인터럽트, 0으로 나누기와 같은 비정상적인 이벤트에 의해 생성된 인터럽트입니다.
인터럽트를 관리하는 장치: APIC(Advanced Programmable Interrupt Controller).
하드 인터럽트
중단 처리 프로세스
중단은 언제든지 발생할 수 있으며 발생 후 즉시 처리해야 합니다. 인터럽트 이벤트 수신 후 처리 흐름:
- 현재 작업 선점: 커널은 실행 프로세스를 일시 중지해야 합니다.
- 인터럽트 처리 기능 실행: 해당 인터럽트 처리 기능을 찾아서 CPU를 넘겨줍니다(실행). 인터럽트 처리가 완료된 후
- : 1단계에서 선점된 프로세스가 실행을 재개합니다.
마스킹 가능한 인터럽트는 x64_64에서
두 가지 명령어를 사용하여 마스킹(닫기)하고 복원할 수 있습니다. sti/cli
으아악
마스킹 기간 동안 이러한 유형의 인터럽트는 더 이상 새로운 인터럽트 이벤트를 트리거하지 않습니다. 대부분의 IRQ는 이러한 유형입니다. 예: 패킷을 보내고 받는 네트워크 카드의 하드웨어가 중단되었습니다.
마스크 불가능 인터럽트는 마스킹할 수 없으므로 사실상 더 긴급합니다.
문제: 충분히 빠른 실행과 복잡한 논리IRQ 처리기의 두 가지 기능:
실행은 매우 빨라야 합니다. 그렇지 않으면 이벤트(및 데이터)가 손실됩니다.-
해야 할 일이 많을 수 있으며, 패키지 수신과 같은 로직도 매우 복잡합니다
- 여기에는 본질적인 모순이 있습니다.
전통적으로 이 본질적인 모순을 해결하는 방법은 인터럽트 처리를 두 부분으로 나누는 것입니다.
상반기-
하반신
- 이러한 접근 방식을 중단의 연기 처리 또는 지연 처리 라고 합니다. 예전에는 연기하는 유일한 방법이었지만 더 이상은 아닙니다. 이제 이는 인터럽트 처리를 연기하는 다양한 방법을 나타내는 일반적인 용어입니다. 이러한 방식으로 인터럽트는 두 부분으로 나뉩니다.
- 第一部分:只进行最重要、必须得在硬中断上下文中执行的部分;剩下的处理作为第二部分,放入一个待处理队列;
- 第二部分:一般是调度器根据轻重缓急来调度执行,不在硬中断上下文中执行。
Linux 中的三种推迟中断(deferred interrupts):
- softirq
- tasklet
- workqueue
后面会具体介绍。
软中断
软中断子系统
软中断是一个内核子系统:
1、每个 CPU 上会初始化一个 ksoftirqd
内核线程,负责处理各种类型的 softirq 中断事件;
用 cgroup ls 或者 ps -ef
都能看到:
$ systemd-cgls -k | grep softirq # -k: include kernel threads in the output ├─ 12 [ksoftirqd/0] ├─ 19 [ksoftirqd/1] ├─ 24 [ksoftirqd/2] ...
2、软中断事件的 handler 提前注册到 softirq 子系统, 注册方式 open_softirq(softirq_id, handler)
例如,注册网卡收发包(RX/TX)软中断处理函数:
// net/core/dev.c open_softirq(NET_TX_SOFTIRQ, net_tx_action); open_softirq(NET_RX_SOFTIRQ, net_rx_action);
3、软中断占 CPU 的总开销:可以用 top
查看,里面 si
字段就是系统的软中断开销(第三行倒数第二个指标):
$ top -n1 | head -n3 top - 18:14:05 up 86 days, 23:45, 2 users, load average: 5.01, 5.56, 6.26 Tasks: 969 total, 2 running, 733 sleeping, 0 stopped, 2 zombie %Cpu(s): 13.9 us, 3.2 sy, 0.0 ni, 82.7 id, 0.0 wa, 0.0 hi, 0.1 si, 0.0 st
主处理
smpboot.c 类似于一个事件驱动的循环,里面会调度到 ksoftirqd
线程,执行 pending 的软中断。ksoftirqd
里面会进一步调用到 __do_softirq
,
- 判断哪些 softirq 需要处理,
- 执行 softirq handler
避免软中断占用过多 CPU
软中断方式的潜在影响:推迟执行部分(比如 softirq)可能会占用较长的时间,在这个时间段内, 用户空间线程只能等待。反映在 top
里面,就是 si
占比。
不过 softirq 调度循环对此也有改进,通过 budget 机制来避免 softirq 占用过久的 CPU 时间。
unsigned long end = jiffies + MAX_SOFTIRQ_TIME; ... restart: while ((softirq_bit = ffs(pending))) { ... h->action(h); // 这里面其实也有机制,避免 softirq 占用太多 CPU ... } ... pending = local_softirq_pending(); if (pending) { if (time_before(jiffies, end) && !need_resched() && --max_restart) // 避免 softirq 占用太多 CPU goto restart; } ...
硬中断 -> 软中断 调用栈
前面提到,softirq 是一种推迟中断处理机制,将 IRQ 的大部分处理逻辑推迟到了这里执行。两条路径都会执行到 softirq 主处理逻辑 __do_softirq()
,
1、CPU 调度到 ksoftirqd
线程时,会执行到 __do_softirq()
;
2、每次 IRQ handler 退出时:do_IRQ() -> ...
。
do_IRQ()
是内核中最主要的 IRQ 处理方式。它执行结束时,会调用 exiting_irq()
,这会展开成 irq_exit()
。后者会检查是pending 的 softirq,有的话就唤醒:
// arch/x86/kernel/irq.c if (!in_interrupt() && local_softirq_pending()) invoke_softirq();
进而会使 CPU 执行到 __do_softirq()
。
软中断触发执行的步骤
To summarize, each softirq goes through the following stages: 每个软中断会经过下面几个阶段:
-
通过
open_softirq()
注册软中断处理函数; -
通过
raise_softirq()
将一个软中断标记为 deferred interrupt,这会唤醒改软中断(但还没有开始处理); -
内核调度器调度到
ksoftirqd
内核线程时,会将所有等待处理的 deferred interrupt(也就是 softirq)拿出来,执行对应的处理方法(softirq handler);
以收包软中断为例, IRQ handler 并不执行 NAPI,只是触发它,在里面会执行到 raise NET_RX_SOFTIRQ;真正的执行在 softirq,里面会调用网卡的 poll() 方法收包。IRQ handler 中会调用 napi_schedule(),然后启动 NAPI poll(),
这里需要注意,虽然 IRQ handler 做的事情非常少,但是接下来处理这个包的 softirq 和 IRQ 在同一个 CPU 运行。这就是说,如果大量的包都放到了同一个 RX queue,那虽然 IRQ 的开销可能并不多,但这个 CPU 仍然会非常繁忙,都花在 softirq 上了。解决方式:RPS。它并不会降低延迟,只是将包重新分发:RXQ -> CPU。
三种推迟执行方式(softirq/tasklet/workqueue)
前面提到,Linux 中的三种推迟中断执行的方式:
- softirq
- tasklet
- workqueue
其中,
- softirq 和 tasklet 依赖软中断子系统,运行在软中断上下文中;
- workqueue 不依赖软中断子系统,运行在进程上下文中。
softirq
前面已经看到, Linux 在每个 CPU 上会创建一个 ksoftirqd 内核线程。
softirqs 是在 Linux 内核编译时就确定好的,例外网络收包对应的 NET_RX_SOFTIRQ
软中断。因此是一种静态机制。如果想加一种新 softirq 类型,就需要修改并重新编译内核。
内部组织
在内部是用一个数组(或称向量)来管理的,每个软中断号对应一个 softirq handler。数组和注册:
// kernel/softirq.c // NR_SOFTIRQS 是 enum softirq type 的最大值,在 5.10 中是 10,见下面 static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp; void open_softirq(int nr, void (*action)(struct softirq_action *)) { softirq_vec[nr].action = action; }
5.10 中所有类型的 softirq:
// include/linux/interrupt.h enum { HI_SOFTIRQ=0, // tasklet TIMER_SOFTIRQ, // timer NET_TX_SOFTIRQ, // networking NET_RX_SOFTIRQ, // networking BLOCK_SOFTIRQ, // IO IRQ_POLL_SOFTIRQ, TASKLET_SOFTIRQ, // tasklet SCHED_SOFTIRQ, // schedule HRTIMER_SOFTIRQ, // timer RCU_SOFTIRQ, // lock NR_SOFTIRQS };
也就是在 cat /proc/softirqs
看到的哪些。
$ cat /proc/softirqs CPU0 CPU1 ... CPU46 CPU47 HI: 2 0 ... 0 1 TIMER: 443727 467971 ... 313696 270110 NET_TX: 57919 65998 ... 42287 54840 NET_RX: 28728 5262341 ... 81106 55244 BLOCK: 261 1564 ... 268986 463918 IRQ_POLL: 0 0 ... 0 0 TASKLET: 98 207 ... 129 122 SCHED: 1854427 1124268 ... 5154804 5332269 HRTIMER: 12224 68926 ... 25497 24272 RCU: 1469356 972856 ... 5961737 5917455
触发(唤醒)softirq
void raise_softirq(unsigned int nr) { local_irq_save(flags); // 关闭 IRQ raise_softirq_irqoff(nr); // 唤醒 ksoftirqd 线程(但执行不在这里,在 ksoftirqd 线程中) local_irq_restore(flags); // 打开 IRQ } if (!in_interrupt()) wakeup_softirqd(); static void wakeup_softirqd(void) { struct task_struct *tsk = __this_cpu_read(ksoftirqd); if (tsk && tsk->state != TASK_RUNNING) wake_up_process(tsk); }
以收包软中断为例, IRQ handler 并不执行 NAPI,只是触发它,在里面会执行到 raise NET_RX_SOFTIRQ;真正的执行在 softirq,里面会调用网卡的 poll() 方法收包。IRQ handler 中会调用 napi_schedule(),然后启动 NAPI poll()。
tasklet
如果对内核源码有一定了解就会发现,softirq 用到的地方非常少,原因之一就是上面提到的,它是静态编译的, 靠内置的 ksoftirqd 线程来调度内置的那 9 种 softirq。如果想新加一种,就得修改并重新编译内核, 所以开发成本非常高。
实际上,实现推迟执行的更常用方式 tasklet。它构建在 softirq 机制之上, 具体来说就是使用了上面提到的两种 softirq:
-
HI_SOFTIRQ
-
TASKLET_SOFTIRQ
换句话说,tasklet 是可以在运行时(runtime)创建和初始化的 softirq,
void __init softirq_init(void) { for_each_possible_cpu(cpu) { per_cpu(tasklet_vec, cpu).tail = &per_cpu(tasklet_vec, cpu).head; per_cpu(tasklet_hi_vec, cpu).tail = &per_cpu(tasklet_hi_vec, cpu).head; } open_softirq(TASKLET_SOFTIRQ, tasklet_action); open_softirq(HI_SOFTIRQ, tasklet_hi_action); }
内核软中断子系统初始化了两个 per-cpu 变量:
- tasklet_vec:普通 tasklet,回调 tasklet_action()
- tasklet_hi_vec:高优先级 tasklet,回调 tasklet_hi_action()
struct tasklet_struct { struct tasklet_struct *next; unsigned long state; atomic_t count; void (*func)(unsigned long); unsigned long data; };
tasklet 再执行针对 list 的循环:
static void tasklet_action(struct softirq_action *a) { local_irq_disable(); list = __this_cpu_read(tasklet_vec.head); __this_cpu_write(tasklet_vec.head, NULL); __this_cpu_write(tasklet_vec.tail, this_cpu_ptr(&tasklet_vec.head)); local_irq_enable(); while (list) { if (tasklet_trylock(t)) { t->func(t->data); tasklet_unlock(t); } ... } }
tasklet 在内核中的使用非常广泛。不过,后面又出现了第三种方式:workqueue。
workqueue
这也是一种推迟执行机制,与 tasklet 有点类似,但也有很大不同。
- tasklet 是运行在 softirq 上下文中;
- workqueue 运行在内核进程上下文中;这意味着 wq 不能像 tasklet 那样是原子的;
- tasklet 永远运行在指定 CPU,这是初始化时就确定了的;
- workqueue 默认行为也是这样,但是可以通过配置修改这种行为。
使用场景
// Documentation/core-api/workqueue.rst: There are many cases where an asynchronous process execution context is needed and the workqueue (wq) API is the most commonly used mechanism for such cases. When such an asynchronous execution context is needed, a work item describing which function to execute is put on a queue. An independent thread serves as the asynchronous execution context. The queue is called workqueue and the thread is called worker. While there are work items on the workqueue the worker executes the functions associated with the work items one after the other. When there is no work item left on the workqueue the worker becomes idle. When a new work item gets queued, the worker begins executing again.
简单来说,workqueue 子系统提供了一个接口,通过这个接口可以创建内核线程来处理从其他地方 enqueue 过来的任务。这些内核线程就称为 worker threads,内置的 per-cpu worker threads:
$ systemd-cgls -k | grep kworker ├─ 5 [kworker/0:0H] ├─ 15 [kworker/1:0H] ├─ 20 [kworker/2:0H] ├─ 25 [kworker/3:0H]
结构体
// include/linux/workqueue.h struct worker_pool { spinlock_t lock; int cpu; int node; int id; unsigned int flags; struct list_head worklist; int nr_workers; ... struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; struct lockdep_map lockdep_map; };
kworker 线程调度 workqueues,原理与 ksoftirqd 线程调度 softirqs 一样。但是我们可以为 workqueue 创建新的线程,而 softirq 则不行。
参考资料
- Linux Inside (online book), Interrupts and Interrupt Handling[1]
引用链接
[1]Interrupts and Interrupt Handling: https://0xax.gitbooks.io/linux-insides/content/Interrupts/linux-interrupts-9.html
위 내용은 Linux 인터럽트(IRQ/softirq) 기본: 원리 및 커널 구현의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

Linux 시스템의 5 가지 기본 구성 요소는 다음과 같습니다. 1. Kernel, 2. System Library, 3. System Utilities, 4. 그래픽 사용자 인터페이스, 5. 응용 프로그램. 커널은 하드웨어 리소스를 관리하고 시스템 라이브러리는 사전 컴파일 된 기능을 제공하며 시스템 유틸리티는 시스템 관리에 사용되며 GUI는 시각적 상호 작용을 제공하며 응용 프로그램은 이러한 구성 요소를 사용하여 기능을 구현합니다.

git 저장소 주소를 보려면 다음 단계를 수행하십시오. 1. 명령 줄을 열고 리포지토리 디렉토리로 이동하십시오. 2. "git remote -v"명령을 실행하십시오. 3. 출력 및 해당 주소에서 저장소 이름을 봅니다.

메모장은 Java 코드를 직접 실행할 수는 없지만 다른 도구를 사용하여 명령 줄 컴파일러 (Javac)를 사용하여 Bytecode 파일 (filename.class)을 생성하면 달성 할 수 있습니다. Java Interpreter (Java)를 사용하여 바이트 코드를 해석하고 코드를 실행하고 결과를 출력하십시오.

Sublime에서 코드를 실행하는 6 가지 방법이 있습니다. 핫키, 메뉴, 빌드 시스템, 명령 줄, 기본 빌드 시스템 설정 및 사용자 정의 빌드 명령, 프로젝트/파일을 마우스 오른쪽 단추로 클릭하여 개별 파일/프로젝트를 실행합니다. 빌드 시스템 가용성은 숭고한 텍스트 설치에 따라 다릅니다.

Laravel을 설치하려면 다음 단계를 순서대로 수행하십시오. Composer 설치 (MacOS/Linux 및 Windows) 설치 LARAVEL 설치 프로그램 새 프로젝트 시작 서비스 액세스 애플리케이션 (URL : http://127.0.1:8000) 데이터베이스 연결 (필요한 경우)을 설정하십시오.

Linux의 주요 용도에는 다음이 포함됩니다. 1. 서버 운영 체제, 2. 임베디드 시스템, 3. 데스크탑 운영 체제, 4. 개발 및 테스트 환경. Linux는이 분야에서 뛰어나 안정성, 보안 및 효율적인 개발 도구를 제공합니다.

GIT 소프트웨어 설치 단계는 다음 단계가 포함됩니다. 설치 패키지를 다운로드하고 설치 패키지를 실행하여 설치 구성 GIT 설치 GIT BASH (Windows 만)를 확인하십시오.

Visual Studio Code (VSCODE)는 Microsoft가 개발 한 크로스 플랫폼, 오픈 소스 및 무료 코드 편집기입니다. 광범위한 프로그래밍 언어에 대한 가볍고 확장 성 및 지원으로 유명합니다. VSCODE를 설치하려면 공식 웹 사이트를 방문하여 설치 프로그램을 다운로드하고 실행하십시오. VScode를 사용하는 경우 새 프로젝트를 만들고 코드 편집, 디버그 코드, 프로젝트 탐색, VSCODE 확장 및 설정을 관리 할 수 있습니다. VSCODE는 Windows, MacOS 및 Linux에서 사용할 수 있으며 여러 프로그래밍 언어를 지원하며 Marketplace를 통해 다양한 확장을 제공합니다. 이점은 경량, 확장 성, 광범위한 언어 지원, 풍부한 기능 및 버전이 포함됩니다.
