Servicing Interrupt Study+more

servicing interrupt
자아. 슬슬 읽어가면서 정리를 해 봅시다.


인터럽트가 뭐 하는건지는 알지? 하드웨어가 신호를 보내는거지 뭐..
(아 뭐, 시퓨가 하드웨어에 인터럽트를 요청할수도 있는거 같긴 한데..)

어쨌든, 드라이버는 인터럽트를 서비스하기 위해 한개 이상의 인터럽트 서비스 루틴을 가질수 있다는군. 좀더 부가적으로는 시스템이 인터럽트를 받으면 인터럽트 서비스 루틴(이하 ISR)을 콜 한다고 하네.

PCI 2.2 이전의 포트나 버스는 line-based 인터럽트를 발생시킨다고 한다. 이게 뭐냐면 말이야. 디바이스에서 시스템에꼽혀있는 선 중에서 하나를 인터럽트 선 으로 쓰는거야. 그래서 인터럽트를 발생시킬때에는 그 선(line)으로 전기 신호를보내는거지. 그러면 시스템이 '아 인터럽트가 발생했구나'하고 아는것이고. 윈도우 비스타 이전의 윈도우들은 line-based인터럽트밖에 지원하지 못한다네.

어? 그러면 다른 방식의 인터럽트도 있는건가? 응 여기 적혀져 있어. PCI 2.2부터는 message-based 인터럽트가 가능하다네. 이게 뭐냐고? 응.. 잠만,
이건 뭐냐면, 특정 주소에 어떤 내용(data value)를 적어 넣음으로서 인터럽트를 발생시키는건가 본데.. 정확하게는 언급이안되어 있네. 다만, 윈도우즈 비스타 이후 버전부터는 line-based랑 message-based 인터럽트를 둘다 지원해준다는구만.

궁금하면, InterruptService랑 InterruptMessageService 함수를 찾아 보시라구-0-

ISR을 등록하려면 IoConnectInterruptEx 함수를 쓰면 된다고 합니다. 얘는 뭐냐면, 비스타 이후의 윈도우즈에서지원하는 ISR등록 함수인데, W2K이상에서도 쓸수는 있답니다. 어케? iointex.lib(iointex.h)를 사용하면된다는데? 나도 정확히는 모르겠어. 내가 코드를 작성해 봤어야지..- _-; (아참, ISR을 등록한다는 말은, 디바이스에서인터럽트를 발생시켰을때 인터럽트를 처리할 루틴을 정한다는 말이야.)

뭐, 디테일한건 스킵하고, ISR을 등록했으니 해제(해지-.-?)도 해야겠지?
만약에 ISR을 해제 안하면 어케 대냐구? 음.. 뭐 이런거야. 니가 세금을 자동 이체 되도록 해 놨어(ISR 등록), 그래서매달 세금이 빠져 나가고 있지(인터럽트 발생시 처리), 근데, 니가 이사를 간거야(드라이버 해제). 근데 근데... 아뿔사-0-세금 자동 이체되는거 주소를 안 바꿨네?!(ISR 해제 안함).  걍 니돈 깨지는거지 뭐. 드라이버쪽에서 보면, 뭔지 몰라도 큰일이 날게고(정확히는 ISR루틴을 해제 했는데, 인터럽트가 발생하면 등록되어 있는 ISR루틴을 처리하려 할테고.. 근데해제했는데? 남은건 뭐? System C!R!A!S!H!)

아.. 뭔말하려다가 이 말을 했더라? 아. 그래 ISR 해제. IoConnectInterruptEx에 대응되는 함수로IoDisconnectInterruptEx가 있대. 근데.. 얘가 인자(매개 변수)를 하나 받는데 그게 중요한 건가봐.. 잠깐만문서좀 더 보고.. 음.. 그니까, ISR을 등록할때 어떤 정보를 저장해 둬야 하는데, 아웅.. 설명하려니 귀찮다. 그냥 별로안 중요한 매개 변수다 하고 넘어가자. 어차피 쓸때 되면 알게 될거 같으니.. - _;

