목차
❶ Linux의 Interrupt 처리 과정
1. 프로그램가능 인터럽트 컨트롤러(Programmable Interrupt Controller, PIC)
2. 인터럽트 처리용 자료구조의 초기화
3. 인터럽트 처리
❷ Windows의 Interrupt 처리 과정
1. 인터럽트를 사용해서 이벤트 발생을 알려준다.
2. 우선순위가 낮은 인터럽트는 보류한다.
❸ Windows와 Linux의 Interrupt의 가장 큰 차이
❹ 추가 참고 내용 ( 리눅스 - IRQ 인터럽트 처리 과정 )
1. 프로그램가능 인터럽트 컨트롤러(Programmable Interrupt Controller, PIC)
2. 인터럽트 처리용 자료구조의 초기화
3. 인터럽트 처리
❷ Windows의 Interrupt 처리 과정
1. 인터럽트를 사용해서 이벤트 발생을 알려준다.
2. 우선순위가 낮은 인터럽트는 보류한다.
❸ Windows와 Linux의 Interrupt의 가장 큰 차이
❹ 추가 참고 내용 ( 리눅스 - IRQ 인터럽트 처리 과정 )
본문내용
~장치 N인 부분은 특정 장치에 할당된 하드웨어 인터럽트에 대응하는 부분이다. 실제로 할당할 수 있는 장치는 하드웨어 구성에 따라서 다르다.
//3~31 까지는 하드웨어 인터럽트에서 처리하고, 1~2 까지는 소프트웨어 인터럽트에서 처리한다. 그리고 0 에선 애플리케이션 실행된다.//
‘DPC/디스패치’는 지연 프로시저 호출과 스레드 전화을 위한 소프트웨어 인터럽트다. 지연 프로시저 호출은 I/O 접근 완료 통지처럼 곧바로 실행하지 않아도 되는 처리를 Windows에서 실행할 때 쓰인다. ‘APC'는 비동기 프로시저 호출을 하기 위한 소프트웨어 인터럽트다. 이는 특정 스레드의 컨텍스트로 함수를 실행하기 위해 이용한다. DPC와 APC는 인터럽트를 처리할 때 중요한 역할을 수행한다. 인터럽트를 제대로 처리하는 데 시간이 걸릴 경우에 인터럽트 처리기 안에서 모든 처리를 한다면 그 사이 다른 인터럽트를 처리하지 못할 가능성이 있다. 그래서 곧바로 실행해야 하는 처리만 핸들러 안에서 실행하고, 나머지 처리는 DPC를 사용해 다음에 실행하도록 한다. DPC의 IRQL은 Passive보다 상위이므로 다른 인터럽트 처리가 모두 끝난 후에 실행될 것을 보장해 준다.
마찬가지로 애플리케이션이 준비한 버퍼에 데이터를 써넣는 처리에서는 그 프로세스의 가상 주소 공간에 접근할 수 있어야 한다. 이러한 경우 APC를 이용해 그 프로세스의 스레드로 데이터를 써넣도록 한다.
일반적인 애플리케이션을 개발할 때는 이런 인터럽트 요구 레벨을 의식하면서 프로그래밍을 할 필요는 없지만, 장치 드라이버를 개발하는 경우에는 매우 중요하다. 예를 들어, 하드웨어 인터럽트는 페이지 부재 예외보다 우선도가 높기 때문에 실제 메모리가 할당되지 않은 주소에 접근해도 페이징에 의한 자동 할당이 이루어지지 않는다. 그 때문에 메모리에 접근할 때는 실제 메모리가 할당되었음을 보장해 주는 영역에만 접근할 수 있다.
제가 생각하는 Windows와 Linux의 Interrupt의 가장 큰 차이는,
Windows 에서는
① CPU가 각각의 인터럽트 처리기의 주소를 얻어올 때는 메모리에 있는 ‘인터럽트 디스크립터 테이블(IDT)' 이라는 영역을 참조한다. IDT(Interrupt Descriptor Table)는 인터럽트 처리에 관련된 Descriptor를 모아둔 테이블입니다. 여기에는 인터럽트 처리기가 존재하는 주소를 나타내는 셀렉터 값과 오프셋, 속성을 저장한 ’인터럽트 디스크립터‘가 인터럽트 번호 순서대로 나열되어 있다. 운영체제는 시작 시 이 테이블을 생성해서 선두 주소를 IDTR 레지스터에 저장해둔다. 인터럽트가 발생하면 CPU는 이 레지스터를 참조해 인터럽트 디스크립터 테이블의 주소를 얻고, 인터럽트 번호에 대응하는 엔트리에서 인터럽트 처리기의 주소를 구한다.
즉, IDT(인터럽트 디스크립터 테이블) + 오프셋, 셀렉터 값, 속성으로 메모리 공간의 ISR(INT3의 인터럽트 핸들러)의 위치를 찾아간다.
② 우선순위가 낮은 인터럽트는 보류 한다는 것이다.
인터럽트에 관해 주의할 것은 어떤 인터럽트를 처리하고 있는 도중에 다른 인터럽트가 발생할 가능성이 있다는 것이다. 이런 경우에 단지 나중에 발생한 인터럽트를 보류하도록 하면 중요한 인터럽트 처리가 늦어질지도 모른다. 그래서 Windows NT/2000/XP에서는 하드웨어 인터럽트나 소프트웨어 인터럽트에 대해서 인터럽트 요구 레벨(IRQL)이라고 하는 우선순위를 정의하고, 그에 따라 인터럽트 중첩 허락 여부를 결정한다. (선점성)
Linux 에서는
① 요청을 한 뒤 다른 더 유용한 작업을 하고 요청한 작업이 끝나면 장치로부터 인터럽트를 받는 것이다. 이런 설계를 사용하면 여러 장치에 동시에 작업을 요청하는 것이 가능하다. 즉, Linux IDT 테이블 주소 값을 저장하고 IDT 테이블은 256개의 엔트리에 있는 interrupt 주소로 바로 간다(즉, 엔트리에 시스템 호출이 발생할 때 호출되는 system_call() 함수가 등록 되어있다) -> ISR을 찾아간다. (비선점성)
추가 참고 내용
리눅스 - IRQ 인터럽트 처리 과정
IRQ 인터럽트를 처리하는 방식은 프로세서마다 다르다. 예를 들어, i386 계열 프로세서는 내부에 있는 인터럽트 벡터 테이블 안에 IRQ 인터럽트 벡터가 포함되어 있어서 인터럽트가 발생하면 해당 벡터 테이블에 의해 인터럽트 처리 루틴으로 분기(branch)되는 데 반해, ARM 계열 프로세서는 IRQ 벡터가 하나이므로 인터럽트 제어 장치를 통해 인터럽트 처리를 재분기한다. 이렇게 프로세서마다 인터럽트를 처리하는 방식이 다르기 때문에 리눅스 커널에서는 동일하게 처리하기 위해 IRQ 인터럽트는 모두 do_IRQ() 함수를 호출하여 처리하도록 하고 있다. 이 do_IRQ() 함수는 오직 IRQ 처리만 담당하며, irq_desc 라는 전역 변수에 등록된 인터럽트 서비스 함수를 호출하는 구조로 되어 있다. 디바이스 드라이버나 커널에서 IRQ 인터럽트 처리가 필요한 경우에는 처리하고자 하는 IRQ 번호에 해당하는 인터럽트 서비스 함수를 irq_desc 전역 변수에 등록하면 된다. 그리고 더 이상 처리가 필요 없다면 irq_desc 에 등록된 인터럽트 서비스 함수를 제거하면 된다.
그림에서 보듯이 IRQ 인터럽트 서비스를 처리하기 위해서는 request_irq() 함수를 이용하여 처리하고자 하는 IRQ 번호와 서비스 함수 주소를 등록한다. 인터럽트가 발생하면 커널은 아키텍처마다 고유의 IRQ 검출 루틴을 이용하여 발생된 인터럽트의 IRQ 번호를 획득하여 do_IRQ() 함수를 호출한다. 이렇게 호출된 do_IRQ() 함수는 irq_desc 전역 변수에서 IRQ 번호에 맞게 등록된 인터럽트 서비스 함수를 찾아보고, 등록된 함수가 있다면 호출한다. 이때 request_irq() 함수를 이용한 등록 과정에서 참조된 데이터의 주소나 값들은 인터럽트 서비스 함수의 매개변수인 dev_id에 전달된다.
더 이상 인터럽트 처리가 필요 없는 경우에는 free_irq() 함수를 이용해 irq_desc 전역 변수에 등록된 인터럽트 서비스 함수를 제거한다.
//3~31 까지는 하드웨어 인터럽트에서 처리하고, 1~2 까지는 소프트웨어 인터럽트에서 처리한다. 그리고 0 에선 애플리케이션 실행된다.//
‘DPC/디스패치’는 지연 프로시저 호출과 스레드 전화을 위한 소프트웨어 인터럽트다. 지연 프로시저 호출은 I/O 접근 완료 통지처럼 곧바로 실행하지 않아도 되는 처리를 Windows에서 실행할 때 쓰인다. ‘APC'는 비동기 프로시저 호출을 하기 위한 소프트웨어 인터럽트다. 이는 특정 스레드의 컨텍스트로 함수를 실행하기 위해 이용한다. DPC와 APC는 인터럽트를 처리할 때 중요한 역할을 수행한다. 인터럽트를 제대로 처리하는 데 시간이 걸릴 경우에 인터럽트 처리기 안에서 모든 처리를 한다면 그 사이 다른 인터럽트를 처리하지 못할 가능성이 있다. 그래서 곧바로 실행해야 하는 처리만 핸들러 안에서 실행하고, 나머지 처리는 DPC를 사용해 다음에 실행하도록 한다. DPC의 IRQL은 Passive보다 상위이므로 다른 인터럽트 처리가 모두 끝난 후에 실행될 것을 보장해 준다.
마찬가지로 애플리케이션이 준비한 버퍼에 데이터를 써넣는 처리에서는 그 프로세스의 가상 주소 공간에 접근할 수 있어야 한다. 이러한 경우 APC를 이용해 그 프로세스의 스레드로 데이터를 써넣도록 한다.
일반적인 애플리케이션을 개발할 때는 이런 인터럽트 요구 레벨을 의식하면서 프로그래밍을 할 필요는 없지만, 장치 드라이버를 개발하는 경우에는 매우 중요하다. 예를 들어, 하드웨어 인터럽트는 페이지 부재 예외보다 우선도가 높기 때문에 실제 메모리가 할당되지 않은 주소에 접근해도 페이징에 의한 자동 할당이 이루어지지 않는다. 그 때문에 메모리에 접근할 때는 실제 메모리가 할당되었음을 보장해 주는 영역에만 접근할 수 있다.
제가 생각하는 Windows와 Linux의 Interrupt의 가장 큰 차이는,
Windows 에서는
① CPU가 각각의 인터럽트 처리기의 주소를 얻어올 때는 메모리에 있는 ‘인터럽트 디스크립터 테이블(IDT)' 이라는 영역을 참조한다. IDT(Interrupt Descriptor Table)는 인터럽트 처리에 관련된 Descriptor를 모아둔 테이블입니다. 여기에는 인터럽트 처리기가 존재하는 주소를 나타내는 셀렉터 값과 오프셋, 속성을 저장한 ’인터럽트 디스크립터‘가 인터럽트 번호 순서대로 나열되어 있다. 운영체제는 시작 시 이 테이블을 생성해서 선두 주소를 IDTR 레지스터에 저장해둔다. 인터럽트가 발생하면 CPU는 이 레지스터를 참조해 인터럽트 디스크립터 테이블의 주소를 얻고, 인터럽트 번호에 대응하는 엔트리에서 인터럽트 처리기의 주소를 구한다.
즉, IDT(인터럽트 디스크립터 테이블) + 오프셋, 셀렉터 값, 속성으로 메모리 공간의 ISR(INT3의 인터럽트 핸들러)의 위치를 찾아간다.
② 우선순위가 낮은 인터럽트는 보류 한다는 것이다.
인터럽트에 관해 주의할 것은 어떤 인터럽트를 처리하고 있는 도중에 다른 인터럽트가 발생할 가능성이 있다는 것이다. 이런 경우에 단지 나중에 발생한 인터럽트를 보류하도록 하면 중요한 인터럽트 처리가 늦어질지도 모른다. 그래서 Windows NT/2000/XP에서는 하드웨어 인터럽트나 소프트웨어 인터럽트에 대해서 인터럽트 요구 레벨(IRQL)이라고 하는 우선순위를 정의하고, 그에 따라 인터럽트 중첩 허락 여부를 결정한다. (선점성)
Linux 에서는
① 요청을 한 뒤 다른 더 유용한 작업을 하고 요청한 작업이 끝나면 장치로부터 인터럽트를 받는 것이다. 이런 설계를 사용하면 여러 장치에 동시에 작업을 요청하는 것이 가능하다. 즉, Linux IDT 테이블 주소 값을 저장하고 IDT 테이블은 256개의 엔트리에 있는 interrupt 주소로 바로 간다(즉, 엔트리에 시스템 호출이 발생할 때 호출되는 system_call() 함수가 등록 되어있다) -> ISR을 찾아간다. (비선점성)
추가 참고 내용
리눅스 - IRQ 인터럽트 처리 과정
IRQ 인터럽트를 처리하는 방식은 프로세서마다 다르다. 예를 들어, i386 계열 프로세서는 내부에 있는 인터럽트 벡터 테이블 안에 IRQ 인터럽트 벡터가 포함되어 있어서 인터럽트가 발생하면 해당 벡터 테이블에 의해 인터럽트 처리 루틴으로 분기(branch)되는 데 반해, ARM 계열 프로세서는 IRQ 벡터가 하나이므로 인터럽트 제어 장치를 통해 인터럽트 처리를 재분기한다. 이렇게 프로세서마다 인터럽트를 처리하는 방식이 다르기 때문에 리눅스 커널에서는 동일하게 처리하기 위해 IRQ 인터럽트는 모두 do_IRQ() 함수를 호출하여 처리하도록 하고 있다. 이 do_IRQ() 함수는 오직 IRQ 처리만 담당하며, irq_desc 라는 전역 변수에 등록된 인터럽트 서비스 함수를 호출하는 구조로 되어 있다. 디바이스 드라이버나 커널에서 IRQ 인터럽트 처리가 필요한 경우에는 처리하고자 하는 IRQ 번호에 해당하는 인터럽트 서비스 함수를 irq_desc 전역 변수에 등록하면 된다. 그리고 더 이상 처리가 필요 없다면 irq_desc 에 등록된 인터럽트 서비스 함수를 제거하면 된다.
그림에서 보듯이 IRQ 인터럽트 서비스를 처리하기 위해서는 request_irq() 함수를 이용하여 처리하고자 하는 IRQ 번호와 서비스 함수 주소를 등록한다. 인터럽트가 발생하면 커널은 아키텍처마다 고유의 IRQ 검출 루틴을 이용하여 발생된 인터럽트의 IRQ 번호를 획득하여 do_IRQ() 함수를 호출한다. 이렇게 호출된 do_IRQ() 함수는 irq_desc 전역 변수에서 IRQ 번호에 맞게 등록된 인터럽트 서비스 함수를 찾아보고, 등록된 함수가 있다면 호출한다. 이때 request_irq() 함수를 이용한 등록 과정에서 참조된 데이터의 주소나 값들은 인터럽트 서비스 함수의 매개변수인 dev_id에 전달된다.
더 이상 인터럽트 처리가 필요 없는 경우에는 free_irq() 함수를 이용해 irq_desc 전역 변수에 등록된 인터럽트 서비스 함수를 제거한다.
소개글