IT/Linux Kernel

디버깅을 통해 배우는 리눅스 커널의 구조와 원리 1, 도서 공부하기 20 - 인터럽트(interrupt), 인터럽트 컨텍스트란?

변화의 물결1 2025. 5. 13. 00:15

 

 

 

안녕하세요.

 

 프로세스에 관한 내용을 지나 5장 인터럽트 내용으로 왔습니다. 인터럽터가 OS에 중요한 부분을 차지하고 있으니 5, 6장에 걸쳐 소개하고 있습니다. 천천히 책 내용을 따라가 보겠습니다.

 

 진도가 너무 느려져서, 요약하면서 조금 빠르게 진행해 보도록 하겠습니다.

 


 

1. 인터럽트 소개

 

 인터럽터라는 단어 자체는 생활에서도 사용하고 있는 용어입니다. 끼어들다는 말로 사용하고 있습니다.

 대화를 하는 중에 다른 사람이 와서 끼어들기도 하고 공부하는 중에 전화가 온다던지 이상생활에서 인터럽트가 발생합니다. 실제적으로 컴퓨터 입장에서 인터럽터를 확인해 보겠습니다.

 

1) 하드웨어 관점의 인터럽트

 

  하드웨어 관정에서 인터럽트란 하드웨어의 변화를 감지해서 외부 입력으로 전달되는 전기 신호입니다. 예를 들면 키보드를 치면 하드웨어적으로 키보드 하드웨어의 신호가 발생합니다. 단순하게 화면에 글자가 나타나는 것이 아니라는 것이죠.

 

 오실로코프로 인터럽트 신호를 측정하면 파형을 볼 수 있습니다. low에서 high 올라가는 RISING EDGE 구간과 high를 유지하는 HIGH를 구간으로 분류할 수 있습니다.

 

 

 

2) CPU 입장에서 인터럽터란?

 

 ARMv7 아키텍처에서 인터럽터는 익셉션(Exception)의 한 종류로 처리되므로 익셉션의 처리 방식에 대해 알 필요가 있습니다.

 ARMv7 프로세서는 익셉션이 발생했다고 감지하면 익셉션 종류별로 이미 정해 놓은 주소로 브랜치(이동)합니다. 즉, ARM 코어 프로그램 카운터(PC)를 정해놓은 주소로 바꿉니다. 그리고 현재 실행 중인 레지스터 세트를 스택에 푸시합니다.

 

 이런 일이 일어난다는 것은 실행하고 있는 어떤 커널 코드도 인터럽트가 발생하면 인터럽트 백터로 실행 흐름이 바뀔 수 있다는 것을 이야기하고 있습니다.

 

 

2. 리눅스 커널 인터럽트 주요 개념

 

1) 인터럽트 핸들러

 

 인터럽트가 발생하면 이를 핸들링하기 위한 함수가 호출되는데 이를 인터럽트 핸들러라고 합니다. 디바이스별로 인터럽트 핸들러가 처리되는 과정을 확인할 수 있습니다. 인터럽트 종류별로 인터럽트 핸들러가 있으며 함수 형태로 존재하며, 커널 내부의 IRQ(Interrupt ReQuest) 서브시스템을 통해 호출됩니다.

 

 

 request_irq() 함수를 적절한 인자와 함께 호출해서 미리 인터럽트 핸들러를 동록해야 합니다. 예로 마우스를 움직일 때 인터럽트를 처리하는 코드를 보겠습니다.

 

 84째 줄 request_irq() 함수의 두 번째 인자로 인터럽트 핸들러 함수인 amimouse_interrupt()를 등록합니다.

 

# vim drivers/input/mouse/amimouse.c

 

 

 

 인터럽트 핸들러에서는 마우스에서 입력한 데이터 정보를 참고해 유저 공간에 알리는 동작을 수행합니다.

 

 

 