인터럽트 친화성(affinity)에 대해서도 짧게 언급되네. 아 근데, 친화성이 뭐냐고요-0-. 눈에 자꾸 밟히는게 친화성인데, 이렇게 적어놓으면 누가알아!!. 근데 책에도 설명이 없네. 이상하다 본거 같은데.. 쩝..- _;
이거 내 머리속에 있는거 이야기 하면 신빙성이 매우 떨어지는데.. 어쩌지? 그냥 '그럴지도..' 라고만 생각해 줘..-;;. 그친화성이 뭐냐면 말이야. 시퓨가 2개가 있다고 쳐. Amy랑 Bread라고 이름을 지어주자. 그리고 OTL이라는 디바이스가있다고 할때. OTL의 인터럽트를 '에이미'랑 '브레드' 중에서 언놈이 처리해 줄지를 정해놓은거야. '항상'인지 '가능한한'인지는 잘 모르겠다. OTL이라는 디바이스랑 '친한'녀석이 처리해 주는거지. 그걸 바로 친화성이라고 하는듯 해.

어쨌든, WDK문서에는 이렇게 언급되어 있어. '윈도우즈 비스타 이후 버전에서는 친화성과 priority를 레지스트리를 이용해서 설정할수 있다.'... 뭐. 그런가 보지 - _- (나랑 별 관계 없는 이야 같은데..)
근데, 이걸로 끝이야. 더이상 별 이야기는 없어. 어떻게 레지스트리를 바꾸는지에 관한 이야기만 있는거 같아. 그래서 이번에도 스킵이야.

흠흠.. 잘 알고 있듯이 우리의 윈도우즈(MS의 윈도우즈 인가..- _;)는 선점형 운영체제야. 이게 뭔 말인지는 알지? 몰라-0-? 수업시간에 잤냐? 잤어?- _-
간단하게, 존내 급한놈이 덜 급한놈보다 볼일을 빨리 볼수도 있다. 는 거야. 먼 말인지는 몰라도 감은 오지? 어쨌든. 그래서 ISR도 선점이 가능해.
이게 뭔소리? 인터럽트가 서비스 되고 있는 도중에 새로운 인터럽트가 서비스 될수도 있다는 말이야. 이런 상황 상상 가능해?'에이미'와 '브레드'라는 CPU가 존재하고 OTL드라이버가 '도레미파솔라시도'를 출력해주는 ISR을 서비스 하고 있었단말이야. 근데 '에이미'가 OTL의 인터럽트를 요청하고 거의 동시에 '브레드'가 OTL의 인터럽트를 요청하면'도레미도레미파솔라시도파솔라시도'..이렇게 되는거야.. 아놔. 이상하잖아. 안그래-.-? 이거 막으려면 어케해? 어케하긴..동기화 처리를 해 줘야지. 문서에는 ISR호출하기 전에 스핀락을 걸어주고, ISR이 리턴하면 스핀락 풀어주라고되어있어.(드라이버에서 인터럽트를 일으키면 시스템이 해당하는 ISR을 찾아서 서비스 해 준다고 했었지-0-?)

음.. 위 예에서 설명이 부족한거 같아서 좀더 덧 붙일께. 시퓨가 2개 이상이 존재하면 말 그대로 '동시에' 어떤 루틴을호출하는게 가능해. 얘네는 순서고 뭐고 없어- _-; 걍 동시에 가는거야. 쓰레드는 존내 미세하게 보면 그래도 순서가 있긴하거덩-0-(내가 아무리 빨라도 밥먹으면서 세수할수는 없잖아- _), 근데 얘네는 각자 '세수'하고 '밥먹'고 하면 동시에가능하잖아. 그런 차이가 있어.

음. 그럼 이제 ISR루틴이 뭘 하는지 볼까? 어차피 오늘은 개념 잡는정도이기 대문에 세부적으로 들어가지는 않아. 그래서 깐딴하게 ISR루틴이 뭘 하는지만 볼거야. 문서에는 이렇게 적혀 있어(근데, 나 영어 못한다는거 알지?)
한 가지는, 디바이스가 이상한 ISR루틴을 호출했으면 바로 FALSE를 리턴하라는 거고.(정확히는, ISR루틴이 지원하지 않는 디바이스가 인터럽트를 걸었다면, 곧바로 FALSE를 리턴해라. 야)
다른 한 가지는, 필요한 처리(인터럽트 클리어, 디바이스 컨텍스트 저장, 낮은 IRQL에서 처리할 필요가 있는 IO루틴 처리를위한 DPC큐잉(?) 등)를 하고 TRUE를 리턴하는거야. 후음.. 잘 모르겠다. 나중에 다시한번 봐 보자구.

