리눅스 커널에서는 로컬 인터럽트(local interrupt)를 활성화하거나 비활성화할 때 사용하는 API 함수들이 있습니다:
- local_irq_enable()
- local_irq_disable()
코드 분석: preempt_schedule_irq() 함수
이제 preempt_schedule_irq() 함수의 일부 코드를 살펴보겠습니다:
asmlinkage __visible void __sched preempt_schedule_irq(void)
{
enum ctx_state prev_state;
/* Catch callers which need to be fixed */
BUG_ON(preempt_count() || !irqs_disabled());
prev_state = exception_enter();
do {
preempt_disable();
local_irq_enable();
__schedule(SM_PREEMPT);
local_irq_disable();
sched_preempt_enable_no_resched();
} while (need_resched());
exception_exit(prev_state);
}
이 코드에서는 __schedule() 함수가 실행되는 동안 local_irq_enable() 함수가 호출되어 로컬 인터럽트를 활성화합니다.
그 후, __schedule() 함수가 종료되면 다시 local_irq_disable() 함수가 호출되어 로컬 인터럽트를 비활성화합니다.
디스어셈블리(Disassembly) 결과 분석
이제 이 코드를 RISC-V 기반 컴파일러로 빌드한 다음, 디스어셈블리(disassembly) 결과를 확인해 보겠습니다:
SP:FFFFFFFF80D8E526|preempt_schedule_irq: lw a5,0x8(tp) ; a5,8(tp)
SP:FFFFFFFF80D8E52A| c.bnez a5,0xFFFFFFFF80D8E55E
[...]
SP:FFFFFFFF80D8E53C| csrsi sstatus,0x2
SP:FFFFFFFF80D8E540| c.li a0,0x1
SP:FFFFFFFF80D8E542| auipc ra,0xFFFFF ; ra,1048575
SP:FFFFFFFF80D8E546| jalr ra,0x516(ra) ; ra,1302(ra) ; __schedule
SP:FFFFFFFF80D8E54A| csrci sstatus,0x2
이 코드를 보면 csrsi sstatus,0x2 명령어와 csrci sstatus,0x2 명령어가 사용됩니다.
이 명령어들은 각각 sstatus 레지스터의 2번째 비트를 설정(1)하거나 해제(0)하는 역할을 수행합니다.
local_irq_enable() 함수의 내부 구현
local_irq_enable() 함수는 아키텍처에 의존적인 함수입니다. 코드를 단계별로 분석하면서 함수 내부 동작을 파악해 보겠습니다.
먼저 local_irq_enable() 함수의 구현부를 보겠습니다:
// include/linux/irqflags.h
#define local_irq_enable() do { raw_local_irq_enable(); } while (0)
이 함수는 raw_local_irq_enable() 함수로 치환됩니다.
raw_local_irq_enable() 함수를 봅시다:
// include/linux/irqflags.h
#define raw_local_irq_enable() arch_local_irq_enable()
이제 arch_local_irq_enable() 함수로 치환됩니다. 리눅스 커널에서는 매크로를 사용해 특정 함수가 아키텍처에 맞춰 동작하도록 하는 방식이 자주 사용됩니다.
arch_local_irq_enable() 함수의 구현부를 보겠습니다.
// arch/riscv/include/asm/irqflags.h
static inline void arch_local_irq_enable(void)
{
csr_set(CSR_STATUS, SR_IE);
}
이 함수는 RISC-V 아키텍처에 맞춰 구현된 함수입니다. 여기서 SSTATUS 레지스터의 SR_IE 비트를 1로 설정하는 동작입니다.
SR_IE 매크로와 SR_SIE 매크로 정의를 알아 봅시다.
// arch/riscv/include/asm/csr.h
#define SR_IE SR_SIE
[...]
#define SR_SIE _AC(0x00000002, UL) /* Supervisor Interrupt Enable */
여기서 SR_IE는 0x2 값과 동일합니다. 이는 csrsi sstatus,0x2 명령어가 sstatus 레지스터의 2번째 비트를 설정한다는 의미입니다.
분석 결과 요약
분석한 내용을 정리하면 다음과 같습니다:
- local_irq_enable() 함수는 로컬 인터럽트를 활성화하는 역할을 합니다.
- RISC-V 아키텍처 기반에서는 csrsi sstatus,0x2 명령어가 해당 함수의 핵심 동작입니다.
- 이 명령어는 sstatus 레지스터의 2번째 비트를 1로 설정합니다.
하드웨어 디버깅: sstatus 레지스터 확인
아래는 하드웨어 디버깅 과정에서 확인한 결과입니다.
csrsi sstatus,0x2 명령어 실행 후 결과:
sstatus 레지스터 값이 0x2로 변경되었습니다.
csrci sstatus,0x2 명령어 실행 후 결과:
sstatus 레지스터 값이 0x0으로 변경되었습니다.
디버깅 결과로, 이제 local_irq_enable() 함수의 동작 원리와 sstatus 레지스터의 역할에 대해 명확히 이해했을 것입니다.
이를 통해 로컬 인터럽트를 활성화하고 비활성화하는 과정이 소프트웨어와 하드웨어 레벨에서 어떻게 연동되는지도 알 수 있습니다.
'RISC-V > 인터럽트 콘트롤러' 카테고리의 다른 글
[RISC-V] 인터럽트 콘트롤러(Interrupt Controller)의 구조 - PLIC, CLIC (0) | 2025.03.14 |
---|