디바이스 드라이버를 위한 커널 서비스



2. 인터럽트 처리




2.1 예외 처리


 외부의 정상적인 요청이나 비정상적인 오류의 발생에 의해서 프로그램을 중지하고 발생된 요청이나 오류를 처리하는 것

 예외처리가 발생하면 처리해야 할 프로그램의 위치가 메모리의 특정한 위치에 지정


 예외 벡터 테이블

 address

Exception 

Mode on Entry 

Priorities

0x00000000

Reset 

Supervisor 

1 

0x00000004

Undefined instruction 

Undefined 

0x00000008 

Software interrupt 

Supervisor 

0x0000000C 

Abort (prefetch) 

Abort 

0x00000010 

Abort (data) 

Abort 

0x00000014 

Reserved 

Reserved 

Reserved 

0x00000018 

IRQ 

IRQ 

0x0000001C 

FIQ 

FIQ 






2.2 인터럽트



디바이스는 프로세서와는 다른 시간 간격, 정확하게는 훨씬 느린 시간으로 수행된다.


프로세서가 무작정 외부 이벤트를 기다리는 것은 바람직하지 못하므로 프로세서에게 뭔가가 발생했다고 알릴 방법이 있어야 한다.





2.3 인터럽트 컨트롤러



외부에서 발생하는 여러가지 인터럽트를 받아들여 서비스 여부를 결정.

발생된 인터럽트에 대한 정보는 처리가 완료될 때까지 저장


인터럽트 컨트롤러 레지스터

 이름

용도

 SRCPND

 인터럽트가 발생 여부가 체크 되는 레지스터, 여러개의 인터럽트 소스가 체크될 수 있음.

 INTMSK

 인터럽트의 사용 여부를 결정

 INTPND

 SRCPND 레지스터와 달리 여러개의 인터럽트 소스가 발생하면 우선 순위 로직을 거쳐 하나의 인터럽트 소스만 체크되는 레지스터 

 SUBSRCPND

 SRCPND 레지스터와 동일한 기능이며, Sub 개념을 좀 더 많은 인터럽트 소스를 처리하기 위해 사용 

 INTSUBMSK

 INTMSK 레지스터와 동일한 기능이며, SUBSRCPND의 인터럽트 사용 여부를 결정

 INTMOD

 인터럽트 소스를 IRQ/FIQ로 사용할지를 결정 






2.4 인터럽트 처리



  • 인터럽트 핸들러 (인터럽트 서비스 루틴 - ISR)

인터럽트를 처리하는 커널 함수.
각각의 디바이스가 발생시키는 인터럽트에는 그와 관련된 인터럽트 핸들러 존재
디바이스 드라이버의 일부분
인터럽트 컨텍스트에서 실행 (인터럽트 모드로 전환)



  • 인터럽트 처리를 두 단계로 분리

- 톱 하프 (top half)

: 인터럽트 핸들러
  인터럽트가 발생하는 즉시 실행되는 타임 크리티컬한 작업들


- 바톰 하프 (bottom half)

: 나중에 해도 무관한 작업들
  모든 인터럽트를 활성화시킨 상태에서 실행 (보통 인터럽트 핸들러가 리턴되면 실행)




  • 인터럽트 서비스 처리 흐름

프로세서마다 인터럽트 처리 방식이 다르기 때문에 리눅스 커널에서는 동일하게 처리하기 위해 IRQ 인터럽트는 모두 do_IRQ() 함수를 호출하여 처리






1. request_irq() 함수를 이용하여 처리하고자 하는 IRQ 번호와 서비스 함수 주소를 등록


2. 인터럽트 발생 후

2.1 아키텍처마다 고유의 IRQ 검출 루틴을 이용하여 발생된 인터럽트의 IRQ 번호를 획득하여 do_IRQ() 함수 호출
2.2 do_IRQ() 함수는 irq_desc 전역 변수에서 IRQ 번호에 맞게 등록된 인터럽트 서비스 함수를 찾고, 있으면 호출
2.3 참조된 데이터의 주소나 값들은 인터럽트 서비스 함수의 매개 변수인 dev_id에 전달
2.4 인터럽트 처리가 필요 없는 경우는 free_irq() 함수 호출
      -> irq_desc 전역 변수에 등록된 인터럽트 서비스 함수를 제거




  • 인터럽트 서비스 함수의 형식

     - 매개 변수

 int irq 

 인터럽트 번호 (irqs.h에 정의) 

 void *dev_id 

 인터럽트 ID 또는 인터럽트 함수가 사용 가능한 메모리 주소.

 인터럽트 서비스 공유에 사용되거나 인터럽트 서비스 함수를 수행할 때 필요한 정보가 저장된 메모리의 주소를 저장

 struct pt_regs *regs

 인터럽트가 발생했을 당시의 레지스터 값

 대부분 사용하지 않음 (디버깅 목적)



- 주로 사용하는 메모리 할당 및 해제

kalloc()

- GFP_ATOMIC 인자를 사용한 방식만 사용해야 함
- vmalloc 함수로 인터럽트가 발생하기 전 미리 할당한 메모리는 아무런 제약 사항이 없음


- 인터럽트 함수 실행 후 정상적으로 처리되었다면 IRQ_HANDLED를 반환




  • 인터럽트 서비스 함수의 등록 - request_irq()

