-
반응형
인터럽트 컨텍스트에서는 인터럽트 핸들링을 빠른 시간에 마무리해야 한다. 또한 시간이 오래 걸리는 함수를 호출하면 시스템은 커널 패닉으로 시스템이 오동작할 수 있다. 그럼 인터럽트가 발생한 후 인터럽트 핸들러에서 처리할 일이 많을 때는 어떻게 해야 할까? 이럴 때는 해야 할 작업을 2단계로 나누면 된다. 즉, 빨리 처리해야 하는 일과 조금 있다가 처리해도 되는 일이다. 임베디드 용어로 인터럽트가 발생한 후 빨리 처리해야 하는 일은 Top Half, 조금 있다가 처리해도 되는 일은 Bottom Half라고 표현한다. 이를 조금 더 전문적으로 표현해 보자면 인터럽트 핸들러가 하는 일은 Top Half라고 볼 수 있고, Bottom Half는 인터럽트 처리를 프로세스 레벨에서 수행하는 방식이다. 인터럽트 핸들러는 일하고 있는 프로세스를 멈춘 시점인 인터럽트 컨텍스트에서 실행한다. 급하게 처리해야 할 일은 인터럽트 컨텍스트에서 처리하고, 조금 후 실행해도 되는 일은 프로세스 컨텍스트에서 처리한다. 커널은 인터럽트 후반부 처리를 위해 다음과 같은 기법을 지원한다.
- IRQ 스레드
- Solt IRQ
- 태스크릿
- 워크큐
인터럽트 컨텍스트와 프로세스 컨텍스트 레벨에서 어떤 코드를 실행할 때는 인터럽트 컨텍스트에서는 호출할 수 있는 함수가 제한되어 있다. 리눅스 커널에서는 인터럽트 컨텍스트에서 많은 일을 하는 함수를 호출할 대 경고 메시지를 출력하거나 커널 패닉을 유발해서 시스템 실행을 중단시킨다. 예를 들어, 스케줄링을 지원하는 뮤텍스나 schedule() 함수를 쓰면 커널은 강제로 커널 패닉을 유발한다. 그런데 인터럽트 컨텍스트에 비해 프로세스 컨텍스트에서는 커널이 제공하는 스케줄링을 포함한 모든 함수를 쓸 수 있다. 따라서 시나리오에 따라 유연하게 코드를 설계할 수 있다. 예를 들어 인터럽트가 발생했을 때 이를 유저 공간에 알리고 싶은 경우가 있다. 안드로이드 디바이스 같은 경우 터치를 입력하면 발생하는 인터럽트를 uevent로 유저 공간에 알릴 수 있다. 유저 공간에 uevent를 보내는 동작은 시간이 오래 걸리는 일이다. 따라서 시간이 오래 걸리는 코드는 인터럽트 후반부에서 처리하도록 드라이버 구조를 잡을 수 있다.
1. 인터럽트 후반부 처리 기법의 종류
리눅스 커널이 인터럽트 후반부를 처리하는 대표적인 기법인 IRQ 스레드와 Soft IRQ 기법과 태스크릿을 알아보자. 이 세 가지 기법은 인터럽트 후반부를 처리하는 방식이 조금씩 다르다. 하지만 인터럽트 핸들러에서 해야 할 일을 2단계로 나눈다는 점은 같다.
처리기법 특징 IRQ 스레드
(threaded IRQ)인터럽트를 처리하는 전용 IRQ 스레드에서 인터럽트 후속 처리를 수행한다. 만약 rasp라는 24번 인터럽트가 있으면 "irq/24-rasp"란 IRQ 스레드가 24번 인터럽트 후반부를 전담해 처리한다. Soft IRQ 인터럽트 핸들러 실행이 끝나면 바로 일을 시작한다. 인터럽트 핸들러에서 바로 처리해야 할 일을 마무리한 후 인터럽트 후반부 처리를 Soft IRQ 컨텍스트에서 실행한다. Soft IRQ 서비스 핸들러의 실행 도중 시간이 오래 걸리면 ksoftirqd 프로세스를 깨우고 Soft IRQ 서비스를 종료한다. ksoftirqd라는 프로세스에서 나머지 인터럽트 후반부를 처리하는 구조이다. 태스크릿 Soft IRQ 서비스를 동적으로 쓸 수 있는 인터페이스이자 자료구조이다. 워크큐 인터럽트 핸들러가 실행될 때 워크를 워크큐에 큐잉하고 프로세스 레벨의 워커 스레드에서 인터럽트 후반부 처리를 하는 방식이다. 그 앞에서 설명한 4가지 기법 중 어떤 방식을 인터럽트 후반부 처리로 적용해야 할까? 답은 상황에 따라 다르다는 것이다. 인터럽트의 발생 빈도와 드라이버 시나리오에 따라 위 세 가지 기법을 적절히 조합해서 드라이버 구조를 잡으면 된다. 이를 위해 인터럽트를 시스템에서 처리하는 방식과 인터럽트가 얼마나 자주 발생하는지를 알아야 한다. 인터럽트를 처리하는 드라이버를 작성할 때 어떤 기법을 쓸지는 드라이버 담당자가 결정해야 한다.
반응형