오오..오오.. DPC Object에 대한 소개가 나와있어. 아놔 나 진짜 DPC가 뭔지 궁금했거덩. 이제 좀 봐 보자구.으응.. 그니까. 이런 말이야. ISR루틴이 인터럽트 서비스를 하는거잖아. 근데, 보자구. 시스템에 물려있는 디바이스가 쫌많아. 실제 우리가 삽입하는 카드는 몇개 안되지만.. 보드에 물려있는거며 기타 등등해서 겁나게 많나봐. 게다가 각 디바이스가인터럽트를 한두번 발생시키나? 아니란 말이지. 존내 많이 바쁘게 인터럽트가 발생하고 처리되고 하는데.. 인터럽트 루틴이 시간을많이 먹으면 안될거란 말이지. 그래서 Deffered Procedure Calls(DPC)라는걸 만들었데. 얜 뭐냐면,ISR에서 필수 연산을 처리한 다음에 찌꺼기를 맏아서 처리하는얘야. 근데, DPC라는 녀석은 DPC Object라는 녀석과연관이 있다고 하네. DPC Object는 시스템에서 생성해 주는 녀석인데 디바이스 오브젝트마다 하나씩 있다고 하네. 그럼 얜언제 초기화가 되냐-.-? 하면.. 드라이버가 DPC루틴을 등록할때 초기화가 된데. 얘를 더 알고 싶다고.. 안되. 얜 문서화가되어 있지 않데. 문서화 안되있는건 어째라-.-? 궁금해 하지 마라.. 이게 진리야. 하나더, DPC object를 프로그래머가추가적으로 생성할수도 있다네.(CustomDPC 루틴으로 검색해 보삼)

호.. DPC루틴에 대해 좀더 자세히 언급되어 있군. ISR은 보통 최소 한개의DPC루틴(DpcForIsr/CustomDPC)를 가지는데, 얘는 interrupt-driver I/O처리를 완료하는 역할을해. 보통은 다음과 같은 역할을 하는군.
-ISR이 시작한 I/O 연산을 완료함.
-다음 IRP를 디큐잉 해서 드라이버가 처리하도록 함.
-가능하다면 현재 IRP를 처리함.
그리고 DPC루틴은 DISPATCH_LEVEL에서 수행하는거 잊지 말고 말이야.

자아.. 밥도 먹었으니 졸지 않으려면 부지런히 적어야겠군.
내가 앞에서 DPC루틴이 두개로 나뉜다고 얘기 했었나? DpcForIsr하고 CustomDpc로 말이야. 두녀석이 초기화 하고큐잉하는데 약간의 차이가 있단 말이야. 자세한 용법은 관련 책(윈도우즈 드라이버 모델)을 참조하고, 나는 WDK에 나와있는내용을 내 나름의 해석으로 말해주겠어. 즉. 절대 신용하면 안된다는 말이지(-.-;)

어쨌든, DpcForIsr은 두가지 함수를 써서 초기화(등록)를 하고 큐잉을 해.
IoInitializeDpcRequest 함수는 DpcForIsr루틴을 등록하는 역할을 하는데 보통 AddDevice루틴이나 IRP_MN_START_DEVICE를 핸들링하는 디스패치 루틴에서 콜을 하지.
그리고 나서 ISR루틴에서 리턴하기 전에 IoRequestDpc를 콜 하면 DpcForIsr루틴이 큐에 삽입이 되는거지. 그러면 큐에서 기다리다가 찬찬히 처리 될거고 말이야.

근데, 얘네를 톡 까놓고 보면 말이야. IoInitializeDpcRequest함수는 내부적으로 KeInitializeDpc를콜하는거 같어. WDK문서에 그런식으로 그림이 그려져 있걸랑-.-; 또, IoRequestDpc는 내부적으로KeInsertQueueDpc를 호출하고 말이야. 이거 믿을만한 정보냐고? 아니.. 당연히 믿을만 하지는 않지, 근데..

