컴퓨터공부/Linux & Unix

인터럽트

achivenKakao 2007. 3. 26. 03:35

프로세스마다 인터럽트 처리 방식이 다르기 때문에 리눅스 커널에서는 동일하게 처리하기 위해 IRQ 인터럽트는 모두 do_IRQ() 함수를 호출하여 처리하도록 되어 있다. 이 do_IRQ() 함수는 오직 IRQ 처리만 담당하며, irq_desc라는 전역 변수에 등록된 인터럽트 서비스 함수를 호출하는 구조로 되어 있다. 디바이스 드라이버나 커널에서 IRQ 인터럽트 처리가 필요한 경우에는 처리하고자 하는 IRQ 번호에 해당하는 인터럽트 서비스 함수를 이 irq_desc 전역 변수에 등록하면 된다.

IRQ 인터럽트 서비스를 처리하기 위해서는 request_irq() 함수를 이용하여 처리하고자 하는 IRQ 번호와 서비스 함수 주소를 등록한다. 인터럽트가 발생하면 커널은 아키텍처마다 고유의 IRQ 검출 루틴을 이용하여 발생된 인터럽트의 IRQ 번호를 획득하여 do_IRQ() 함수를 호출한다.

더이상의 인터럽트 처리가 필요없는 경우에는 하드웨어에서 인터럽트가 더이상 발생하지 않도록 처리한 후 free_irq() 함수를 이용하여 irq_desc 전역 변수에 등록된 인터럽트 서비스 함수를 제거한다.


. 커널 2.4

. #include <linux/sched.h>

. void int_handler(int irq, void* dev_id, struct pt_regs* regs)

{

}

. int request_irq(unsigned int irq, void (*handler)(int, void*, struct pt_regs*), unsigned long frags, const char* device, void* dev_id);

. void free_irq(unsigned int irq, void* dev_id);


. 커널 2.6

. #include <linux/interrupt.h>

. irqreturn_t int_hander(int irq, void* dev_id, struct pt_regs* regs)

{

    return IRQ_HANDLED;

}

. int request_irq(unsigned int irq, irqreturn_t(*handler)(int, void* struct pt_regs*), unsigned long frags const char* device, void* dev_id);


. frags

    . SA_INTERRUPT             다른 인터럽트를 허가하지 않느다

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

    . SA_SAMPLE_RANDOM  랜덤값 처리에 영향을 준다

. dev_id

    . 인터럽트 ID를 뜻하는 것으로 인터럽트 공유에 사용되거나 인터럽트 서비스 함수를 수행할 때 필요한 정보가 저장된 메모리의 선두 주소를 담고 있다.

    . 인터럽트 서비스 함수를 등록할 때 지정된 값이 그대로 전달된다.

. regs

    . 인터럽트가 발생한 당시의 레지시터 값들


* 인터럽트 서비스 함수 내의 메모리 할당

vmalloc() 함수나 vfree() 그리고 ioremap() 같은 함수를 인터럽트에서 사용하면 안 된다. kmalloc() 함수 역시 GFP_ATOMIC 인자를 사용한 방식만 사용해야 한다는 제약이 있다.


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

왜 open()함수와 close()함수에서 인터럽트 서비스 함수 등록과 해제 루틴을 넣을까?

모듈이 초기화될 때 서비스 함수가 등록되면 해당 디바이스를 사용하는 응용 프로그램이 없다 하더라도 인터럽트가 발생하면 인터럽트 서비스 함수가 항상 호출된다. 이런 호출 방식은 전체 시스템의 처리 속도를 약간 느리게 만드는 원인이 된다. 그래서 open() 함수나 close() 함수에서 인터럽트 서비스 함수의 등록과 해제가 이루어지도록 해야 한다.

PC 같은 범용 시스템에 사용되는 디바이스 드라이버를 만든다면 open() 함수와 close() 함수에서 처리해야 하고, 임베디드와 같은 특정 목적의 시스템에서는 모듈의 등록과 해제 시에 처리하는 것이 좋다.


* 인터럽트의 공유

인터럽트 서비스 함수를 등록할 때 같은 인터럽트 번호에 대해 다른 인터럽트 서비스 함수를 등록할 수 있도록 지원한다.

. frags : SA_SHIRQ가 포함되어야 하고

. dev_id : 동일한 인터럽트 서비스 함수를 구별하기 위해 0이 아닌 값을 사용해야 한다.


* 인터럽트의 금지와 해제

-. 인터럽트 서비스 함수가 동작중에 다른 인터럽트가 발생하지 못하게 막는 경우

. frags 매개변수에 SA_INTERRUPT를 포함시킨다. 인터럽트가 발생했을 때 SA_INTERRUPT 값이 포함된 인터럽트 서비스 함수는 커널이 프로세스의 인터럽트를 금지시킨 후 호출한다.

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

. 특정 인터럽트 번호에 대한 금지와 해제

    . #include <asm/irq.h>

    . void disable_irq(int irq) : 인터럽트 금지

    . void enable_irq(int irq) : 인터럽트 허가

. 전체 인터럽트를 금지하고 해제

    . #include <asm/system.h>

    . 커널 2.4

        . cli(void) : 프로세서의 인터럽트 처리를 금지한다.

        . sti(void) : 프로세서의 인터럽트 처리를 허가한다.

        . save_flags(unsigned long frags) : 현재의 프로세스의 상태를 저장한다.

        . restore_flags(unsigned long frags) : 저장된 프로세스의 상태를 복구한다.

    . 커널 2.6

        . local_irq_disable(void) : 프로세서의 인터럽트 처리를 금지한다.

        . local_irq_enable(void) : 프로세서의 인터럽트 처리를 허가한다.

        . local_save_flags(unsigned long frags) : 현재의 프로세스의 상태를 저장한다.

        . local_restore_flags(unsigned long frags) : 저장된 프로세스의 상태를 복구한다.


        unsigned long frags;

        local_save_flags(frags);

        local_irq_disable();

        // 인터럽트 호출에서 보호하고자 하는 루틴

        local_restore_flags(frags);

        . local_irq_enable() 함수를 사용하지 않는데, 그 이유는 local_save_flags() 함수에 의해 저장된 frags 변수에는 이미 인터럽트 허가 상태가 포함되어 있기 때문이다. 그래서 굳이 local_irq_enable() 함수를 사용하지 않아도 local_restore_flags() 함수에 의해 local_irq_disable() 함수를 호출한 효과가 발생한다.


* 인터럽트 발생 횟수

인터럽트 발생 횟수는 커널이 알아서 계산한다. /proc/interrupts란 파일을 보면 현재 인터럽트의 발생 횟수를 볼 수 있다.

# cat /proc/interrupts