2) 인터럽트 디스크립터

 

 인터럽트 종류별로 여러 가지 인터럽트 세부 속성을 관리하는 자료구조를 인터럽트 디스크립트라고 합니다. 속성에는 인터럽트 핸들러, 인터럽트 핸들러 매개변수, 논리적인 인터럽트 번호, 인터럽트 실행 횟수 등이 있습니다.

 

 이전에 알아보았던 프로세스의 세부 속성을 표현하는 자료구조가 태스크 디스크립터이듯 인터럽트에 대한 속성 정보를 저장하는 구조가 인터럽트 디스크립터입니다.

 

# vim include/linux/irqdesc.h

 

  

 

3) 인터럽트 잘 알아야 하는 이유

 

(1) 기본적인 이유

 

 - 대부분의 리눅스 드라이버는 인터럽트를 통해 하드웨어 디바이스와 통신합니다. 인터럽트의 동작 방식을 알고 있으면 디바이스 드라이버 코드를 빨리 이해할 수 있습니다.

 - 인터럽트 처리 과정을 숙지하면 프로세스가 스택 메모리 공간에서 어떻게 실행되는지 알게 됩니다.

 - 인터럽트 벡터가 어떻게 동작하는지 알면 ARM 아키텍처의 동작 원리에 대해 더 많이 알게 됩니다.

 

(2) 세부동작 방식 알아야 할 필요성

 

 - 스케줄링에서 선정(Preemptive) 스케줄링 진입 경로 중 하나가 인터럽트 처리를 끝낸 시점입니다.

 - 유저 공간에서 등록한 시그널 핸들러는 인터럽트 핸들러를 실행한 다음 처리를 시작합니다.

 - 레이스 컨디션이 발생하는 가장 큰 이유 중 하나는 비동기적으로 인터럽트가 발생해서 임계 영역의 코드를 오염시키기 때문입니다.

 

 

4. 리눅스 커널에서 인터럽트 처리 흐름

 

1) 인터럽트가 발생할 경우 처리하는 3단계 과정

 

(1) 1단계 - 인터럽트 발생

 인터럽트가 발생하면 프로세스는 도중 인터럽트 백터로 이동합니다. 인터럽트 벡터에서 인터럽트 처리를 마무리한 후 원래 프로세스로 돌아오기 위해 실행 중인 레지스터 세트를 스택에 저장합니다. 이후 IRQ 서브 시스템을 구성하는 함수들이 호출됩니다.

 

(2) 2단계 - 인터럽트 핸들러 호출

 커널 내부에서는 발생한 인터럽트에 대응하는 인터럽트 디스크립터를 읽어서 저장된 인터럽트 핸들러를 호출합니다.

 

(3) 3단계 - 인터럽트 핸들러 실행

 인터럽트 핸들러에서 하드웨어를 직접 제어하고 유저 공간에 이 변화를 알립니다. 각 디바이스 드라이버에서 등록한 인터럽트 핸들러를 실행해 인터럽트 발생에 대한 처리를 합니다.

 

 

5. 인터럽트 컨텍스트 (Interrupt Context)

 

1) 인터럽트 컨텍스트란?

 

 인터럽트와 컨텍스트가 합쳐진 의미로 인터럽터는 앞서 보았고, 컨텍스트(Context)란 프로세스 실행 그 자체를 말합니다. 이것은 실행 중인 프로세스 정보를 담고 있는 레지스터 세트로 표현할 수 있습니다. 레지스터 세트로 현재 실행 중인 상태를 저장하기 때문입니다.

 

 또 다른 표현으로 현재 실행 중인 함수가 인터럽트 핸들러이거나 인터럽트 핸들러에서 호출된 함수라는 의미로 볼 수도 있습니다.

 

 프로세스 실행은 레지스터 세트로 표현할 수 있습니다. 레지스터 세트 구조는 cpu_context_save에서 확인할 수 있습니다.

 

 # vim arch/arm/include/asm/thread_info.h

 

 

  

 cpu_context_save 구조체는 프로세스 스택의 최상단 주소에 위치한 thread_info 구조체의 cpu_context 필드에 저장됩니다.

 

# vim arch/arm/include/asm/thread_info.h

 

 

 