- 매개 변수

 int irq

 인터럽트 번호

 irqreturn_t (*handler) (int, void* struct pt_regs *regs)

 인터럽트가 발생했을 때 처리되는 인터럽트 서비스 함수 포인터 

 unsigned long flags

 인터럽트 공유나 바른 인터럽트 같은 처리 속성을 지정

 SA_INTERRUPT: 다른 인터럽트를 허가하지 않음

 SA_SHIRQ: 동일한 인터럽트 번호를 공유

 SA_SAMPLE_RANDOM: 랜덤 값 처리에 영향을 줌 (/dev/random) 

 const char *device

 인터럽트에 대한 소유자를 표현하는 문자열을 지정 

 void* dev_id

 인터럽트가 공유되었을 때 인터럽트에 대한 구분 인자로 사용

 인터럽트 서비스 함수가 참조하는 데이터에 대한 주소를 지정



- 정상적으로 수행되면 0을 리턴

- 비정상적일 때는 에러 상황에 맞는 음수 값을 반환




  • 인터럽트 서비스 함수의 해체 - free_irq()

- 매개 변수

 int irq 

 제거하고자 하는 인터럽트 서비스 함수의 인터럽트 번호

 dev_id

 request_irq() 함수에서 사용한 dev_id와 동일






  • 인터럽트 서비스 등록 시점과 해제 시점


- 하나의 응용 프로그램만이 디바이스 파일을 사용하는 경우


모듈 초기화, open() 함수 모두 ISR 등록 가능

응용 프로그램이 종료되고 난 후 close() 함수에서 ISR 제거


- 두 개 이상의 응용 프로그램이 디바이스 파일을 사용하는 경우


모듈 초기화에서 ISR 등록

-> open() 에서 등록할 경우 동일한 인터럽트 서비스 루틴이 두 번 이상 등록할 수 있음

-> open() 에서 등록할 경우 먼저 해제를 시도한 쪽에서 인터럽트 서비스 함수를 제거하는 경우,

    다른 쪽 응용 프로그램에서 인터럽트 서비스 함수가 동작하지 않음.



- PC 같은 범용 시스템에 사용되는 디바이스 드라이버

-> open()과 close() 함수에서 처리



- 임베디드와 같은 특정 목적의 시스템에서는 모듈의 등록과 해제 시에 처리하는 것이 좋음





2.5 인터럽트 함수와 디바이스 드라이버 간의 데이터 공유


인터럽트 서비스 함수는 프로세스 문맥과 별개로 동작하므로 인터럽트 서비스 함수와 디바이스 드라이버 함수 간에 데이터를 공유하는 방법이 필요

 <공유 방법>

  1. 전역 변수
인터럽트 하나에 인터럽트 서비스 함수 하나가 동작하는 경우

  2. dev_id 매개 변수
여러 디바이스를 하나의 인터럽트 서비스 함수에서 사용 가능
다중 프로세스 환경에 매우 적합






2.6 인터럽트 공유


같은 인터럽트 번호에 대해 다른 인터럽트 서비스 함수 등록 가능

-> request_irq() 함수의 매개 변수에서 플래그를 SA_SHIRQ로 설정
    dev_id 매개 변수의 경우, 동일한 인터럽트 서비스 함수를 구별하기 위해 0이 아닌 값을 사용해야 함


같은 인터럽트에 대해 여러 인터럽트 서비스 함수가 동작할 경우

-> 인터럽트 서비스 함수는 자신이 관리하는 디바이스에서 인터럽트가 발생했는지를 확인하는 과정이 필요

인터럽트가 들어오면 -> 자신의 디바이스에서 발생한 것인지 체크 후 -> 자신의 디바이스가 아니면 IRQ_NONE 리턴,
맞으면 IRQ_HANDLED 리턴



2.7 인터럽트 금지와 해제


    • 인터럽트 서비스 함수가 동작 중에 다른 인터럽트가 발생하지 못하게 금지하는 방법

:request_irq()의 플래그를 SA_INTERRUPT를 포함


커널이 프로세서의 인터럽트를 금지 시킨 후 호출

인터럽트 두 개가 동시에 발생하면 SA_INTERRUPT가 설정된 서비스 함수를 먼저 수행한다.



    • 일반적인 함수 수행 중에 데이터 처리를 보호하기 위해 인터럽트를 강제로 막는 방법


#include <asm/irq.h>

 void disable_irq(int irq)

 인터럽트 금지

 void enable_irq(int irq)

 인터럽트 허가

 void disable_irq_nosync(int irq)

 현재 핸들러가 종료할 때까지 기다리지 않음

 synchronize_irq(unsigned int irq)

 특정 인터럽트 핸들러가 실행 중일 경우 기다렸다가 실행이 끝나면 리턴






    • 프로세서 전체의 인터럽트를 금지하고 해제하는 방법

#include <asm/system.h>

 local_irq_disable(void)

 프로세서의 인터럽트 처리를 금지

 local_irq_enable(void)

 프로세서의 인터럽트 처리를 허가

 local_save_flags(ulong flags)

 현재의 프로세스의 상태를 저장

 local_restore_flags(ulong flags)

 저장된 프로세스의 상태를 복구

 local_irq_save(unsigned long flags)

 프로세서의 인터럽트 처리를 금지시키면서 동시에 현재 프로세서의 상태를 저장

 local_irq_restore(unsigned long flags)

 프로세서의 인터럽트 처리를 허가시키면서 동시에 현재 프로세서의 상태를 복구




    • 보편적인 인터럽트 방지법

unsigned long flags;


local_save_flags(flags);

local_irq_disable();

local_restore_flags(flags); 



* local_irq_enable() 함수를 사용하지 않는 이유


local_save_flags() 함수에 의해 저장된 flags 변수에 이미 인터럽트 허가 상태가 포함되어 있기 때문에 local_restore_flags() 함수에 의해 local_irq_enable() 함수를 호출한 효과가 발생












+ Recent posts