CustomDpc 루틴을 초기화 하고 큐잉하는 다이어그램을 보면 말이야. KeInitializeDpc로 등록하고,KeInsertQueueDpc로 큐잉하도록 되어 있더란 말이야. 어? DpcForIsr에서 콜한함수들(IoInitializeDpcRequest, IoRequestDpc)이 내부적으로 콜 한 얘들이네..-0-. 뭐. 대충그리해서 저런 막무가내 추측을 한거야. 물론 별로 신빙성은 없지만 내가 이해하기론 그랬어.

아. 근데 이거는 알고 있어? 만약에 동일 디바이스에서 인터럽트가 연속적으로 발생한거야.(overlapped I/O) 그니까..이런식이지.. OTL디바이스에서 KIN이라는 인터럽트가 2번 발생했다면, 어쩌구 저쩌구 해서 ISR에서IoRequestDpc던지 KeInsertQueueDpc루틴이 호출되겠지? 그것도 2번이나.. 그러면 DPC가 두번 큐잉될까?큐에 동일한 DPC object가 2개나 존재하게 될까? 아니야. 두번째 큐잉한 DPC object는 무시되어 버려. 어엇!그러면 어떻하지?
-A DpcForIsr or CustomDpc routine that can complete some driver-maintained count of outstanding requests each time it is called.
-An ISR that never overwrites the context information that it passes toa DpcForIsr or CustomDpc routine, until that routine has used thecontect information and completed that IRP to which the contextinformation belongs.
-A SynchCritSection routine that accesses the ISR's context area on behalf of the DpcForIsr or CustomeDpc routine.
..을 따라야 한다고 하는데.. 흑흑.. 번역이 안되네.. 아흑 눈물난다. 누가 해석좀..ㅠ_ㅜ

에? 윈도우 비스타 이후의 버전에서만 지원하는게 있네.. Threaded DPC라고 하는건데.
이런 비교가 있군. 보통의 DPC는 모든 쓰레드에서 선점해서 실행할수 있다고 하네. 즉 이런거야. 큐잉된 DPC가 한 백만개라고하자. 근데, 지금 겁나 급한 쓰레드가 동작하고 있는데, IRQL이 디스패치 레벨이 되어서 DPC를 실행한단 말이야. 그러면겁나 급한 쓰레드가 선점을 당하게 되. 즉 겁나 급한 쓰레드가 멈춘단 말이지. 그리고 DPC를 디큐잉 해서 처리를 하는거야.백만개를.. 아놔. 이거 제정신이야? 물론 큐잉된 DPC가 백만개가 될리는 없지만, 위와 같은 문제가 생길수도 있단 말이야.
근데 비스타에서는 역 발상을 했어. 아놔.. 역 발상.. - _-.. 왠지 테레비에서 많이 들었던 단어인데.. 쩝.. 어쨌든.
비스타에서 적용한 역 발상은 뭐냐면,  DPC가 겁나 급한 쓰레드에 의해 선점 당한다?! 라는거야. 아 뭐, 물론 겁나 급한쓰레드가 진짜로 겁나 겁나 겁나 급해야(only real-time threads) 하기는 하지만 말이야.

얘를 써서 더 낳아지는게 뭐 있냐구? 아. 이사람이- _-. 자자.. 군사 시스템이라고 생각을 해 보자구.(뭐.. 군사시스템에서 윈도우를 쓰는지는 모르겠지만 말이야.). 아놔, 그래서 지금 막 미사일이 한국으로 날아오고 있는거야. 그래서 요격을해야 하는데. 막 연산을 할꺼 아냐. 좌표 계산하고 속도 뭐 기타등등.. 존내 급한 연산이잖아. 아마 real-time 정도의priority일텐데, 그때 IRQL이 디스패치 레벨로 내려와서 미사일 요격하기 위해서 연산중인 애(쓰레드)를밀쳐내고(선점하고) DPC처리를 한다? ... 이러면 임마 너나 나나 다 죽어- _-. 이러면 안대겠지? 뭐, 이런 경우에쓴다고 생각하면 되는데, 대충 서버쪽에서 미션 크리티컬한 부분에서 쓸모가 있나 보더라구.