2) ftrace와 커널 로그로 인터럽트 컨텍스트 확인하기

 

 리눅스 전문가들이 커널 세부 동작을 가장 정밀하게 표현하는 로그를 ftrace라고 합니다. 윈도우 환경에 적응한 분들은 동의하지 않을 수 있을 것입니다. 그러나 리눅스 커널의 공식 트레이서이기도 합니다.

 ftrace로 인터럽트 처리하는 인터럽트 핸들러 함수에 필터를 걸고 콜 스택 로그를 저장해 보겠습니다.

 

(1) 인터럽트 동작을 확인하기 위한 ftrace 설정

 

 기존 부분은 동일하고 필터와 이벤트 설정 부분만 확인하겠습니다.

 

 dwc_otg_common_irq 함수가 실행될 때의 정보만 기록하도록 필터를 설정하는 것입니다. USB OTG 관련 인터럽트가 발생했을 때 어떤 일이 일어나는지 살펴보기 위한 설정입니다.

 

echo dwc_otg_common_irq > /sys/kernel/debug/tracing/set_ftrace_filter

 

 그런데 dwc_otg_common_irq 함수 발생하는 방법이 나와 있지 않아서 usb 메모리 연결하면 발생하는 함수로 변경했습니다. xhci_irq는 xHCI (USB 3.0) 호스트 컨트롤러의 인터럽트 처리 함수이므로 USB 3.0(파란색 커넥터)에 연결하면 함수를 확인할 수 있습니다.  ehci_irq(USB 2.0) 해봤는데 에러가 나서 안됨)

 

echo xhci_irq > /sys/kernel/debug/tracing/set_ftrace_filter

 

  

irq_handler_entry와 irq_handler_exit 이벤트는 인터럽트 핸들러 함수가 시작하고  종료될 때 발생합니다.

 

echo 1 > /sys/kernel/debug/tracing/events/irq/irq_handler_entry/enable
echo 1 > /sys/kernel/debug/tracing/events/irq/irq_handler_exit/enable

 

 

 

(2) 라즈베리 파이에서 받은 ftrace 인터럽트 컨텍스트 확인

 

 로그를 확인하면 책 내용과 다른 함수(xhci_irq)를 사용하다 보니 내용이 다릅니다. 유사한 내용으로 확인해 보겠습니다.

 

 160째 줄을 내용을 보면, CPU 코어 0이 아이들 상태에 있을 때, 427.226611초 시점에 IRQ 번호 53번 인터럽트가 발생했으며, 이 인터럽트를 처리하기 위해 xhci_hcd라는 USB 3.0 호스트 컨트롤러 드라이버의 인터럽트 핸들러 함수가 실행되기 시작했다는 것을 의미합니다.

 

 

 

 위 로그를 보면 설명하자면, arch_cpu_idle 상태에서 irq=53 name=xhci_hcd 인터럽터가 발생했습니다.

ARM 프로세서는 인터럽트가 발생하면 익셉션을 유발해 인터럽트 벡터를 실행합니다.

 

인터럽터 벡터에 있는 vector_irq라는 레이블에서 __irq_svc 레이블을 브랜치(호출)합니다. 이후 리눅스 커널 내부의 인터럽트를 처리하는 함수가 다음 순서대로 호출합니다.

 

 

 인터럽트 컨텍스트 표시된 구간에서 어떤 함수가 실행 중이면 '현재 인터럽트 컨텍스트'라고 말할 수 있습니다. 그리고 인터럽트 컨텍스트를 정의하는 이유는 인터럽트를 핸들링하는 시점에서 더 빠르고 간결하게 코드를 실행하기 위해서입니다.

 

 

감사합니다.

 

 

<참고 자료>

1. [도서] 디버깅을 통해 배우는 리눅스 커널의 구조와 원리 p281 ~ 302, wikibook

2. Signal_edge

https://en.wikipedia.org/wiki/Signal_edge

 

 

ftrace_setting_interrupt.sh
0.00MB

 

 

반응형