정해진 코드
를 실행하는, 인터럽트 벡터와 인터럽트 핸들러를 뜻한다. 소프트웨어적으로 처리하는 이러한 정해진 코드
를 인터럽트 서비스 루틴(Interrupt Service Routine, ISR)
이라 부른다. CPU 아키텍처 별로 인터럽트를 처리하는 방식이 서로 상이한데, ARM
에서는 이러한 인터럽트를 익셉션(Exception)
의 한 종류로 처리한다. ARM 프로세서는 외부 하드웨어 입력이나 오류 이벤트가 발생하면 익셉션 모드로 진입하게 되고 익셉션 종류에 따라 정해진 주소
로 분기(branch)한다.
이렇게 정해진 주소
의 코드를 익셉션 벡터(Exception Vector)
라 부른다.
이러한 인터럽트를 처리할 때 가장 중요한 포인트는 바로, 인터럽트 핸들러는 빨리 실행 되어야 한다는 것이다. 그 이유는 인터럽트가 발생하게 되면 실행 중인 코드가 멈추기 때문이다.
인터럽트가 발생하면 이를 처리하기 위한 함수가 호출되는데 이를 인터럽트 핸들러
라고 부른다. 인터럽트 핸들러는 함수의 형태로 구현되고, 커널 내부의 IRQ (Interrupt ReQuest)
서브 시스템을 통해 호출된다. 이렇게 특정한 이벤트에 동작할 인터럽트 핸들러는 request_irq()
함수로 등록할 수 있다.
필자가 분석하고 있는 drivers/net/ethernet/dlink/dl2k.c
의 코드이다. 이는 rio_open()
함수가 호출 되었을 때 request_irq()
함수를 호출하여 rio_interrupt()
함수를 인터럽트 핸들러로 등록하는 코드이다.
인터럽트 컨텍스트는 현재 실행 중인 코드가 인터럽트를 처리 중이라는 의미이다. 위 코드에서 보자면, 단순히 rio_interrupt()
함수를 호출한다고 해서 인터럽트 컨텍스트로 들어가는 것이 아닌, 커널 내부에서 인터럽트를 받아 해당 함수를 실행했을 때를 의미한다. 실행 흐름은 다음과 같다:
rio_interrupt()
)![]() |
---|
include/linux/irqdesc.h |
인터럽트 디스크립터는 인터럽트의 세부 속성을 관리하는 자료구조이다. 필드가 무지하게 많은데 핵심적으로 다음의 자료를 관리한다:
리눅스 커널 디바이스 드라이버 분석에 있어서는 다음의 이점이 있다:
커널의 핵심 동작을 이해하는 데에 있어서도 이점이 있다:
![]() |
---|
arch/arm64/kernel/entry.S |
이건 이전 장에서 설명했듯이 최초에는 Exception Vector 에서부터 코드가 실행된다. el1h_64_irq_handler()
와 같은 함수로 이어진다. 전체 경로는 다음과 같다:
el1h_64_irq_handler()
el1_interrupt()
(여기에서 handle_arch_irq
를 인자로 넘김)__el1_irq()
혹은 __el1_pnmi()
do_interrupt_handler()
를 호출call_on_irq_stack()
혹은 전달받은 인자인 handler()
호출. 어느 루트를 타던 관계없이 결국 handler()
를 호출. 이 handler 가 바로 handle_arch_irq
이다.
System Call
이 들어와 소프트웨어 인터럽트를 받은 경우에는 살짝 다른 코드 패스를 타는데 결국 호출하는 함수는 같다.
여기에서 el1h_64_irq
는 코드 상에서는 보이지 않는데 이전 장에서 소개한 것처럼 아마 entry.S
에서 등록한 함수로 추정된다.
vmlinux
를 objdump
하게 되면 확실하게 el1h_64_irq
가 등록되어 있다. 그렇다면 handle_arch_irq
는 어디서에서 누가 등록하는가?
![]() |
---|
arch/arm64/kernel/irq.c |
handle_arch_irq
는 arch/arm64/kernel/irq.c
에 선언되어 있는 전역 변수이다. 이 값은 최초에 default_handle_irq
로 초기화되어 있는데 실제로 이 함수가 호출되진 않는다. 그럼 무엇이 호출되는가? 우선 이 값을 변경하는 유일한 경로는 삽화에 있는 set_handle_irq()
이다. 그렇다면 다시 한번 이 함수는 누가 호출해서 무슨 함수를 등록하는가? 이 함수를 호출하는 경로는 다양한데 다음과 같다:
잘린 경로는 drivers/irqchip/
이다. 확인하는 방법은 여러가지가 있는데 필자는 generic_handle_domain_irq()
함수에 ftrace
를 걸어서 확인해보았다:
필자의 플랫폼에서는 bcm2836_arm_irqchip_handle_irq()
가 호출된다. 여기까지가 아키텍처에 종속적인 인터럽트 루틴이다. generic_handle_domain_irq()
함수로 넘어가는 아키텍처와 무관한 리눅스 커널의 코드로 이어지게 된다.
irq
서브 시스템 이어지는 handle_fasteoi_irq
등에 대해서도 분석을 하려 했는데 코드 분량과 배경이 너무나도 많이 필요하기 때문에 우선은 생략한다. 핵심적인 내용만 말하자면 다음과 같다:
PIC (Programmable Interrupt Controller)
와 아키텍처에 종속적이다.https://movefast.tistory.com/346
https://karatus.tistory.com/195
https://linux-kernel-labs.github.io/refs/heads/master/lectures/interrupts.html
https://yohda.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%BB%A4%EB%84%90-interrupt-IRQ-number