윈도우 인터널즈
시스템 메커니즘 05 - APC
묭묭.cpp
2024. 10. 23. 12:09
시스템 메커니즘 05 - APC
비동기 프로시저 호출(APC) 인터럽트
비동기 프로시저 호출(APC, Asynchronous Procedure Calls)
- 유저 프로그램과 시스템 코드가 특정 유저 스레드 컨텍스트에서 실행되게 해줌
- 즉, 특정 프로세스 주소 공간 내에서 실행됨
- 스레드 스케줄링 규칙에 종속되고 DPC와 동일한 환경에서 동작하지 않음
- 즉, DISPATCH_LEVEL에서 동작하지 않음
- 높은 우선순위 스레드에게 선점될 수 있음
- 블로킹 대기가 가능
- 페이징 메모리 액세스 가능
- APC_LEVEL(1) IRQL에서 동작
- 일종의 소프트웨어 인터럽트
APC 큐
- APC는 APC 객체라는 커널 컨트롤 객체에 의해 기술됨
- APC는 커널이 관리하는 2개 중 하나의 APC 큐에 머뭄
- APC 큐는 스레드 별 존재
- 커널 APC 큐와 유저 APC 큐 총 2개가 존재함
- APC를 큐에 추가하는 요청
- 커널은 APC 모드를 확인(커널인지 유저인지)
- APC 루틴을 수행해야 할 스레드의 적절한 큐에 삽입
2가지 APC 모드의 차이점
APC가 스레드에 추가됐을 때의 스레드 상황
- 스레드가 동작 중임(현재 스레드임)
- 스레드가 대기 중임
- 스레드가 다른 동작 중임(레디 또는 대기 등)
스레드 스케줄링을 생각
- 스레드는 대기 동작을 할 때마다 깨어날 수 있는 상태에 놓이게 됨
- 커널 APC의 경우
- APC가 완전히 비활성화되지 않았다면 이 상태를 무시
- APC는 항상 대기를 취소함
- 유저 APC의 경우
- 스레드가 깨어날 수 있는 대기 상태이고 다른 유저 모드 구성 요소 대신 인스턴스화될 때 또는 다른 대기 중인 유저 APC가 대기를 끝내고 있는 경우에만 스레드에 개입 가능
- 여러 개의 프로세서가 같은 스레드에 APC를 주입할 때 발생
- 커널 APC의 경우
유저 APC는 이미 유저 모드에서 동작 중인 스레드에 개입하지 않음
- 유저 APC가 개입할 수 있는 상황
- 깨어날 수 있는 대기를 수행
- 링 전환
- 유저 APC 큐를 다시 확인하는 컨텍스트 스위칭
커널 APC는 대상 스레드에 인터럽트 가능
- IRQL을 APC_LEVEL로 높임
- 프로세서가 커널 APC 큐를 볼 수 있게 함
2가지 모드 모두 APC를 실행시키려면
- 스레드가 무언가 하고 있다면 동작 중 또는 대기 상태로 전이가 일어나야 함
- 이 결과로 중지된 스레드는 큐에 있는 APC를 수행할 수 없음
커널과 드라이버 개발자가 선택할 수 있는 메커니즘
IRQL을 APC_LEVEL로 유지, 코드 수행 중에 그 이상으로 레벨을 높이는 것
- 스레드가 수행 중이기 때문에 인터럽트는 전달될 수 있음
- 각각 IRQL 규칙에 의해 프로세서가 이미 APC_LEVEL 이상에 있다면 인터럽트는 사라짐
- 따라서 IRQL이 패시브 레벨로 떨어질 때만 APC 수행
KeEnterGuardedRegion API를 사용
- 인터럽트 컨트롤러 상태 변화를 일으키지 않기 때문에 선호
- KeLeaveGuardedRegion API와 짝을 이룸
- 스레드가 APC 전달을 받게 복구
- 재귀와 중첩 호출이 가능함
- 스레드의 컨텍스트 스위칭에도 안전
- 스레드 객체 구조체의 필드를 갱신하기 때문
- SpecialApcDiable 필드 갱신
APC_LEVEL에 있는 동안에도 컨텍스트 스위칭이 발생
- 디스패처는 IRQL을 KETHREAD의 WaitIrql 필드에 저장
- 프로세서 IRQL을 새로운 스레드의 WaitIrql로 설정(패시브 레벨일 것)
- 패시브 레벨의 스레드가 APC_LEVEL의 스레드를 선점하게 됨
- 스케줄러의 고려가 IRQL의 고려보다 앞서는 것
- 스레드 선점을 막는 것은 디스패치 레벨로 올려야 함
- 이때의 IRQL은 스레드를 대신함
- APC_LEVEL만 이 방식을 사용
- 스레드 로컬 IRQL이라고 함
커널 개발자에 의해 APC가 어떻게 비활성화되는지 상관없이 하나의 법칙이 가장 우선순위
- APC가 패시브 레벨보다 낮고 SpecialApcDisable이 0이 아니라면 코드가 유저 모드로 돌아오지 못함
- 즉시 버그체크 발생
- 이런 상황은 드라이버가 락을 해제했지만 가드 영역을 해제하지 않은 상황
APC 모드
유저 모드 APC를 삽입하는 일부 API
ReadFileEx, WriteFileEx, QueueUserAPC 등의 APC
- I/O 동작이 끝났을 때 호출되는 완료 루틴을 지정 가능
- I/O를 발생 시킨 스레드에 APC를 전달함으로 I/O 완료가 구현됨
- APC가 삽입된 시점에 바로 콜백이 호출되지 않아도 됨
- 깨어날 수 있는 대기 상태에서만 유저 모드 APC가 전달되기 때문
- 대기 함수에 이런 옵션이 있음
- 유저 모드 APC는 PASSIVE_LEVEL에서 동작 가능
반응형