으아아아!! 동기화 @#$%^&!!
오늘 오전에는 동기화 객체에 대해서 짧막하게나마 정리를 해 보도록 하잠.
우선 동기화 객체를 시작하기 전에, 디스패처란게 뭔가 하는 개념을 알고 넘어가야 한담. ... 그래서...
WDK문서에는 뭐라고 적혀있나 하고 봤더니..- _-. 걍 커널이 정의한 객체 타입들의 세트(a set of object types)를 디스패처 객체라고 부른다네.. 이게 뭐야 - _-
그리고 한다는 말이. 드라이버는 이 디스패처 객체를 동기화 메커니즘에 사용한다고.. 이게 뭐야- _-
인터넷에서 좀더 뒤적뒤적 거려보자... 관련 내용이 없다.. 췌. 그냥. 시그널 상태나 넌-시그널 상태를 가지는 객체로서 보통 동기화에 이용된다. 라고 알고 넘어가도록 하자.
아.. 좀더 읽어보니. 중요하다 싶은 말이 하나 적혀있네.
드라이버는 디스패처 객체를 동기화 메커니즘에 사용할수 있는데, 그게 PASSIVE_LEVEL의 IRQL에서 실행되고 있는 비-임의적(non-arbitrary) 쓰레드에서 가능하다고 하네.
다시 말하면, PASSIVE_LEVEL의 IRQL에서 실행되고 있는 비-임의적(non-arbitrary) 쓰레드에서 디스패처 객체를 이용하여 동기화 처리를 할수 있다는 이야기.
어쨌든, 그래서. 시그널/넌-시그널 이라는 말이 나왔는데, 이게 뭔 말이냐? 하면.. 시그널 상태는 쓰레드가 임계 영역에 진입할수 있는 상태이고, 넌-시그널 상태는 쓰레드가 임계영역에 진입하지 못하고 대기하고 있는거야.
실제로는 KeWaitForXxx 함수로 디스패처 객체가 시그널 상태가 될때까지 기다리게 하는거지.
이제, 실제로 동기화 메커니즘에 사용되는 디스패처 객체를 살펴보도록 하자.
우선 이벤트 객체... 후.. 내가 유저영역에서 동기화를 공부할때 무진장 헤멨던 놈이지 씁..
간단하게 말하면, 기다리고 있는 쓰레드들에게 이벤트를 주는거얌. 먼말인지 모르겠지?ㅋ.. 그럼 이제 자세하게 말해줄께..
에..또.. 예를 들어서 설명해야 이해하기 쉽겠지? 자자.. 이렇게 생각해 보자구. '피터', '브레드', '케인', '존스'라는 네 녀석(쓰레드)가 있어.
그리고, 얘네가 회사를 다니는데, 빨랑빨랑 회사 업무가 끝나기를 기다리는거야. 근데. 중요한건 이 회사는 좀 븅~ 회사라서 사장이 '끝났삼'이라고 말하기(이벤트) 전까지는 업무가 끝난게 아니야..(뭐 이따위 회사가.. - _-)
뭐. 대충 그렇다고 치고, 중요한건, 위에 언급한 네놈(쓰레드)중에서 한놈이 사실은 사장을 조종하는 배후야..(뭔가 스릴러의 냄새가..). 그래서 지 할일 끝내고 사장을 쿡 찌르는거지. 그러면 사장님이 '업무 끝났삼'이라고 말할거고, 업무가 끝나기를 기다리는 '피터'를 비롯한 찌질이 쓰레드들이 '아싸~'이러면서 지 할일(그게 뭐던지)을 하게 되는거지.
이런 경우를 '알림 이벤트'라고 해. (이벤트를 대기하고 있던 모든 쓰레드들이 이벤트가 설정[시그널]되면 자기 업무를 보는거지)
그리고, 이런 경우도 있어. 역시 '피터', '브레드', '케인', '존스'라는 네 녀석이 있는데, 이번에는 네 놈중에 '존스'가 '수잔'이라는 얘(이벤트)랑 사귀고 있어(소유중).
그러다가 '존스'가 '수잔'을 차 버린거야(set to 넌-시그널). 아놔 이런 개쉑. 어쨌든 그렇게 되서 애인 없는 찌질한 놈('피터', '브레드', '케인', 과 '존스'.. 이색퀴는 지가 차 놓고 애인 없다고 또 붙으려고 하는것 보게..- _-)이 '수잔'하고 사귀려고 막 대시(대쉬?)를 하다가 한놈이 '내가 수잔하고 사귄다'고 말해버리는거야.
쩝.. 그럼 나머지 녀석들은 또 수잔이 헤어지길 기다리는거야. (수잔이 먼저 차버리면 안대냐고? 여기선 안대.. 짤 없어 ㅋ;)
이런 경우를 '동기화 이벤트'라고 해. (이벤트를 대기하고 있던 모든 쓰레드들 중 하나의 쓰레드가 이벤트를 받으면 이벤트 객체는 넌-시그널 상태로 변환됨)
그 담으로 우리가 걸핏하면 듣게되는 '상호 배제'를 의미하는 뮤텍스(mutual exclusion의 줄임말)를 보도록 하자굿. 근데, 이게.. 무진장 헷갈리는게.. 동기화 이벤트랑 동작하는게 똑 같그덩-.-..
해서 난 아직도 뮤텍스랑 동기화 이벤트랑 먼 차이가 있는지 모르겠어. 누구 아는사람 손좀 들어주삼.
그래서 인터넷을 쵸큼 돌아다녀 보니 이런 글이 있네, 이벤트랑 커널 뮤텍스(즉, 우리가 아는 그 뮤텍스)는 비스꾸름 하다고 하네.
근데, 차이는 뭐냐면, 뮤텍스는 뮤텍스를 소유한 놈이 뮤텍스를 다시 소유하려고 해도 가능하다는거얌. (그럼 이벤트는 이 짓이 안되는건가 보네.. 안될거 같긴 하다만..)
그리고 이게 쫌 기억해야 할 내용일거 같은데, 얘는 부작용이 있다네. 근데 번역이 안되서 먼 말인지는 잘 모르겠어.
대충 발 번역을 해 보자면, 뮤텍스는 소유한 얘가 다시 요청을 할수가 있잖아. 그러면 뮤텍스의 내부 카운터를 증가시키게 되고.. 뭐 그런데, 이렇게 동작하면 KeEnterCriticalRegion함수를 콜 한것처럼 커널 APC가 disabled된다네. 이게 뭐.. 임의적 재 진입(arbitrary re-entrancy)를 막기 위해서라는데.. 뭔 소린지 모르겠으니 좀 알리조-0-
그리고, 카운트를 가진 뮤텍스를 의미하는 세마포어. 쩝.. 여기서 카운트를 가진다는 의미는 위에서 뮤텍스의 재 진입 카운트와는 다른 말이라는건 알고 있지? 세마포어가 가지고 있는 카운트가 의미하는건 동시에 실행할수 있는 쓰레드의 수를 의미해.
뭐, 이런거야. 세마포어 카운트가 3으로 초기화가 되어 있는데, 2놈의 쓰레드가 임계영역에 진입 할려고 한단 말이야. 그럴때 세마포어 카운트가 2 감소해서 1이 되는거지. 0이 되면 넌-시그널 상태가 되서 쓰레드들이 대기하게 되는거고... 세마포어는 이 정도네..
흠.. 잠깐, 그러면, 세마포어에서 카운트가 1이면.. 뮤텍스랑 똑같은거야?-.-?
에.. WDK에 적혀 있기를.. 기능적으로는 똑같아 보이는데, 바이너리 세마포어(카운트가 1인 세마포어)는 SMP머신에서 시스템 쓰레드가 동작할때 뮤텍스가 제공하는 내장된 데드락 보호(built-in protection against deadlocks)를 제공하지 않는다고 하네.
내장된 데드락 보호..?.. 이게 뭐야? 뮤텍스쪽을 쵸큼 봐 볼까..? 에? WDK문서에는 쵸큼 무서운 말들이 적혀 있어. 첫번째. 바로 위에 언급한 시스템 쓰레드에 제공하는 내장된 데드락 보호... 근데 이건 봐도 먼 소린지 모르겠다. SMP 머신에서 커널이 한번에 하나의 쓰레드에만 뮤덱스의 소유권을 준다라고 적혀 있는거 같은데, 원래 그런거 아냐?- .-?
그리고, 이것도 위에 살짝쿵 언급되어 있는거긴 한데.. 뮤텍스를 얻으면 APC를 disable시켜 버린다고 한다. 그니까.. APC는 또 뭐냐? 하는 질문이 생기는데..(아놔 끝이 없는 질문..ㅅㅂ- _-)..
APC가 뭔고 하니 비동기 프로시져 콜(asynchronous procedure call)..인데.. 이게 뭐.. 에 뭐냐면, 말 그대로 비동기적으로 call되는 함수(리턴값 없는 함수가 프로시저라고 알고 있는데.. 맞나?)를 의미해. 그니까, 언제 불릴지 모르는 함수를 말하는거지.
음.. 그러면 뮤텍스 객체를 얻었다는건 IRQL이 (DISPATCH_LEVEL이상으로)올라갔다고 이해해도 되는건가?... 그런건가???
..뭔지 모를 의문을 남긴채 다음으로 패스.. 하기 전에 하나 더 남아 있네.
에 또.. 뮤텍스 객체를 소유한 상태로, 즉 시그널 상태에서 I/O매니저에게 컨트롤을 리턴 해버리면, 커널이 시스템을 따운시켜 버린다네.. 후우..무스브라..
뭐.. 그렇고.. 아 근데 저거.. 이해가 안되는저거.. 진짜 IRQL이 바뀐거라고 이해해도 되는건가..- _-?
에..또.. 이건 처음 보는 디스패처 객체인데, 타이머 라는것이 있군.
얘를 어디에 써야 할지는 잘 모르겠는데, 간단하게는 다음과 같이 동작해.
어떤 쓰레드가 타이머를 돌리고 KeWaitForSingleObject를 호출하면, 얘는 타이머를 돌릴때 설정한 시간동안 대기를 하게 되는거야. 그리고 타이머에 설정된 시간이 다 되면, 타이머 객체는 시그널 상태가 되고, 그제서야 작업을 시작하는거지.
타이머는 이벤트처럼 알림 타이머와, 동기 타이머, 그리고 주기적(periodic) 타이머. 이렇게 3가지로 다시 나뉘게 되. 아.. 종류도 많지.. 귀찮게 스리 - _-
알림 타이머(책에서는 '통지 타이머'라고 적혀 있지만.. 어차피 영어로 하면 notification timer...)는 알림 이벤트와 거의 흡사해. 단지 타이머에 설정된 시간 후에 시그널 상태로 바뀐다는게 다른점 이지만..
아는지 모르겠는데, 알림 이벤트는 함수 하나만 콜 하면 바로 시그널 상태로 바뀌는 반면에 알림 타이머는 함수 하나를 콜 하면 일정시간 넌 시그널 상태로 있다가 시그널 상태로 바뀌게 되는거지.
동기 타이머는 일정 시간이 지난 다음에 하나의 쓰레드를 깨우고, 다시 넌-시그널 상태로 변하는거야. 뭐.. 더 필요한 설명 없지-.-? (아참, 얘는 초기화 함수가 쵸큼 틀려)
마지막으로 주기적 타이머는 말 그대로 주기적으로 타이머가 시그널 되고 넌 시그널 되고 하는거지-.-;
근데, 타이머를 어디에 써야할까? 우응.. 난 잘 모르겠삼. 근데, 책에 이렇게 적혀 있더라구. 반복적으로 디바이스의 활동을 체크하는 시스템 쓰레드에서 폴링(polling)루프를 돌릴때 쓸수 있대..
동기화를 위해서 '쓰레드'도 쓴다는거 알고 있어? 쓰레드의 display type(WinDbg에서 "dt _KTHREAD")를 보면, DISPATCHER_HEADER가 있다는거 알아? 한마디로 쓰레드 또한 디스패처 객체라는거지.
그니까 이런 추측을 할수가 있는거야. 쓰레드가 동작중일때는 넌-시그널 상태이고, 쓰레드가 terminated되면 시그널 상태가 되는거지.
...후아.. 밥도 먹었으니 정신 차리고 계속 진행해 볼까-.-?...!!
에..또.. 아 쓰레드를 사용해서 동기화 처리 하는 부분이로군.. 흠흠...
...흠. 아트 베이커 책에는 대략 위에 언급한 내용 정도로 정리를 끝내놓은 반면에 월터 오니의 책을 보면.. 뭐가 방대하다. 쩝..- _-;
위에서 언급했던 APC이야기가 본격적으로 나오네.. WDK에는 하나의 서브 챕터에 걸쳐서 설명을 하고 있군.. 먼저 뭔 이야기인지 읽은다음에 계속 진행해 보자구.
쩝.. 먼저 APC에 대해서 봐야겠다.
APC는 비 동기적으로 실행되는 함수를 말하는거고, DPC랑 비슷하긴 한데, APC는 특정 쓰레드의 컨텍스트에서 실행된다는 차이점이 있다.
근데.. 특정 쓰레드라는게, 어떤 특정 쓰레드를 의미하는지 모르겠다. 시스템적으로 APC를 처리하는 쓰레드가 따로 있는건지(그렇다면 DPC랑 무슨 차이야- _-)..
아니면, APC루틴이 실행되는 쓰레드를 설정할수 있다는 이야긴지..- _-;.. 후자일거 같긴 한데..
웅.. 책을 보니.. 후자가 맞는군.. 설정 할수 있는지는 모르겠는데, 'APC는 운영체제가 특정 쓰레드의 컨텍스트에서 함수를 실행시키도록 할 수 있는 메커니즘'이라고 적혀있삼.
우야튼, APC는 3가지의 종류가 있다고해. User APC랑, Normal Kernel APC랑 Special Kernel APC..요렇게 3개.
User APC는 말 그대로 유저모드에서, 현재 쓰레드가 alertable wait 상태일때만 사용이 가능하대..
Normal Kernel APC는 PASSIVE_LEVEL에서 동작하고, 유저모드에서 실행중인 녀석들을 모조리(APC포함) 선점할수 있다네. 얜 파일 시스템하고, 파일 시스템 필터 드라이버에서 쓴대.
Special Kernel APC는 APC_LEVEL에서 동작하고, 유머모드랑 커널모드에서 동작하는 녀석들(APC포함)을 선점할수 있어. 근데 커널 모드에서는 PASSIVE_LEVEL에서 동작하는 녀석만 선점할수 있어.
APC의 실행을 막기(disable) 위해서는 3가지 방법이 있어. Critical region(Special Kernel APC는 실행됨)하고, Guarded regions(APC 모두 막힘), IRQL을 APC_LEVEL이상으로 올리는거야.
음.. 여기 뮤텍스를 이용해서도 APC의 실행을 막을수가 있다는군. 아, 이 이야기 도데체 몇번째 반복하는건지 모르겠네..3번짼가.. 어쨌든.
뮤텍스 오브젝트를 소유하고 있는거는 Critical region에 들어가 있는거랑 같은거고, 보호된 뮤텍스(guarded mutex...얜 뭐니..?)는 Guarded region에 들어가 있는거랑 같은거래.
그리고 fast mutex는 IRQL을 APC_LEVEL로 높이는거랑 동일한 효과를 나타낸다는군. 쩝.
홍홍홍.. 근데 Guarded region은 윈도우 서버 2003 이후의 버전부터 지원을 한다는군...
어... 정작 중요한, 내가 알고싶은, APC를 왜! 쓰는지에 대해서는 일언 반구의 언급도 없네 씁- _-.
우선 '쓰레드를 이용한 동기화 처리'를 스택에 push하고, APC를 스택에 push한 다음.
아놔.. 1시간동안 Alertable이라는거에 대해서 알아 봤는데... 소득이 없었삼..ㅜ_ㅠ
이 #$%^& 같은 경우가!!- _-. 누가좀 알리도-0-! 어쨌든.. 후아.. 아놔.. 스킵..
나도 집에 가야하기 때문에, 이제 개념적으로만 정리하고 가련다. 대충대충 궈궈..
아.. 시스템 워커 쓰레드(System Worker Thread)..한 20분 정도 뒤비고 다녔는데.. 결국 결론은 없다. 다만, 얘는 짧은 동안 서비스를 하고, 시스템 쓰레드 컨텍스트에서 동작하기를 원하는 코드가 있을때 쓸만하다고 한다.
즉, 다음과 같은 경우이다. 드라이버 디스패치 루틴이 유저모드 쓰레드 컨텍스트에서 동작하고 있는데(Highest Level Driver), 시스템 쓰레드 컨텍스트에서만 동작하는 함수를 호출하고자 할때 쓸모가 있을것이다.
또한, 드라이버 온라인에서 본 내용인데, WorkItem 콜백 루틴은 PASSIVE_LEVEL에서 동작하기 대문에, 만약 DISPATCH_LEVEL에서 동작하는 루틴에서 PASSIVE_LEVEL이하에서 동작하는 루틴을 호출할 필요가 있다면, 시스템 워커 쓰레드에서 동작하게 할수도 있다는 언급이 있었다.
자. 그럼 의문은 뭐냐면, 드라이버에서 생성한 쓰레드(driver-dedicated thread)는 PASSIVE_LEVEL에서 동작하는게 아니냐? 이건데, 물론 얘도 PASSIVE_LEVEL에서 동작한다. 근데 이런 제약이 있네, 뭐냐하면, 쓰레드 호출자(드라이버) 역시 PASSIVE_LEVEL상태에 있어야 한다는 거다.
반면에 시스템 워커 쓰레드는 호출자가 어떤 IRQL에 존재해야 한다는 제약사항은 없는듯 보인다.(MSDN에 제약사항으로 언급되지 않았다.).. 좀더 찾아보니, IoQueueWorkItem이라는 함수가 DISPATCH_LEVEL이하에서 호출되어야 한다는 제약이 있긴 하지만, 그게 제약이 없다는 말하고 뭔 차이?
쬐금 마니 늦은 감이 있지만, Fast뮤텍스와 Guarded뮤텍스에 대해 짧게 언급해 보자.
Fast뮤텍스는 걍 커널 뮤텍스랑 별반 다를것은 없는데, 차이점이 무언고 하니, 얘는 지가 뮤텍스 객체를 가지고 있는데(시그널), 다시 뮤텍스 객체를 가지려고 하면 데드락 걸린다. (재 소유 불가)
그리고, 걍 뮤텍스는 IRQL의 변호가 없지만 Fast뮤텍스는 APC_LEVEL로 IRQL을 올린다는 차이가 있다. 뭐, 속도는 이름대로 약간 더 빠른듯..?
글면 Guarded뮤텍스는 뭘까? 얘는 윈도우 서버 2003 이후의 버전부터 지원하는 뮤텍스인데, Fast뮤텍스 보다 더 빠르댄다..-0-
Fast뮤텍스랑 비스꼬롬한 놈인데, Fast뮤텍스가 IRQL을 APC_LEVEL로 올리는 반면에, Guarded뮤텍스는 Guarded region(거 왜 있잖아 APC를 모조리 막는거)에 들어간다네. 이게 훨 빠르다는군.
Fast뮤텍스와 Guarded뮤텍스 모두 APC_LEVEL이하의 코드에서 사용할수 있다.
몇개 안남았다. 힘내자!!
에.. 그래서 이번에는 ERESOURCE다.
얘는 읽기/쓰기 lock에 쓴다고 하네. 아. 뭔소린지 모르겠지? 나도 모르겠어..
하튼, 시스템이 ERESOURCE를 기다리는 두개의 리스트를 보관한대. 그게 공유 대기자(shared waiters)랑 배타적 대기자(exclusive waiter)야.
얘네가 어떤 일을 하냐면, 배타적 대기자가 없고, 배타적으로 ERESOURCE를 소유(시그널)한 녀석도 없다면 읽기만 하는(shared) 쓰레드들이 진입해서 막~ 일(write를 뺀 나머지 연산)을 하는거야.
반면에, ERESOURCE를 얻은 녀석이 아무도(공유/배타적 모두)없다면 배타적 대기자가 소유(시그널)할수 있어. (아. 물론, 아무도 소유하고 있지 않다면 공유/배타적 대기자 모두 소유할 기회가 생기는거지). 얘는 임계 영역에서 쓰기 연산을 하는 거야.
음.. 느낌이 이렇네, 웹사이트 있잖아. 근데 가끔 '정기 정검'이라는 핑계(?!)로 웹 사이트 접속을 막는 경우가 있잖아. 딱 그런 경우랑 비슷하지 않을까? 동기화랑 뭔 상관인지는 잘 몰라도..
이제 가장 어렵다는 스핀락!! 무엇이 너를 그리 어렵다고 인식하도록 만들었는지 한번 보도록 하자.
스핀락은 커널 모드에서만 쓰이는 동기화 매커니즘이야. 얘는 SMP(Symmetric MultiProcessing) 머신의 DISPATCH_LEVEL이상의 루틴에서 동기화를 처리하는데 사용할수 있어. 뭐, 물론 SMP머신에서만 하는건 아니겠지만 말이야.
...하악..하악.. 안대겠다. 집에가서 보자. 후우..- _.. 사실은 내일 보게 될거 같지만 말이야..쩝..- -;
--내일.. 사실은 오늘--
스핀락.. 쳇! 시작부터 우왕ㄱ굳ㅋ.. 뭐 이런 시츄에이션..- _-;
어쨌든 주제를 잡았으니 죽죽 나가보자앗! 아트 베이커의 책에는 이런 언급이 있삼. 멀티 프로세서 시스템에서 한 CPU의 IRQL을 수정하는 것은 또 다른 CPU의 IRQL에는 영향을 미치지 않는다고. 즉 IRQL을 높여봤자. 다른 시퓨에서 더 높은 IRQL로 동일한 루틴을 콜하면.. 선점함수 있다는 이야기다. 흠.. 뭔가 공유 영역에 대한 동기화가 안되는 느낌인데?
해서 윈도우즈 2000에서는 스핀락 이라는 동기화 객체를 사용한다고 한다. 윈도우즈 2000부터 지원한다는게 아니라 사용한다고? 그럼 98시절부터 스핀락 객체가 있긴 했다는 말인가? 애매하네.. 딱히 중요한건 아니긴 하지만..ㅋ
스핀락은 두가지 타입이 있삼. 하나는 실행부 스핀락(excutive 스핀락)이고, 다른 하나는 인터럽트 스핀락(interrupt 스핀락)임. 얘네는 하는일은 똑같아. 즉 임계 영역의 동기화를 책임지는거지. 대신에 사용법에 약간의 차이가 있어. 실행부 스핀락은 KeAcquireSpinLock/KeReleaseSpinLock를 쓰는 반면에 인터럽트 스핀락은 KeSynchronizeExecution함수를 통해 동기화 루틴을 등록하는 형태로 사용한다. 에.. 그런데, 실제 ISR이 동작할때 함수들의 호출 순서를 잘 모르겠담..ㅠ_ㅜ
어쨌든, 위와 같은 실제 코딩시의 차이점이 존재하고, 또한 IRQL의 차이도 존재한다. 실행부 스핀락은 DISPATCH_LEVEL에서 동작하고, 인터럽트 스핀락은 DIRQL에서 동작한다.
스핀락 사용시 MS에서 요구하는 가이드 라인이 있다. 대충 보자.
-스핀락으로 동기화 처리를 하기 전에 KeInitializeSpinLock을 호출해서 스핀락을 초기화 해 줘야 한다고함.
-스핀락을 사용하는 루틴은 올바른 IRQL에서 불려야 한다. 실행부 스핀락은 DISPATCH_LEVEL이하에서, 인터럽트 스핀락은 DIRQL이하에서.
-에.. 스핀락을 25마이크로초 이상 잡고 있지 말아주삼. (뭐, 꼭 필요한 최소한의 작업만 하고 스핀락을 해제할것!)
-하드웨어/소프트웨어 예외가 발생 가능한 구간에서 스핀락 쓰지 말것!
-페이징 가능한 메모리에 접근하지 말것. (응? 왜? 페이징 관리자가 동작하는게 DISPATCH_LEVEL인가?)
-스핀락을 너무 오래 잡고 있거나, 스핀락을 소유한 상태에서 다시 스핀락을 요구(KeAcquireSpinLock)을 하면 데드락-0-!
등.
윈도우 xp이상에서는 새로운 유형의 스핀락인 '큐드(queued) 스핀락'을 사용할수 있다. 이녀석은 뭐냐면, 스핀락을 큐에 넣어 놓고 대기하는거다. 음.. 인터럽트 스핀락을 예로 들어서 생각을 해 보자. OTL이라는 디바이스가 있고, 해당 디바이스는 0.001초마다 인터럽트를 보낸다고 가정하자. 또한 OTL디바이스가 달려 있는 컴퓨터는 요즘 유행하는 듀얼코언지 시퓬지 하는 컴퓨터라고 생각을 해보잔 말이다. 디바이스가 인터럽트를 보내면, 인터럽트 요청은 CPU에게 전달이 되고, CPU는 해당 인터럽트 요청에 알맞는 인터럽트를 IDT에서 찾아서 해당 루틴의 주소를 실행해 준다. 자. 이때. 1002번째 요청이 CPU1에게 가고, 1003번째 요청이 CPU2에게 갔다고 생각하면, CPU2는 스핀락을 얻기위해 말 그대로 스핀하고 있게 된다.
뭐, 위의 상황은 일반 스핀락을 사용했을때의 상황이고.. 만약 큐드 스핀락을 사용하면 다음과 같아지게 된다.
CPU1이 스핀락을 소유하게 되면, CPU2는 큐(Queue)에 스핀락을 큐잉하게 된다. 그리고 CPU1이 스핀락을 릴리즈하면, CPU2가 스핀락을 얻게 되는거다. 근데.. 이게 뭐가 효율적이라는걸까? 웅.. 모르겠삼-0-;
에.. 하여간, 머 그런 장점이 있는것이고. 다만, 큐드 스핀락은 소유 요청을 할때(KeAcquireInStackQueuedSpinLock), KLOCK_QUEUE_HANDLE 구조체를 할당해서 스택에 올려 놓고 요청을 해야 한다.
후아...
오늘 오전에는 동기화 객체에 대해서 짧막하게나마 정리를 해 보도록 하잠.
우선 동기화 객체를 시작하기 전에, 디스패처란게 뭔가 하는 개념을 알고 넘어가야 한담. ... 그래서...
WDK문서에는 뭐라고 적혀있나 하고 봤더니..- _-. 걍 커널이 정의한 객체 타입들의 세트(a set of object types)를 디스패처 객체라고 부른다네.. 이게 뭐야 - _-
그리고 한다는 말이. 드라이버는 이 디스패처 객체를 동기화 메커니즘에 사용한다고.. 이게 뭐야- _-
인터넷에서 좀더 뒤적뒤적 거려보자... 관련 내용이 없다.. 췌. 그냥. 시그널 상태나 넌-시그널 상태를 가지는 객체로서 보통 동기화에 이용된다. 라고 알고 넘어가도록 하자.
아.. 좀더 읽어보니. 중요하다 싶은 말이 하나 적혀있네.
드라이버는 디스패처 객체를 동기화 메커니즘에 사용할수 있는데, 그게 PASSIVE_LEVEL의 IRQL에서 실행되고 있는 비-임의적(non-arbitrary) 쓰레드에서 가능하다고 하네.
다시 말하면, PASSIVE_LEVEL의 IRQL에서 실행되고 있는 비-임의적(non-arbitrary) 쓰레드에서 디스패처 객체를 이용하여 동기화 처리를 할수 있다는 이야기.
어쨌든, 그래서. 시그널/넌-시그널 이라는 말이 나왔는데, 이게 뭔 말이냐? 하면.. 시그널 상태는 쓰레드가 임계 영역에 진입할수 있는 상태이고, 넌-시그널 상태는 쓰레드가 임계영역에 진입하지 못하고 대기하고 있는거야.
실제로는 KeWaitForXxx 함수로 디스패처 객체가 시그널 상태가 될때까지 기다리게 하는거지.
이제, 실제로 동기화 메커니즘에 사용되는 디스패처 객체를 살펴보도록 하자.
우선 이벤트 객체... 후.. 내가 유저영역에서 동기화를 공부할때 무진장 헤멨던 놈이지 씁..
간단하게 말하면, 기다리고 있는 쓰레드들에게 이벤트를 주는거얌. 먼말인지 모르겠지?ㅋ.. 그럼 이제 자세하게 말해줄께..
에..또.. 예를 들어서 설명해야 이해하기 쉽겠지? 자자.. 이렇게 생각해 보자구. '피터', '브레드', '케인', '존스'라는 네 녀석(쓰레드)가 있어.
그리고, 얘네가 회사를 다니는데, 빨랑빨랑 회사 업무가 끝나기를 기다리는거야. 근데. 중요한건 이 회사는 좀 븅~ 회사라서 사장이 '끝났삼'이라고 말하기(이벤트) 전까지는 업무가 끝난게 아니야..(뭐 이따위 회사가.. - _-)
뭐. 대충 그렇다고 치고, 중요한건, 위에 언급한 네놈(쓰레드)중에서 한놈이 사실은 사장을 조종하는 배후야..(뭔가 스릴러의 냄새가..). 그래서 지 할일 끝내고 사장을 쿡 찌르는거지. 그러면 사장님이 '업무 끝났삼'이라고 말할거고, 업무가 끝나기를 기다리는 '피터'를 비롯한 찌질이 쓰레드들이 '아싸~'이러면서 지 할일(그게 뭐던지)을 하게 되는거지.
이런 경우를 '알림 이벤트'라고 해. (이벤트를 대기하고 있던 모든 쓰레드들이 이벤트가 설정[시그널]되면 자기 업무를 보는거지)
그리고, 이런 경우도 있어. 역시 '피터', '브레드', '케인', '존스'라는 네 녀석이 있는데, 이번에는 네 놈중에 '존스'가 '수잔'이라는 얘(이벤트)랑 사귀고 있어(소유중).
그러다가 '존스'가 '수잔'을 차 버린거야(set to 넌-시그널). 아놔 이런 개쉑. 어쨌든 그렇게 되서 애인 없는 찌질한 놈('피터', '브레드', '케인', 과 '존스'.. 이색퀴는 지가 차 놓고 애인 없다고 또 붙으려고 하는것 보게..- _-)이 '수잔'하고 사귀려고 막 대시(대쉬?)를 하다가 한놈이 '내가 수잔하고 사귄다'고 말해버리는거야.
쩝.. 그럼 나머지 녀석들은 또 수잔이 헤어지길 기다리는거야. (수잔이 먼저 차버리면 안대냐고? 여기선 안대.. 짤 없어 ㅋ;)
이런 경우를 '동기화 이벤트'라고 해. (이벤트를 대기하고 있던 모든 쓰레드들 중 하나의 쓰레드가 이벤트를 받으면 이벤트 객체는 넌-시그널 상태로 변환됨)
그 담으로 우리가 걸핏하면 듣게되는 '상호 배제'를 의미하는 뮤텍스(mutual exclusion의 줄임말)를 보도록 하자굿. 근데, 이게.. 무진장 헷갈리는게.. 동기화 이벤트랑 동작하는게 똑 같그덩-.-..
해서 난 아직도 뮤텍스랑 동기화 이벤트랑 먼 차이가 있는지 모르겠어. 누구 아는사람 손좀 들어주삼.
그래서 인터넷을 쵸큼 돌아다녀 보니 이런 글이 있네, 이벤트랑 커널 뮤텍스(즉, 우리가 아는 그 뮤텍스)는 비스꾸름 하다고 하네.
근데, 차이는 뭐냐면, 뮤텍스는 뮤텍스를 소유한 놈이 뮤텍스를 다시 소유하려고 해도 가능하다는거얌. (그럼 이벤트는 이 짓이 안되는건가 보네.. 안될거 같긴 하다만..)
그리고 이게 쫌 기억해야 할 내용일거 같은데, 얘는 부작용이 있다네. 근데 번역이 안되서 먼 말인지는 잘 모르겠어.
대충 발 번역을 해 보자면, 뮤텍스는 소유한 얘가 다시 요청을 할수가 있잖아. 그러면 뮤텍스의 내부 카운터를 증가시키게 되고.. 뭐 그런데, 이렇게 동작하면 KeEnterCriticalRegion함수를 콜 한것처럼 커널 APC가 disabled된다네. 이게 뭐.. 임의적 재 진입(arbitrary re-entrancy)를 막기 위해서라는데.. 뭔 소린지 모르겠으니 좀 알리조-0-
그리고, 카운트를 가진 뮤텍스를 의미하는 세마포어. 쩝.. 여기서 카운트를 가진다는 의미는 위에서 뮤텍스의 재 진입 카운트와는 다른 말이라는건 알고 있지? 세마포어가 가지고 있는 카운트가 의미하는건 동시에 실행할수 있는 쓰레드의 수를 의미해.
뭐, 이런거야. 세마포어 카운트가 3으로 초기화가 되어 있는데, 2놈의 쓰레드가 임계영역에 진입 할려고 한단 말이야. 그럴때 세마포어 카운트가 2 감소해서 1이 되는거지. 0이 되면 넌-시그널 상태가 되서 쓰레드들이 대기하게 되는거고... 세마포어는 이 정도네..
흠.. 잠깐, 그러면, 세마포어에서 카운트가 1이면.. 뮤텍스랑 똑같은거야?-.-?
에.. WDK에 적혀 있기를.. 기능적으로는 똑같아 보이는데, 바이너리 세마포어(카운트가 1인 세마포어)는 SMP머신에서 시스템 쓰레드가 동작할때 뮤텍스가 제공하는 내장된 데드락 보호(built-in protection against deadlocks)를 제공하지 않는다고 하네.
내장된 데드락 보호..?.. 이게 뭐야? 뮤텍스쪽을 쵸큼 봐 볼까..? 에? WDK문서에는 쵸큼 무서운 말들이 적혀 있어. 첫번째. 바로 위에 언급한 시스템 쓰레드에 제공하는 내장된 데드락 보호... 근데 이건 봐도 먼 소린지 모르겠다. SMP 머신에서 커널이 한번에 하나의 쓰레드에만 뮤덱스의 소유권을 준다라고 적혀 있는거 같은데, 원래 그런거 아냐?- .-?
그리고, 이것도 위에 살짝쿵 언급되어 있는거긴 한데.. 뮤텍스를 얻으면 APC를 disable시켜 버린다고 한다. 그니까.. APC는 또 뭐냐? 하는 질문이 생기는데..(아놔 끝이 없는 질문..ㅅㅂ- _-)..
APC가 뭔고 하니 비동기 프로시져 콜(asynchronous procedure call)..인데.. 이게 뭐.. 에 뭐냐면, 말 그대로 비동기적으로 call되는 함수(리턴값 없는 함수가 프로시저라고 알고 있는데.. 맞나?)를 의미해. 그니까, 언제 불릴지 모르는 함수를 말하는거지.
음.. 그러면 뮤텍스 객체를 얻었다는건 IRQL이 (DISPATCH_LEVEL이상으로)올라갔다고 이해해도 되는건가?... 그런건가???
..뭔지 모를 의문을 남긴채 다음으로 패스.. 하기 전에 하나 더 남아 있네.
에 또.. 뮤텍스 객체를 소유한 상태로, 즉 시그널 상태에서 I/O매니저에게 컨트롤을 리턴 해버리면, 커널이 시스템을 따운시켜 버린다네.. 후우..무스브라..
뭐.. 그렇고.. 아 근데 저거.. 이해가 안되는저거.. 진짜 IRQL이 바뀐거라고 이해해도 되는건가..- _-?
에..또.. 이건 처음 보는 디스패처 객체인데, 타이머 라는것이 있군.
얘를 어디에 써야 할지는 잘 모르겠는데, 간단하게는 다음과 같이 동작해.
어떤 쓰레드가 타이머를 돌리고 KeWaitForSingleObject를 호출하면, 얘는 타이머를 돌릴때 설정한 시간동안 대기를 하게 되는거야. 그리고 타이머에 설정된 시간이 다 되면, 타이머 객체는 시그널 상태가 되고, 그제서야 작업을 시작하는거지.
타이머는 이벤트처럼 알림 타이머와, 동기 타이머, 그리고 주기적(periodic) 타이머. 이렇게 3가지로 다시 나뉘게 되. 아.. 종류도 많지.. 귀찮게 스리 - _-
알림 타이머(책에서는 '통지 타이머'라고 적혀 있지만.. 어차피 영어로 하면 notification timer...)는 알림 이벤트와 거의 흡사해. 단지 타이머에 설정된 시간 후에 시그널 상태로 바뀐다는게 다른점 이지만..
아는지 모르겠는데, 알림 이벤트는 함수 하나만 콜 하면 바로 시그널 상태로 바뀌는 반면에 알림 타이머는 함수 하나를 콜 하면 일정시간 넌 시그널 상태로 있다가 시그널 상태로 바뀌게 되는거지.
동기 타이머는 일정 시간이 지난 다음에 하나의 쓰레드를 깨우고, 다시 넌-시그널 상태로 변하는거야. 뭐.. 더 필요한 설명 없지-.-? (아참, 얘는 초기화 함수가 쵸큼 틀려)
마지막으로 주기적 타이머는 말 그대로 주기적으로 타이머가 시그널 되고 넌 시그널 되고 하는거지-.-;
근데, 타이머를 어디에 써야할까? 우응.. 난 잘 모르겠삼. 근데, 책에 이렇게 적혀 있더라구. 반복적으로 디바이스의 활동을 체크하는 시스템 쓰레드에서 폴링(polling)루프를 돌릴때 쓸수 있대..
동기화를 위해서 '쓰레드'도 쓴다는거 알고 있어? 쓰레드의 display type(WinDbg에서 "dt _KTHREAD")를 보면, DISPATCHER_HEADER가 있다는거 알아? 한마디로 쓰레드 또한 디스패처 객체라는거지.
그니까 이런 추측을 할수가 있는거야. 쓰레드가 동작중일때는 넌-시그널 상태이고, 쓰레드가 terminated되면 시그널 상태가 되는거지.
...후아.. 밥도 먹었으니 정신 차리고 계속 진행해 볼까-.-?...!!
에..또.. 아 쓰레드를 사용해서 동기화 처리 하는 부분이로군.. 흠흠...
...흠. 아트 베이커 책에는 대략 위에 언급한 내용 정도로 정리를 끝내놓은 반면에 월터 오니의 책을 보면.. 뭐가 방대하다. 쩝..- _-;
위에서 언급했던 APC이야기가 본격적으로 나오네.. WDK에는 하나의 서브 챕터에 걸쳐서 설명을 하고 있군.. 먼저 뭔 이야기인지 읽은다음에 계속 진행해 보자구.
쩝.. 먼저 APC에 대해서 봐야겠다.
APC는 비 동기적으로 실행되는 함수를 말하는거고, DPC랑 비슷하긴 한데, APC는 특정 쓰레드의 컨텍스트에서 실행된다는 차이점이 있다.
근데.. 특정 쓰레드라는게, 어떤 특정 쓰레드를 의미하는지 모르겠다. 시스템적으로 APC를 처리하는 쓰레드가 따로 있는건지(그렇다면 DPC랑 무슨 차이야- _-)..
아니면, APC루틴이 실행되는 쓰레드를 설정할수 있다는 이야긴지..- _-;.. 후자일거 같긴 한데..
웅.. 책을 보니.. 후자가 맞는군.. 설정 할수 있는지는 모르겠는데, 'APC는 운영체제가 특정 쓰레드의 컨텍스트에서 함수를 실행시키도록 할 수 있는 메커니즘'이라고 적혀있삼.
우야튼, APC는 3가지의 종류가 있다고해. User APC랑, Normal Kernel APC랑 Special Kernel APC..요렇게 3개.
User APC는 말 그대로 유저모드에서, 현재 쓰레드가 alertable wait 상태일때만 사용이 가능하대..
Normal Kernel APC는 PASSIVE_LEVEL에서 동작하고, 유저모드에서 실행중인 녀석들을 모조리(APC포함) 선점할수 있다네. 얜 파일 시스템하고, 파일 시스템 필터 드라이버에서 쓴대.
Special Kernel APC는 APC_LEVEL에서 동작하고, 유머모드랑 커널모드에서 동작하는 녀석들(APC포함)을 선점할수 있어. 근데 커널 모드에서는 PASSIVE_LEVEL에서 동작하는 녀석만 선점할수 있어.
APC의 실행을 막기(disable) 위해서는 3가지 방법이 있어. Critical region(Special Kernel APC는 실행됨)하고, Guarded regions(APC 모두 막힘), IRQL을 APC_LEVEL이상으로 올리는거야.
음.. 여기 뮤텍스를 이용해서도 APC의 실행을 막을수가 있다는군. 아, 이 이야기 도데체 몇번째 반복하는건지 모르겠네..3번짼가.. 어쨌든.
뮤텍스 오브젝트를 소유하고 있는거는 Critical region에 들어가 있는거랑 같은거고, 보호된 뮤텍스(guarded mutex...얜 뭐니..?)는 Guarded region에 들어가 있는거랑 같은거래.
그리고 fast mutex는 IRQL을 APC_LEVEL로 높이는거랑 동일한 효과를 나타낸다는군. 쩝.
홍홍홍.. 근데 Guarded region은 윈도우 서버 2003 이후의 버전부터 지원을 한다는군...
어... 정작 중요한, 내가 알고싶은, APC를 왜! 쓰는지에 대해서는 일언 반구의 언급도 없네 씁- _-.
우선 '쓰레드를 이용한 동기화 처리'를 스택에 push하고, APC를 스택에 push한 다음.
아놔.. 1시간동안 Alertable이라는거에 대해서 알아 봤는데... 소득이 없었삼..ㅜ_ㅠ
이 #$%^& 같은 경우가!!- _-. 누가좀 알리도-0-! 어쨌든.. 후아.. 아놔.. 스킵..
나도 집에 가야하기 때문에, 이제 개념적으로만 정리하고 가련다. 대충대충 궈궈..
아.. 시스템 워커 쓰레드(System Worker Thread)..한 20분 정도 뒤비고 다녔는데.. 결국 결론은 없다. 다만, 얘는 짧은 동안 서비스를 하고, 시스템 쓰레드 컨텍스트에서 동작하기를 원하는 코드가 있을때 쓸만하다고 한다.
즉, 다음과 같은 경우이다. 드라이버 디스패치 루틴이 유저모드 쓰레드 컨텍스트에서 동작하고 있는데(Highest Level Driver), 시스템 쓰레드 컨텍스트에서만 동작하는 함수를 호출하고자 할때 쓸모가 있을것이다.
또한, 드라이버 온라인에서 본 내용인데, WorkItem 콜백 루틴은 PASSIVE_LEVEL에서 동작하기 대문에, 만약 DISPATCH_LEVEL에서 동작하는 루틴에서 PASSIVE_LEVEL이하에서 동작하는 루틴을 호출할 필요가 있다면, 시스템 워커 쓰레드에서 동작하게 할수도 있다는 언급이 있었다.
자. 그럼 의문은 뭐냐면, 드라이버에서 생성한 쓰레드(driver-dedicated thread)는 PASSIVE_LEVEL에서 동작하는게 아니냐? 이건데, 물론 얘도 PASSIVE_LEVEL에서 동작한다. 근데 이런 제약이 있네, 뭐냐하면, 쓰레드 호출자(드라이버) 역시 PASSIVE_LEVEL상태에 있어야 한다는 거다.
반면에 시스템 워커 쓰레드는 호출자가 어떤 IRQL에 존재해야 한다는 제약사항은 없는듯 보인다.(MSDN에 제약사항으로 언급되지 않았다.).. 좀더 찾아보니, IoQueueWorkItem이라는 함수가 DISPATCH_LEVEL이하에서 호출되어야 한다는 제약이 있긴 하지만, 그게 제약이 없다는 말하고 뭔 차이?
쬐금 마니 늦은 감이 있지만, Fast뮤텍스와 Guarded뮤텍스에 대해 짧게 언급해 보자.
Fast뮤텍스는 걍 커널 뮤텍스랑 별반 다를것은 없는데, 차이점이 무언고 하니, 얘는 지가 뮤텍스 객체를 가지고 있는데(시그널), 다시 뮤텍스 객체를 가지려고 하면 데드락 걸린다. (재 소유 불가)
그리고, 걍 뮤텍스는 IRQL의 변호가 없지만 Fast뮤텍스는 APC_LEVEL로 IRQL을 올린다는 차이가 있다. 뭐, 속도는 이름대로 약간 더 빠른듯..?
글면 Guarded뮤텍스는 뭘까? 얘는 윈도우 서버 2003 이후의 버전부터 지원하는 뮤텍스인데, Fast뮤텍스 보다 더 빠르댄다..-0-
Fast뮤텍스랑 비스꼬롬한 놈인데, Fast뮤텍스가 IRQL을 APC_LEVEL로 올리는 반면에, Guarded뮤텍스는 Guarded region(거 왜 있잖아 APC를 모조리 막는거)에 들어간다네. 이게 훨 빠르다는군.
Fast뮤텍스와 Guarded뮤텍스 모두 APC_LEVEL이하의 코드에서 사용할수 있다.
몇개 안남았다. 힘내자!!
에.. 그래서 이번에는 ERESOURCE다.
얘는 읽기/쓰기 lock에 쓴다고 하네. 아. 뭔소린지 모르겠지? 나도 모르겠어..
하튼, 시스템이 ERESOURCE를 기다리는 두개의 리스트를 보관한대. 그게 공유 대기자(shared waiters)랑 배타적 대기자(exclusive waiter)야.
얘네가 어떤 일을 하냐면, 배타적 대기자가 없고, 배타적으로 ERESOURCE를 소유(시그널)한 녀석도 없다면 읽기만 하는(shared) 쓰레드들이 진입해서 막~ 일(write를 뺀 나머지 연산)을 하는거야.
반면에, ERESOURCE를 얻은 녀석이 아무도(공유/배타적 모두)없다면 배타적 대기자가 소유(시그널)할수 있어. (아. 물론, 아무도 소유하고 있지 않다면 공유/배타적 대기자 모두 소유할 기회가 생기는거지). 얘는 임계 영역에서 쓰기 연산을 하는 거야.
음.. 느낌이 이렇네, 웹사이트 있잖아. 근데 가끔 '정기 정검'이라는 핑계(?!)로 웹 사이트 접속을 막는 경우가 있잖아. 딱 그런 경우랑 비슷하지 않을까? 동기화랑 뭔 상관인지는 잘 몰라도..
이제 가장 어렵다는 스핀락!! 무엇이 너를 그리 어렵다고 인식하도록 만들었는지 한번 보도록 하자.
스핀락은 커널 모드에서만 쓰이는 동기화 매커니즘이야. 얘는 SMP(Symmetric MultiProcessing) 머신의 DISPATCH_LEVEL이상의 루틴에서 동기화를 처리하는데 사용할수 있어. 뭐, 물론 SMP머신에서만 하는건 아니겠지만 말이야.
...하악..하악.. 안대겠다. 집에가서 보자. 후우..- _.. 사실은 내일 보게 될거 같지만 말이야..쩝..- -;
--내일.. 사실은 오늘--
스핀락.. 쳇! 시작부터 우왕ㄱ굳ㅋ.. 뭐 이런 시츄에이션..- _-;
어쨌든 주제를 잡았으니 죽죽 나가보자앗! 아트 베이커의 책에는 이런 언급이 있삼. 멀티 프로세서 시스템에서 한 CPU의 IRQL을 수정하는 것은 또 다른 CPU의 IRQL에는 영향을 미치지 않는다고. 즉 IRQL을 높여봤자. 다른 시퓨에서 더 높은 IRQL로 동일한 루틴을 콜하면.. 선점함수 있다는 이야기다. 흠.. 뭔가 공유 영역에 대한 동기화가 안되는 느낌인데?
해서 윈도우즈 2000에서는 스핀락 이라는 동기화 객체를 사용한다고 한다. 윈도우즈 2000부터 지원한다는게 아니라 사용한다고? 그럼 98시절부터 스핀락 객체가 있긴 했다는 말인가? 애매하네.. 딱히 중요한건 아니긴 하지만..ㅋ
스핀락은 두가지 타입이 있삼. 하나는 실행부 스핀락(excutive 스핀락)이고, 다른 하나는 인터럽트 스핀락(interrupt 스핀락)임. 얘네는 하는일은 똑같아. 즉 임계 영역의 동기화를 책임지는거지. 대신에 사용법에 약간의 차이가 있어. 실행부 스핀락은 KeAcquireSpinLock/KeReleaseSpinLock를 쓰는 반면에 인터럽트 스핀락은 KeSynchronizeExecution함수를 통해 동기화 루틴을 등록하는 형태로 사용한다. 에.. 그런데, 실제 ISR이 동작할때 함수들의 호출 순서를 잘 모르겠담..ㅠ_ㅜ
어쨌든, 위와 같은 실제 코딩시의 차이점이 존재하고, 또한 IRQL의 차이도 존재한다. 실행부 스핀락은 DISPATCH_LEVEL에서 동작하고, 인터럽트 스핀락은 DIRQL에서 동작한다.
스핀락 사용시 MS에서 요구하는 가이드 라인이 있다. 대충 보자.
-스핀락으로 동기화 처리를 하기 전에 KeInitializeSpinLock을 호출해서 스핀락을 초기화 해 줘야 한다고함.
-스핀락을 사용하는 루틴은 올바른 IRQL에서 불려야 한다. 실행부 스핀락은 DISPATCH_LEVEL이하에서, 인터럽트 스핀락은 DIRQL이하에서.
-에.. 스핀락을 25마이크로초 이상 잡고 있지 말아주삼. (뭐, 꼭 필요한 최소한의 작업만 하고 스핀락을 해제할것!)
-하드웨어/소프트웨어 예외가 발생 가능한 구간에서 스핀락 쓰지 말것!
-페이징 가능한 메모리에 접근하지 말것. (응? 왜? 페이징 관리자가 동작하는게 DISPATCH_LEVEL인가?)
-스핀락을 너무 오래 잡고 있거나, 스핀락을 소유한 상태에서 다시 스핀락을 요구(KeAcquireSpinLock)을 하면 데드락-0-!
등.
윈도우 xp이상에서는 새로운 유형의 스핀락인 '큐드(queued) 스핀락'을 사용할수 있다. 이녀석은 뭐냐면, 스핀락을 큐에 넣어 놓고 대기하는거다. 음.. 인터럽트 스핀락을 예로 들어서 생각을 해 보자. OTL이라는 디바이스가 있고, 해당 디바이스는 0.001초마다 인터럽트를 보낸다고 가정하자. 또한 OTL디바이스가 달려 있는 컴퓨터는 요즘 유행하는 듀얼코언지 시퓬지 하는 컴퓨터라고 생각을 해보잔 말이다. 디바이스가 인터럽트를 보내면, 인터럽트 요청은 CPU에게 전달이 되고, CPU는 해당 인터럽트 요청에 알맞는 인터럽트를 IDT에서 찾아서 해당 루틴의 주소를 실행해 준다. 자. 이때. 1002번째 요청이 CPU1에게 가고, 1003번째 요청이 CPU2에게 갔다고 생각하면, CPU2는 스핀락을 얻기위해 말 그대로 스핀하고 있게 된다.
뭐, 위의 상황은 일반 스핀락을 사용했을때의 상황이고.. 만약 큐드 스핀락을 사용하면 다음과 같아지게 된다.
CPU1이 스핀락을 소유하게 되면, CPU2는 큐(Queue)에 스핀락을 큐잉하게 된다. 그리고 CPU1이 스핀락을 릴리즈하면, CPU2가 스핀락을 얻게 되는거다. 근데.. 이게 뭐가 효율적이라는걸까? 웅.. 모르겠삼-0-;
에.. 하여간, 머 그런 장점이 있는것이고. 다만, 큐드 스핀락은 소유 요청을 할때(KeAcquireInStackQueuedSpinLock), KLOCK_QUEUE_HANDLE 구조체를 할당해서 스택에 올려 놓고 요청을 해야 한다.
후아...


덧글
zXEr 2009/06/25 20:32 # 답글
형은 역시 쩔어요.. -_-
승네군 2009/06/26 07:18 # 삭제 답글
...쩐다는 표현을 개인적으로 과히 좋아하지 않아서.. 기분이 별로 좋지는 않군핫.(뭐.. 덕후체나 초딩체는 웃기고 귀여워서 좋아한다만..)
그리고.. 니가 더 쩔어.. -0-b