엄.. 후우.. 앞으로도 가야할 길이 까마득 하구나-0-. 어쨌든 한번 달려온길 이제와서 멈출수는 없지.
다음은 DPC루틴 작성법이야. 잠만. 쫌 읽고나서 계속 하자구... 아웅.. 등에 난로가 있어서 따시네.. 아웅 잠 쏟아진다..쩝쩝..

크흥.. 이거 뭐 작성법이라고 하기도 쫌 그런데- _-. 읽어보니까 DPC루틴에서 할수 있는일.. 정도랄까? 한번 보자구.
-time out으로 종료되었거나 failed된 연산을 재 시도.
-에러 로그 패킷을 셋팅해서 디바이스 I/O 에러 로그를 기록.(할수 있다는거 같은데.. 모르는 내용이니 패~쓰~)
-Buffered I/O라면 IOCTL일때 디바이스에서 데이터를 읽어서 Irp->AssociatedIrp.SystemBuffer에 적어 놓는 작업을 IRP완료 전에 한다던지..
-Direct I/O라면 대용량 전송을 잘게 쪼개서 어디까지 전송했고 다음에는 어디서부터 전송해야 한다는 정보들을 저장해 두고 SynchCritSection 을 이용해서 다음 전송을 한다던지..(이거 맞는 말이야-.-?)

그 다음에 packet-based DMA일 경우에 어쩌고, Programmed I/O일 경우에 어쩌고,ControllerContgrol루틴을 포함한 non-WDM드라이버라면 어찌어찌한 처리를 해라 라는 글이 있는데, 뭐.. 이건할수 있는 일이라고 보기엔 조금 무리가 있어서 패쓰.

하튼. 중요한건 ISR에서 중요한 처리를 한 다음에 DPC루틴에서 IRP의 I/O 처리를 도맡아서 한다는거야.

크아악! DPC 루틴을 작성하기 위한 가이드 라인.. 아놔! DPC루틴 작성법하고 뭐가 틀려-0-!!! 아.. 내용은 틀린데.. 제목보고 하는 말 이었어. 아웅..
-DPC루틴이 물리 디바이스에 액세스하거나 드라이버의 공유 자원에 엑세스 한다면 동기화 처리를 해 줘야해. 음..KeSynchronizeExecution함수를 사용해서 SynchCritSection 콜백 함수를 지정해 주면 콜백 함수에서공유 자원에 액세스를 할수 있게되다고 하네. (실제 어떻게 동작하는지는 애매... 하긴 한데.. 저렇다고 하니뭐.. 쩝..)
-DPC루틴은 DISPATCH Level 의 IRQL에서 동작한다고해. 그래서...뭐? IRQL은 레벨이 올라갈수록 사용할수있는 함수들이 제한적이야. 그리고, pageable한 메모리에도 엑세스하면 안되고 말이야. 물론 pageable한 메모리를allocate하는것도 당연히 안되. 게다가 KeWaitForSingleObject같은 커널 디스패처 오브젝트를 대기하는 함수도쓸수 없어. 다만 한가지. 스핀락을 요청/반환하는 KeAcquireSpinLockAtDpcLevel/KeReleaseSpinLockFromDpcLevel 함수는 쓸수 있다고 하네.
-다음 I/O 연산을 시작할 책임이 있다네.
-아.. 이건 좀 긴데.. 번역한대로 휘 갈겨줄께. 만약에 말이야 드라이버가 DMA를 사용하고 있고AdapterControl루틴이 KeepObject나 DeallocatedObjectRegisters를 반환하면, DPC루틴은adaptor object나 map registers를 해제해야할 책임이 있다네.
-요청을 처리하다가 에러가 나면 에러 로깅을 해야할 책임이 있고, 가능하면 재시도(retry)를 해야 한다네. 쩝..
-최 하단 물리 디바이스 드라이버가 controller object를 setup하면 DPC에서 해제해야할 책임이 있다는군.
-overlapped I/O 연산이면 overlapped I/O 처리 방법을 따라야 한다는데. 그게 뭐냐곳!!!(아마, 내가 해석하지 못한 그것일게야- _-)

