사전 지식이 없었던 만큼 정말 어려웠던 내용이 였습니다.
고생 많으셨습니다. 별로 아는 것도 없으면서 수업을 진행하니 어려움이 많네요...^^ 4회 Windows internals 세미나 [수업 내용] http://maystyle.tistory.com/entry/지루한-인터럽트-한방에-알아보자 [진행] 민성 [참석] 준상, 동조, 민우형님, 준규선배, 조현정 과장님 [진도] p140 ~ p170 [교제 내용] 전체적으로 살펴 본다면 이렇게 정리 될 수 있습니다. Windows 는 IRQL 이라는 자체적인 우선 순위를 통해 인터럽트 혹은 여러가지 발생 가능한 요청 사항을 수행하게 됩니다. 이때 상위 레벨의 IRQL을 갖는 인터럽트가 수행된다면, 하위 레벨의 Thread는 컨텍스트 스위칭이 되면서 하위의 모든 IRQL 레벨은 마스킹 되게 됩니다. 그렇다면 프로그램들이 수행 될때 어떻게 IRQL을 이용하는지 설명하겠습니다. 만약 IRQL이 없고 그냥 평등하게 Thread가 수행되게 된다 라고 가정하게 되고 Memory Access 나 Hard Disk Access가 필요하게 되면 어떻게 될까요? 당연히 Access를 통해 Data를 로드하는 시점에서 해당 Thread는 멈춰야 합니다. 이를 보장하기 위해 보통 사용자 Thread가 수행되는 Passive 레벨 윗단에 APC라는 Memory를 Access 하기 위한 인터럽트를 두고 해당 인터럽트 발생시 유저 Thread는 실행을 멈추고, APC 단의 Thread 수행을 기다리게 됩니다. 이를 통해 한 Thread가 커널단... 즉 실제 memory 나 paging memory에 접근하는 로직을 수행할 수 있습니다. 그리고 윗단 DPC 이상은 예를 들면 IO 및 인터럽트가 발생하는 레벨인데, 이전에 설명했듯이 특정 IO가 발생할 경우 HAL은 해당 IO의 IRQL을 결정해 주고, 해당 IRQL에 맞춰 CPU는 인터럽트를 처리합니다. 그렇다면 DPC/Dispatch 는 뭘까요? 책에서 보듯이 해당 레벨은 소프트웨어적으로 발생하는 인터럽트입니다. 제가 설명드렸다 시피 IDT 테이블에는 연관되 일어나는 여러 인터럽트들이 정의 되어 있을 수 있습니다. 이때 한 인터럽트가 완전히 수행 될때 까지 다음 인터럽트가 기다린다거나, 조금 늦게 수행 되도 되는 인터럽트가 존제 한다거나, 기타 Thread의 수행 시간이 너무 길어질 경우 CPU의 점유 현상이 나타나고 이를 방지 하기 위해 일단 늦게 수행되도 괜찮은 경우 DPC 큐에 넣어 수행을 지연시킵니다. 그리고 윗단의 인터럽트가 종료 되면 DPC레벨에서 해당 인터럽트를 꺼내 수행하게 됩니다. |
APC들은 사용자 프로그램과 시스템 코드가 특정한 사용자 스레드 (특정 프로세스 주소공간)의 컨텍스트에서 실행되는 방법을 제공하는 인터럽트 이다. APC의 대기열은 스레드에 한정되어 있다. 전에 다른 적이 있지만 스레드는 커널 모드와 사용자 모드에서 실행 된다. APC들은 APC 개체라고 부르는 커널 제어 개체를 통해 설명되는데, 실행을 기다리는 APC들은 커널이 관리하는 APC 대기열에 상주한다. 즉 APC는 스레드가 같은 하나의 수행할 job에 대한 대기열로 볼수 있고, 커널이 이 APC 루틴을 수행한다. 즉 스레드의 실행은 APC의 실행으로 보면 된다. 먼저 실행부 (Process 나 메모리 스레드 I/O 보안 등을 관리 ntoskernl.exe)는 커널 모드 APC들을 사용하여 운영체제 작업이 특정 스레드의 컨텍스트에 있는 주소 공간 내에서 완료 되도록 수행된다. 즉 인터럽트를 일으킬 수 있는 시스템 서비스의 실행 정지 및 스레드간 메모리상에서 비동기 I/O지원 등을 제공한다. 장치드라이버들은 I/O 작업을 위해서 사용한다. 사용자 모드 APC는 패시브 모드에서 실행된다. |
Windows 의 NLB는 L4처럼 동작하는 것 처럼 보이지만 실제로는 브로드케스팅을 통해 모든 Node에 트레픽을 전달 하고, 해당 노드에서 NLB 규칙에 따라 해당 트레픽을 드랍 또는 처리하는 방식이므로 L4와 비교하면 않되고, 기능상 L4와 같이 사용할 때만 쓰셔야 합니다. 아시다시피 무엇보다 해당 서버 네트워크 속성에서 Network Load Balancing 부분의 Check가 선행 되어야 합니다. 이러한 설정이 모두 끝났다면 구현을 해봅니다. 주요 발생 에러 2. Router 넘어 단에서 해당 WNLN 서비스 IP 및 DNS Name으로 접근이 않된다. 주요 NLB 관련 Trouble Shooting 방법 은 다음 링크를 참조 합니다. |
인터럽트나 Exception이 발생하면 커널은 기존 처리 중 이던 Thread를 커널 스텍에 Trap Frame을 만들어 상태를 처리하고, Trap Handler를 호출하여 해당 Trap을 처리한다. ( 알수 없거나 처리 할 수 없을 경우 KeBugCheckEx를 호출 하고 정지 ) 덤프 미 생성시 블러스터 웜/ Disk Array Controller / CPU / Memory / 메인 보드 를 확인 해야 한다. 하드웨어 인터럽트 처리 이전 포스트들을 잘 읽어 봤다면, 용어에 대하여 별도로 설명할 필요는 없을꺼 같다.하드웨어 인터럽트가 발생하면 위와 같은 순서로 처리된다. 또한 CPU는 여러 ISR에 대하여, 개별 적으로 처리가 가능하다. PIC Windows 에서 인터럽트의 처리 (IRQL) IRQL에 대해서는 이전 포스트(http://maystyle.tistory.com/entry/IRQL)에서 다뤘다 시피 인터럽트에 대한 처리 우선 순이라 할 수 있다. 즉 더 높은 순위의 인터럽트는 낮은 순위의 인터럽트를 마스크(블럭 시키고 자기가 실행 된다.) 한다. 높은 인터럽트가 발생하면 기 실행 Thread는 저장되고 해당 인터럽트와 연결된 Dispatcher들을 실행 한다. 인터럽트가 발생 > CPU의 IRQL을 높힘 > CPU에서 인터럽트 처리 > CPU의 IRQL을 원상 복귀 시킴 중요한건 IRQL을 통해서 PIC와 통신 비용을 줄일 수 있다는 거다. (이 부분은 나만의 유추다) 일단 PIC는 3개의 레지스터를 가지고 인터럽트 프로세스를 진행한다. 커널 모드 스레드는 자신이 실행하려는 작업에 따라 IRQL을 높히게 된다. 그래서 장치 인터럽트시 커널 및 장치 드라이버는 왠만하면 Passive 수준을 유지하여, 인터럽트를 빠르게 처리하려 한다. 방금 언급한 것 처럼 PIC에서 정의된 IRQ와 IRQL은 다르다. 솔직히 Windows는 IRQ와 별계로 IRQL을 통해 서비스 우선 순위를 결정한다. 그렇다면 어떻게 누가 결정할까? 바로 HAL에서 HalpGetSystemInterruptVector를 호출하여 매핑한다. 이전 포스트를 참고 하자. http://maystyle.tistory.com/entry/인터럽트를-IRQL에-매핑하기 DPC 계층 이상에서 코드가 실행 중일 경우 CPU는 다른 스케줄러를 실행 할 수 없다. 없는데 그런 경우가 생기니깐 장애가 생기는 것이다. DPC는 이전 포스트를 참고 하자. (http://maystyle.tistory.com/entry/DPC-Deferred-Procedure-call) 원문을 참고 하자. 본문의 예를 들자면 DPC/디스패치 수준은 무조건 Nonpaged Memory에서 수행되는 것이다. 즉 DPC의 오브젝트들은 메모리에 저장이 되는데, 만약 Page Fault가 발생하게 되면 어떻게 될까? 그렇게 되면 분명 Disk에서 해당 object를 읽어 와야 하고, 또 다시 인터럽트들이 발생하게 된다. 그러면 다시 컨텍스트 전환이 일어나게 되며, 이는 on code running at DPC/dispatch level or above is that it can't wait for an object 를 어기게 된다. 이때 충돌 코드가 IRQL_NOT_LESS_OR_EQUAL이다. 순환 큐가 아닌 이상... 큐에 들어간 DPC는 수행되야 하며, 이에 컨텍스트 스위칭이 일어날 수 없다. 자 마지막으로 인터럽트 개체는 무엇일까? |
(출처 : Windows internals) This interrupt-object resident code calls the real interrupt dispatcher, which is typically either the kernel's KiInterruptDispatch or KiChainedDispatch routine, passing it a pointer to the interrupt object. KiInterruptDispatch is the routine used for interrupt vectors for which only one interrupt object is registered, and KiChainedDispatch is for vectors shared among multiple interrupt objects. The interrupt object contains information this second dispatcher routine needs to locate and properly call the ISR the device driver provides. The interrupt object also stores the IRQL associated with the interrupt so that KiInterruptDispatch or KiChainedDispatch can raise the IRQL to the correct level before calling the ISR and then lower the IRQL after the ISR has returned. This two-step process is required because there's no way to pass a pointer to the interrupt object (or any other argument for that matter) on the initial dispatch because the initial dispatch is done by hardware. On a multiprocessor system, the kernel allocates and initializes an interrupt object for each CPU, enabling the local APIC on that CPU to accept the particular interrupt. 위의 명령어 (dt nt!_kinterrupt) 는 ISR의 규정된 인터럽트 개체를 직접 보기위한 명령어 이다. |
디스패칭은 종종 한 스레드의 실행이 너무 길어져서 스케줄링을 다시해야하는데, 자발적인 대기상태 전환이나 종료가 않되는 경우 사용한다. 그런대 이 인터럽트도 여러 Thread로 나뉠 수 있다. 그리고 계중에는 중요하거나 우선 처리 않되도 되는 경우가 있을 수 있다. 이럴 경우 DPC를 이용하게 된다. 자 DPC가 어떻게 수행되는지 확인해 보자. 1. 먼저 인터럽트가 발생한다. |