흐헉-0-. 갈수록 뭐가 많아진다. 아놔. 이제 세부적인 작성법은 때려치우고 개념만 잡고 넘어가도록 하자. 시간을 너무 많이 빼앗기고 있어- _-

SynchCritSection 루틴에 관한 이야기야. SynchCritSection은 배타적 접근을 위한 코드 쪼가리 라고생각하면 된다는군. 얘가 어케 동작하냐면 말이야. 시스템이 얘를 call하면 현재 프로세서의 IRQL을 DIRQL로 올린다음.스핀락을 얻는다. 그러면 어떤 일이 생기냐면 말이야. priority가 현재 디바이스보다 높지 않는 얘들이 인터럽트를 걸어봤자무시된단 말이야. 또한 멀티 CPU환경에서도 딴 CPU가 요청한것도 무시하고 말이야. 해서, SynchCritSection루틴은공유자원과 하드웨어 자원을 유유히 만지작 만지작 한다음 리턴을 한단 말이야. 그러면 시스템이 원래 IRQL로 되돌려 주는거지.

... 이 문서 뭔가 문제있는거 아냐? 이제와서 하드웨어 priority를 이야기하면.. 뭐 어쩌자고? 아.. 미치겠네. 쩝.
-PASSIVE_LEVEL: 인터럽트가 마스크 되는것 없음... 이라고 적어놓으면 먼 말인지 알려나? 한마디로 개나소나 인터럽트 걸수 있는 말.
    호출되는 드라이버 루틴 - DriverEntry, AddDevice, ReInitialize, Unload, 대부분의 디스패치 루틴, 드라이버가 생성한 쓰레드들, 등
-APC_LEVEL: APC_LEVEL 인터럽트는 마스크됨.
    호출되는 드라이버 루틴 - 몇개의 디스패치 루틴(알아서 찾아보삼- _;)
-DISPATCH_LEVEL: DISPATCH_LEVEL과 APC_LEVEL 인터럽트는 마스크됨. 디바이스, 클럭, 전원 failure 인터럽트 발생 가능.
    호출되는 드라이버 루틴 - StartIo, AdapterControl, AdapterListControl,ControllerControl, IoTimer, Cancel(cancel 스핀락을 잡고 있는 동안), DpcForIsr,CustomTimerDpc, CustomDpc
-DIRQL: DIRQL이하의 모든 인터럽트는 마스크됨. 클럭, 전원 failure나 더 높은 DIRQL값을 가진 디바이스의 인터럽트는 가능.
    호출되는 드라이버 루틴 - InterruptService, SynchCritSection

크헝-0-.. 이걸로 Servicing interrupt 챕터는 끝난건데 뭔가 많이 비는거 같..은 느낌은 나만의 착각일거라고 치부하고.. 다음 챕터로 궈궈-0-
#####################################################################
#####################################################################
이번에는 동기화 챕터를 해 보도록 하자굿. 오늘 계획이 동기화 챕터라서 어쩔수 없삼ㅋㅋ;
커널에 정의되어 있는 커널 디스패처 오브젝트(줄여서 디스패처 오브젝트)는timer/event/semaphore/mutex/thread object등이 있다고 하고, 드라이버는 이 디스패처 오브젝트를PASSIVE_LEVEL의 non-arbitrary 쓰레드 컨텍스트에서 동기화에 사용할수 있다고 적혀있삼.

커널에 정의된 디스패처 오브젝트는 signaled와 non-signaled 두가지 상태를 가질수있으며, non-signaled는어떤 루틴이 임계영역(critical section)에 진입해 있다는 의미이며(말이 좀 이상하지만 이해는 되지-.-?)signaled는 어떤 루틴도 임계 영역에 진입해 있지 않다는 의미가 되겠다.(그러니까, signaled 상태는 '임계 영역들어가고 싶은 놈은 나 델꼬가주~' 하고 신호를 보내고 있다는 말이다)

대스패처 객체를 얻기위해서는 KeWaitForSingleObject를 쓰는거고.. 이거 뭐 유저영역 동기화랑 거의 비슷한데- _;

트랙백

이 글과 관련된 글 쓰기 (트랙백 보내기)
TrackbackURL : http://handmade.egloos.com/tb/4048855 [도움말]

핑백

덧글

덧글 입력 영역