Maystyle :
Admin : New post
Guestbook
Local
media
Catergories
Recent Articles
Recent Comments
Recent Trackbacks
Calendar
Tag
Archive
Link
Search
 
해당되는 게시물 103건
  왜 Windows Server 2012 의 Dynamic Team이 대단한가? 
작성일시 : 2014. 2. 27. 22:11 | 분류 : Windows Server/Kernel

기존 Out bound A/A Team 구현 방식의 한계

- 구현 방식은 TCP Port / IP Address / Mac Address Hash 방식이 제공되었습니다 반면 현재의 Dynamic Teaming의 경우 동일한 Port일지라도 Flowlet에 따라 부하 분산이 가능하나 기존의 구현 방식은 Port 수준까지만의 부하 분산만 지원 하였습니다.

왜? TCP Port 수준 밑단까지는 부산 분산을 구현 할 수 없었을까?

- 만약 TCP Stream 상의 패킷들이 동시에 NIC를 통해 외부로 전송된다고 생각해 보자. 이렇게 전달된 패킷들은 실제의 Stream 순서대로 목적지로 전송되지 않을 확률이 큽니다. 일반적으로 이러한 경우을 Out-of-order라고 부르고 있으며 이 경우 목적지에서는 이에 대한 Packet Re-ordering을 진행하게 됩니다. 자 다시 말해 바로 하나의 스트림에 해당하는 데이터가 동시에 Team을 이루고 있는 NIC로 전송됨으로써 당연히 Out-of-order의 발생이 필연적이 되게 되는 이는 수신측의 Re-ordering 연산으로 인한 성능 저하로 이어지게 됩니다.

정말 이런 Re-ordering 이 성능에 영향을 주나요?

- 내 아래 문서를 참조하세요. (http://kb.pert.geant.net/PERTKB/PacketReordering)
In principle, applications that use a transport protocol such as TCP or SCTP don't have to worry about packet reordering, because the transport protocol is responsible for reassembling the byte stream (message stream(s) in the case of SCTP) into the original ordering. However, reordering can have a severe (심각한) performance impact on some implementations of TCP.

그렇다면 동일 TCP Stream는 여러 팀 Member 중 하나만 사용하게 되나요?

- 내 맞습니다. 문제는 TCP Stream을 과연 Team 드라이버가 인식할 수 있냐는 부분입니다. R2의 Dynamic 부하 분산 모드는 동일한 Port를 사용하더라도 TCP Stream이 다른 경우 부하 분산을 제공해 줍니다. 이는 TCP Stream을 이해 할 수 있는 알고리즘인 Flowlet 이 적용되어 있기 때문입니다.

잠깐 Flowlet은 어떻게 구현 되어 있나요?

- 수많은 TEST를 거쳐서 동일 Port를 이용하는 Traffic에 대해서 연속 스트립 여부를 확인할 수 있게 되었습니다. 결론은 시간차이인데요. 동일 스트림일때와 단일 스트림에서 다른 스트림으로 바뀔때는 약간의 시간차가 있습니다. 그리고 그 시간차를 확인하여 동일 스트림이 아닌 경우 다른 NIC 즉 다른 팀 멤버를 사용하도록 디자인 한거죠.

- 실은 자연 과학도 마찮가지지만 관찰과 직관은 꽤 멋진 결과물을 내곤 합니다. 제가 소계해 드리는 Flowlet 알고리즘 역시 그 관찰과 직관의 결과물이죠.
“흔한 예로 IBM 친구들의 최고의 발명품 중 하나인 RISC 프로세서가 있습니다.”

그 외에 다른 장점은 없나요?

- VM에서 외부로 나가는 Traffic 역시도 Dynamic 모드를 통해 Team을 이루고 있는 멤버들 모두를 이용한 부하 분산 모드를 제공하고 있습니다.

PS. 아차… 혹시 이거 쓰는데 네트워크 성능이 더 떨어진다 싶으면 스위치에 라우터 가드 기능을 끄시는게… :) 꼭 궁합이 않맞는 장비가 일부 있는거 같더라구요.

Name   Password   Home   Secret   Submit
  Hyper Threading 쓸까? 말까? 
작성일시 : 2011. 9. 1. 15:39 | 분류 : Windows Server/Kernel

음 제가 MS 올 때 받았던 질문이기도 하고…
깊게는 이야기 하지 않고 딱 고객이 질문하면 대답할 수 있는 수준만 정리해봤습니다.
결론만 보실려면 맨 아래 영문만 보시면 됩니다.

먼저 하이퍼 스래딩을 알려면 CPU 구조 중 파이프라인과 수퍼 스칼라를 알아야 합니다.

먼저 파이프 라인을 알아 보겠습니다.

CPU의 성능을 높히기 위해서는 어떻게 해야 할까요?

1. 당연히 처리 속도를 높힌다.
2. 그리고 단위 시간당 처리할 수 있는 양을 늘린다.

음 1번과 2번이 같은 이야기로 들리시나요?
다시 CPU에서 명령어가 실행 된다 함은 “메모리에서 읽어서 > 해독하고 > Operation Code를 찾아 내어 > 해당 코드를 실행 하고 > 결과를 적제 한다.” 정도가 되겠죠?

자 이렇게 명령어가 한 사이클 동작한다고 했을때 다음과 같은 가정을 해 봅니다.

1. 5단계의 과정이 다 끝난 이후 다음 명령어가 실행 된다.

명령어 처리는 분명 5단계라는 가정을 했습니다. 즉 특정 명령어는 분명 5단계 중 하나의 단계인 상태로 남아 있을 것이고, 이는 나머지 4단계를 처리하는 모듈들은 모두 쉬고 있다라고 볼 수 있는 것이죠.

그래서 명령어는 CPU는 각 단계를 통해 실행 되고 해당 단계가 완료 됐을 때, Pipe line을 통해 다음 실행 단계로 전이됩니다. 이 기술을 파이프 라인이라고 부르며 이를 통해 단위 시간당 처리 량을 늘릴 수 있죠.

자 이제는 수퍼 스칼라 구조에 대해서 알아보겠습니다.

그런데 파이프 라인만으로는 처리률을 늘리는데 한계가 있습니다.
자 다음과 같은 코드를 생각해 보겠습니다.

1. a1 = a0 + 1
2. a2 = a1 + 5
3. b1 = b2 +b3
4. c0 = c1 +4

각각이 하나의 명령어라고 볼 때, 1번과 2번은 1번 명령어가 첫번째 “메모리에서 읽어서”가 진행 된 이후 2번이 진행되기가 어렵습니다. 왜냐? 2번은 1번 과정이 끝난 이후 즉 1번 과정에서 결과를 적재한 이후 a1 을 읽어서 진행해야 하기 때문이죠. 물론 recursive 하게 호출하는 방식으로 병렬 처리율을 높히기는 하지만 말이죠. 하지만 코드를 자세히 보니깐 음… 3번과 4번은 1번과 2번과 같은 종속성이 없군요. 자 그렇다면 3번과 4번을 먼저 실행하는건 어떨까요? 이렇게 하기위해서는? 파이프 라인이 병렬로 있으면 되겠군요! (물론 비 순차 실행을 위한 여러 도구가 필요하긴 합니다.)

그래서 현대의 Processor는 파이프라인을 병렬로 여러개 가지게 돼었습니다.

그리고 이런 명령어 수준의 병렬화 처리 다시 말해 처리 집적도를 높혀주기 위해서 비 순차 수퍼 스칼라 파이프 라인 구조를 탄생하게 됐습니다.

그!런!데! 지금까지 전 명령어 수준의 즉 Instruction 수준의 병렬화만 이야기 했죠.
다시 쓰래드 수준의 병렬화가 이루어진다면 해당 명령어들 사이의 종속성은 훨씬 줄어 들게 되고, 다시 이 병렬 파이프 라인들을 더욱 효율적으로 사용하게 될 수 있지 않을까요? 그래서 지금의 Processor 들은 쓰래드 간의 병렬화를 지원하기 위한 하이퍼 쓰래딩 기술이 적용되게 됐습니다. 단지 쓰래드들간의 문맥 교환 비용 완화를 위한 약간의 레지스터가 필요했을 뿐이니깐요. (펜티엄 4에서 하이퍼 쓰래딩 구현을 위한 레지스터로 인한 증가량은 단일 면적에 비하여 약 5% 수준이라고 합니다.)

자 다시 말하면 명령어를 처리하는 파이프 라인을 빈틈 없이 꽉 채워 쓰기 위해서 하이퍼 쓰래딩 기술이 나왔다고 할 수 있습니다. 하지만 하이퍼 쓰래딩 환경은 실제로 케시 및 비 순차 실행 (위에서 설명한 1~4까지의 코드 실행을 생각해 보면 알 수 있습니다.) 으로 인한 불균형 문제가 발생 할 수 있습니다. 먼저 비 순차 실행은 이발소 문제와 같은 스래드 간의 기아 조건을 야기 시킬 수 있고, (즉 실행하는 쓰래드만 열라 실행하게 되는….) 이를 위한 여러가지 해결 책들이 적용되고 있음에도 쉽지 않음이 사실입니다. 또한 실행하는 쓰래드들 간의 캐쉬가 충돌을 일으키는 경우에도 성능저하가 발생하게 됩니다.

하지만 그럼에도 불구하고 인텔에서는 약 30% 정도의 성능 향상 효과가 있다고 이야기 하고 있습니다. 더불어서 현재의 Windows 2008 R2의 경우 하이퍼 쓰래딩을 aware 하여 CPU 스케줄링을 하고 있습니다. (이전에는 잘 모르겠습니다… ㅡㅡ;;;)

하지만 결론은 무조건 하이퍼 쓰래딩을 Disable 시키는 것은 아니다 정도가 되지 않을까 합니다.
더 나아간다면 실제 성능 측정을 통한 방법 많이 이익인지 불이익인지를 명확하게 판가름하는 방법이 아닐까 합니다.

SQL Server에서의 적용에 대한 저희 문서는 아래와 같이 이야기 하고 있습니다.

”The performance of hyper-threaded environments varies. Conservative testing has shown 10 to 20 percent gains for SQL Server workloads, but the application patterns have a significant affect. You might find that some applications do not receive an increase in performance by taking advantage of hyper-threading. If the physical processors are already saturated, using logical processors can actually reduce the workload achieved.
For example, applications that cause high levels of contention can cause decreased performance in a hyper-threaded environment. We recommend that you test your application thoroughly to make sure that a hyper-threaded environment provides you the performance gain that you want versus the purchase of equivalent physical CPUs. Hyper-threading can be very helpful but hyper-threading cannot replace the full power of an additional physical CPU.
The third-party products that this article discusses are manufactured by companies that are independent of Microsoft. Microsoft makes no warranty, implied or otherwise, about the performance or reliability of these products.”
출처 : http://support.microsoft.com/kb/322385/en-us

Hyper-V 관점에서는 64개 까지 LP를 지원 함으로 이에 따라 Hyper Thread 사용 여부를 판별 하면 됩니다.

Exchange는 아직까지는 Disable이 권고 입니다.

SQL Server는 2000까지는 Disable 이후에는 위에 언급한것과 같이 TEST후 적용 입니다.

BlogIcon archmond (2011.09.10 22:57 신고) R | X
좋은 글, 재밌게 읽고 갑니다.

Name   Password   Home   Secret   Submit
  Lock Pages in Memory 
작성일시 : 2011. 8. 8. 21:58 | 분류 : Windows Server/Kernel

오랫만에 글을 올리는군요… : )

목적 : Page Size와 Lock Pages in Memory
(작은 머리가 폭발할 지경 / 어떤 문서는 Page Size무슨 4M니 2M이야기 해서 그냥 AMD 문서로 통일함)

Large Page Size는 x64 CPU인 Westmere 부터는 1G까지 지원됩니다. 하지만 AMD의 문서를 보니 보통 2M씩 쓰는게 일반적이라고 합니다. (http://blogs.amd.com/developer/2009/02/23/huge-pages-and-numa-on-windows-operating-systems/)

하지만 실제로 Page Size가 2M증가하는 것은 아니고 4K짜리 512개를 모아서 2M것처럼 할당을 해주는거라고 합니다. 그래서 TLB에서만 이 효과를 볼 수 있다는 말인건지… 좀 햇갈립니다.

The JVM or other application makes a VirtualAlloc request to allocate a chunk of memory with a flag to map it to huge pages.  The OS really deals in terms of 4K pages.  To return a 2MB page it must find 512 contiguous 4K pages and those will then be locked in memory (they are not candidates for paging out).  By default, it will look for such a 2MB block in the memory that is local to the requesting processor.  If the OS cannot find a free 2MB page in local memory, it will search in memory from other nodes in the system.

아무튼 위와 같기 때문에 이 설정이 Lock Pages in Memory였던 거군요…
어쨌든 W2k8에서는 기본으로 지원한다는것은 사실이고, 가장 큰 장점은 TLB 를 자알 쓸 수 있다 입니다. (성능차이는 약 20% 정도라고 함)

· Windows Server 2008/2008 R2 have Large Memory Pages enabled by default
· Windows Vista/7 have Large Memory Pages enabled by default
· Windows Server 2008 R2 Hyper-V added support for Large Memory Pages (surprise!) and is one of many new performance improvements in R2

(예를 들어 1M의 메모리를 할당하는 Application이 해당 메모리를 자주쓰는 경우 TLB는 256개가 필요합니다. 하지만 2M짜리 Page Size를 이용하는 경우1~ 2번이면 가능하겠죠….)

Large Page사용함으로 얻는 이익 : http://developer.amd.com/documentation/articles/Pages/2142006111.aspx
(주로 TLB와 관련된 성능 향상을 이야기 하고 있음)

JVM 이 Large Page Size사용하는 방법 : http://developer.amd.com/documentation/Articles/Pages/322006145.aspx

(Lock Pages in memory 정책을 통한 설정에 추가적으로 튜닝 포인트를 언급)
마지막으로 개발할때 고려사항은http://msdn.microsoft.com/en-us/library/aa366720(VS.85).aspx 을 참고하시면 될꺼 같습니다.

Name   Password   Home   Secret   Submit
  3GB와 Free System PTE 그리고 USERVA 
작성일시 : 2011. 4. 4. 11:07 | 분류 : Windows Server/Kernel

본 수치는 실 머신을 기준으로 만들어졌기 때문에 환경에 따라 다른 결과가 나올 수 있습니다.

/PAE /3GB /USERVA=3030 의 TEST Machine Free System PTE 수 : 17,000 개
/NOPAE /3GB /USERVA=2900, Dynamic Memory Disable TEST Machine Free System PTE 수 : 70,000 개
/PAE 의 TEST Machine Free System PTE 수 : 182,000 개

Name   Password   Home   Secret   Submit
  Bug Check 0x3F: NO_MORE_SYSTEM_PTES 
작성일시 : 2010. 8. 17. 17:20 | 분류 : Windows Server/Kernel

일반적으로 시스템에 많은 I/O 발생하는 경우 System PTE가 fragment 되어 발생 합니다.

매개 메시지 설명
0xA - PTE (페이지 테이블 항목 (형식: 0 = 시스템 확장, 1 = 비페이징 풀 확장
0xB - 요청된 크기
0xC - 사용 가능한 총 시스템 PTE
0xD - 총 시스템 PTE

[원인]
디라이버에서 큰 사이즈의 메모리를 요청하였으나 시스템에서 해당 사이즈의 연속된 메모리 공간을 제공하지 못한 경우 발생 하게 됩니다.

[해결 방안]
문제 확인을 위하여 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\TrackPtes 값을 DWORD 1로 설정하여 Stack trace 를 활성화 시킨 후 해당 문제가 다시 한번 발생하는 것을 기다립니다.

0: kd> !sysptes 4
0x2c47 System PTEs allocated to mapping locked pages
VA MDL PageCount Caller/CallersCaller
f0e5db48 eb6ceef0 1 ntkrpamp!MmMapLockedPages+0x15/ntkrpamp!IopfCallDriver+0x35
f0c3fe48 eb634bf0 1 netbt!NbtTdiAssociateConnection+0x1f/netbt!DelayedNbtProcessConnect+0x17c
f0db38e8 eb65b880 1 mrxsmb!SmbMmAllocateSessionEntry+0x89/mrxsmb!SmbCepInitializeExchange+0xda
f8312568 eb6df880 1 rdbss!RxCreateFromNetRoot+0x3d7/rdbss!RxCreateFromNetRoot+0x93

출처 : http://www.osronline.com/ddkx/ddtools/bccodes_4sh3.htm

Name   Password   Home   Secret   Submit
  풀 메모리의 단편화 
작성일시 : 2010. 3. 12. 16:37 | 분류 : Windows Server/Kernel

원문 : http://blogs.msdn.com/ntdebugging/archive/2009/12/11/pool-fragmentation.aspx
모든 권리는 원문 저작자에 있습니다.

안녕하세요. 제 이름은 스태판입니다. 저는 Microsoft의 Global Escalation Service Team에서 Escalation engineer로 근무하고 있습니다. 오늘은 제가 최근에 경험했던 풀 메모리의 단편화에 대해서 이야기 하겠습니다. 자 덤프파일을 보도록 하겠습니다.

아래는 !vm에 대한 결과 입니다.
image

!poolused /t5 2 명령어를 통해 nonpaged pool 사용량이 많은 유저들을 확인해 보았습니다.

image

!vm 의 결과는 101MB 의 사용을 표기 하고 있으나 !poolused 는 65MB 의 사용이 확인 됩니다. 이는 바로 풀 메모리 단편화를 의미합니다. 조사 결과 저는 많은 풀 페이지들이 아래와 같은 패턴으로 구성되어 있음을 확인하였습니다.

image

페이지 fa808000 은 0x18 = 24 byte 크기 입니다.  fa808000 과 fa808a38의 모든 페이지들은 freed 되어 이고, 다른 목적을 위하여 재사용 될 것입니다. 위의 페이지에서는 4096 Bytes 중 24 Bytes 만 사용 중입니다.

이런 상황은 fa550000 과 f8feb000 에서도 동일하게 나타나고 있습니다. 자 우리의 질문은 왜 이런 일들이 일어나고 어떻게 하면 이런 상황을 미연에 방지 할 수 있는가? 입니다.

이 덤프에서 저는 많은 MmCm 의 Pool 메모리 사용 현황을 확인 하였습니다.

image

다음은 일반적으로 단편화가 발생 하기 위한 상황 입니다.

1) 드라이버가 0xF18 크기의 풀 블럭을 요구합니다.  위의 3개의 풀 모두 Free 된 공간의 총합은 충분히 0xF18 크기 이상이 됩니다. 하지만 전체 페이지 4K는 3개로 분활 되어 있고, 이중 이 Free 된 공간은 꼭대기와 밑쪽에 위치하고 있습니다. 그리고 둘 모두 0xF18 크기를 할당 할 만큼 크지 못합니다.
2) 그래서 OS는 드라이버의 요청에 대한 새로운 풀 페이지를 제공합니다. 그리고 역시 밑쪽의 공간은 Freed pool 로 표시 됩니다.
3) 자 아주 작은 크기의 Pool에 대한 요청이 진행 됩니다. OS는 위에서 할당한 풀의 밑쪽 공간을 제공하게 됩니다.
4) 드라이버 MmCm이 사용하던 Pool을 해제 합니다. 하지만 해당 풀 페이지의 하단 부분은 아직도 사용 중이고, 그래서 해당 페이지가 전체는 Freed 되지 못합니다. 그리고 시간이 흘러서 이 공간은 다른 목적을 위해 할당 되게 됩니다.
5) 자 또 0xF18 크기의 풀 블럭을 요청됐습니다. 이전의 풀 블럭은 다른 용도로 이미 할당되어 사용되고 있어 새로운 요청에 사용할 수 없습니다. 그래서 OS는 다시 새로운 페이지를 할당하여 풀 블럭을 제공합니다.
6) 위와 같은 일들로 인해 풀 메모리는 점점 단편화 되게 됩니다. 그리고 그 증거가 바로 이 crash memory dump 죠.

이와 같은 이슈를 피하기 위해서는 0xF18 사이즈의 요청 대신에 전체 풀을 커버할 수 있는 4K 사이즈의 메모리를 요청하는 것이 좋습니다. 물론 할당된 페이지의 약간의 잔여 공간을 활용하지 못하긴 합니다. 하지만 그 대신에 위와 같은 단편화 현상은 방지 할 수 있습니다. 참고로 MSDN에서는 드라이버의 MmCm ( MmAllocateContiguousMemory ) 이용은 충분히 길게 사용하도록 권고 하고 있습니다. 라이브 디버깅을 할 때, 여러분은 연속적으로 할당 되고 해제 된 MmCm을 볼 것 입니다.

참고 링크
http://msdn.microsoft.com/en-us/library/ms801986.aspx
http://blogs.msdn.com/ntdebugging/archive/2006/12/18/Understanding-Pool-Consumption-and-Event-ID_3A00_--2020-or-2019.aspx

BlogIcon MayStyle (2010.03.12 16:42 신고) R | X
아... MmCm 을 이용할 경우에 Pool memory의 단편화가 발생 할 수 있는 거군요...^^
그럼 4K 단위로 사용하게 되면 스테판씨 말처럼 단편화를 줄이 수 있겠네요.

Name   Password   Home   Secret   Submit
  Windows Internals 다시보기 16 
작성일시 : 2010. 3. 11. 00:53 | 분류 : Windows Server/Kernel

* 아직은 많이 부족하기 때문에 제가 자신이 생길 때 까지 본 글은 제 블로그에 대한 링크만 허용합니다.

드디어 돌고 돌아 커널에 대한 설명을 시작합니다. 아직도 절반을 못 왔군요…ㅜㅜ

Privilege Level

윈도상에서 동작하는 모든 실행 코드들은 Privilege Level 을 할당 받습니다. 이 Privilege Level 이란 컴퓨터의 안정성을 위하여 임의로 설계 해 놓은 권한으로써 0레벨이 가장 높은 권한에 해당합니다. 실제로 CPU가 메모리를 참조하는 단위인 세그먼트에는 이 권한을 명시해 놓은 DPL flag가 있습니다.

그래서 어떤 메모리를 참조하게 되면 우선은 DPL을 검사한 다음 PDE 및 PTE의 User/Supervisor bit를 검사하게 됩니다.
하지만 윈도우즈의 경우 가상 메모리 상에서 커널레벨과 유저레벨이 분리가 되어 있어 DPL 검사는 생략하고 PDE와 PTE의 User/Supervisor bit 검사를 통해 해당 메모리의 접근 가능 유무를 판단하게 됩니다.

image

앞에서 설명한 것과 같이 0 레벨은 가장 권한이 높습니다. 이는 가장 신뢰를 할 수 있다는 의미로써, OS Kernel의 실행 영역이며, 레벨 3은 가장 신뢰가 낮은 영역으로 이 영역에서 동작하는 Application의 문제로 인한 영향의 정도를 줄일 수 있습니다.

이와 같은 신뢰성 확보를 위해 3레벨에서는 실행 가능한 명령어를 제한 하고 있습니다.
이렇게 커널에서만 실행 되는 명령어는 시스템과 관련된 명령어들 (예 : 인터럽트 처리 / cli, sti , IO 처리 / in, out ) 등이 있으며, 이를 통해 우리는 커널의 가장 중요한 역활은 인터럽트 처리라는 것을 확인 할 수 있습니다.

Name   Password   Home   Secret   Submit
  Windows Internals 다시보기 15 
작성일시 : 2010. 3. 9. 20:19 | 분류 : Windows Server/Kernel

* 아직은 많이 부족하기 때문에 제가 자신이 생길 때 까지 본 글은 제 블로그에 대한 링크만 허용합니다.

쓰래드의 상태 변화

image

윈도우에서 실행 되는 쓰래드들은 실제로 위의 8가지 상태 중 한 가지의 상태가 됩니다.
먼저 init 단계를 쓰래드의 생성 단계입니다. 이렇게 생성된 쓰래드는 먼저 우선 순위가 설정되지 않은 상태 Deferred Ready에서 대기하다 우선 순위가 결정 되면, Ready 상태에서 대기하게 됩니다. 그리고 이중 우선 순위가 가장 높은 쓰래드를 선택하고 다음 번에 실행 하도록 합니다. 이렇게 스케줄러에 의해 다음 번에 실행하도록 결정된 쓰래드는 Standby 상태가 됩니다. 이 Standby 상태의 쓰래드는 현재 Running 중인 쓰래드와 우선 순위를 비교하여 자신이 우선 순위가 높은 경우 해당 스래드를 디스패치 시킨 후 자신이 실행 (Running 상태) 되게 되고, 우선 순위가 낮은 경우 해당 쓰래드가 실행 하도록 할당 받은 시간 (퀀텀) 을 다 소비할 때 까지 기다리게 됩니다. 이렇게 실행 중인 쓰래드는 대부분 동기화 객체 WaitForSingleObject나 Sleep 에 의해 자발적으로 대기하거나 예를 들어 페이지 아웃된 페이지를 찾아 메모리에 올리는 것과 같이 시스템에 의해 일시 정지하게 되는데, 이 상태를 Wait 라 합니다. 또한 대기 상태 중 쓰래드의 커널 스택이 디스크로 페이지 아웃되어 바로 준비 상태로 가지 못하는 상태를 Transition 이라 하며, 쓰래드의 실행이 종료 된 경우는 Terminate 상태라 합니다.

스레드의 우선 순위

image

Windows 의 스레드 스케줄링은 스레드 우선 순위와 라운드 로빈 방식을 조한한 형태 입니다. 위 그림과 같이 실시간 처리가 필요한 스레드의 경우 우선 순위를 높게 주고, 다소 느린 처리가 가능한 경우에는 우선 순위를 낮게 준 후 동일 우선 순위 사이에서는 큐의 가장 앞에 있는 스레드를 처리하게 됩니다.

스레드가 CPU를 점유하는 시간 간격을 앞서 설명한 것처럼 퀀텀이라고 합니다. Windows 2000 이후의 OS에서는 퀀텀 부스팅이 가능 합니다. 이는 퀀텀 시간을 늘려 주는 것으로 예를 들어 활성 윈도우에서 실행 되는 스레드의 경우 해당 스레드의 퀀텀 부스팅을 통해서 유저 엑션에 대한 응답속도를 높이고 있습니다.

스레드는 퀀텀 기간 동안 CPU를 점유하게 됩니다. 하지만 아래와 같은 경우 스레드의 실행은 잠시 멈추게 됩니다.

1. 자발적으로 세마포 및 뮤텍스 와 같은 객체를 획득하기 위해 대기하는 경우 (WaitForSingleObject 등 ) : 대기 상태로 전환
2. 더 높은 우선 순위의 스레드 처리를 위한 대기 : 큐의 맨 앞에서 대기 (선점)
3. 퀀텀 시간 소모 : 큐의 맨 뒤에서 대기

위의 그림과 같이 스레드의 우선 순위는 0 ~ 31 까지의 32단계의 값을 갖습니다. 이 중 16이상의 값을 갖는 스레드를 실시간 수준으로 15이하 1이상의 스레드를 가변 수준이라 부릅니다.

우선 순위가 1 ~ 15인 경우 스레드는 처음 프로세스의 우선 순위에 준한 기본 우선 순위를 갖으나, 시스템에 의해 우선 순위가 올라갈 수 있습니다. 예를 들어 실행 준비가 끝난 스레드가 일정 시간 동안 실행 기회를 얻지 못한 경우 Window는 일시적으로 해당 스레드의 우선 순위를 높여 줍니다. 하지만 16이상인 경우에는 고정된 일정한 우선 순위값을 갖게 됩니다.

우선 수위 수준
실시간 : 24
높음 : 13
보통 이상 : 10
보통 : 8
보통 이하 6
Idle : 4

우선 순위가 올라가는 경우
I/O 연산의 완료
실행 이벤트 및 세마포를 기다린 경우
포어 그라운드 프로세스의 스레드들이 Wait를 완료한 직후
GUI 스레드가 윈도우 활동에 의해 깨어난 경우
실행 준비가 완료된 스레드가 한동안 실행 되지 못할 때

동기화

Windows에서 제공하는 동기화 방법은 아래와 같습니다.

동기화 객체

용도

사용 방법

Critical_Section

동일 프로세스내 스래드간 배타적 동기화

InitializeCriticalSection

EnterCriticalSection

LeaveCriticalSection

DeleteCriticalSection

뮤텍스

리소스 배타적 제어

CreateMutex

WaitForSingleObject

ReleaseMutex

ClosedHandle

세마포어

뮤텍스와 비슷하며 카운터를 관리 (해당 카운터 만큼 공동 소유함)

CreateSemaphore

OpenSemaphore

WaitForSingleObject

ReleaseSemaphore

이벤트

다른 스레드에 이벤트를 알림

CreateEvent

OpenEvent

SetEvent

ResetEvent

PulseEvent

Name   Password   Home   Secret   Submit
  내가 생각하는 윈도 커널이란? 
작성일시 : 2010. 3. 9. 10:09 | 분류 : Windows Server/Kernel

이전에는 메모리 영역의 구분이네 뭐네 복잡하게 이야기 했는데…
생각해보니 단순한거 같다.

그저… 사용할 수 있는 명령어의 한계가 없다 = 커널 그리고 한계가 있다 = 유저 모드…
다시 말해서 CPU에서 이미 의도된 설계를 통해 구분하는 것이 옮을 듯 하다.

물론 Windows의 경우 가상 메모리 주소의 영역이 분리되어 있는 것은 맞다.
하지만 이는 Windows 디자인에 대한 부분이고, 실제로 커널 권한이라고 하면 바로 CPU구조… 일반적으로 Privileged Level 에 기인한다고 보겠다.

이러한 Priviledged Level 이 높은 명령어를 실행 하는 방법은 3가지가 있다.
1. 인터럽트
2. 시스템 콜 (파일을 쓴다든지 하는 일들…)
3. 콜 게이트 (유저 모드에서 권한을 획득하여 필요한 명령어 수행)

정상적인 상태에서 커널에 일을 시키는 방법은 바로 위의 3가지 이고, 이는 높은 권한이 필요한 명령어를 수행 할때 필요한 것이라고 하겠다.

Name   Password   Home   Secret   Submit
  Windows Internals 다시보기 14 
작성일시 : 2010. 2. 19. 15:08 | 분류 : Windows Server/Kernel

* 아직은 많이 부족하기 때문에 제가 자신이 생길 때 까지 본 글은 제 블로그에 대한 링크만 허용합니다.

쓰래드의 백미는 스택입니다.

가끔 우리는 어항과 물고기를 통해 이 프로세스와 쓰래드의 관계를 표현하게 됩니다.
즉 어항은 프로세스 실제 해엄을 치고 있는 물고기는 쓰래드가 되죠.

그 경우 이 물고기들은 자신이 해엄치는 자취를 남기게 되는데, 이를 콜 스택이라고 볼 수 있습니다. 다시 말해서 어떠한 일을 한다 즉 함수를 실행 할 때 그 함수 실행의 자취를 콜 스택으로 비유 할 수 있습니다.

우리가 만드는 함수는 아래와 같은 모습이 됩니다.

Int Procedure1( ) { --------- 4
Procedure2( )       --------- 5
}
Int Procedure2( ) { --------- 6
…                      --------- 7
}
Main( ) {              --------- 1
…                      --------- 2
Procedure1( );       --------- 3
…                      --------- 8
}

제가 함수 옆에 숫자를 붙여 놓았는데, 이는 실행 순서 입니다. 여러분도 아시는 것 처럼 이 함수들은 먼저 Main( )이 실행 된 다음 Procedure1( )이 호출 되고, 호출된 Procedure1( ) 이 실행 되고 다시 Procedure2( ) 가 실행되고 다시 Main( )이 실행 됩니다.

이를 위하여 컴퓨터에서는 복귀 주소를 이용하게 되는데, 아래 그림과 같이 먼저 Main( )이 실행 하면 먼저 Main( )의 복귀 주소가 저장이 됩니다. 그리고 Procedure1( )을 호출 하게 되면 스택에 역시 Procedure1 ( )의 복귀 주소가 저장이 되고, 다시 Procedure1 ( )에서 Procedure2 ( )가 호출되게 되면 역시 Procedure 2( )의 복귀 주소가 저장이 되고, 실행 코드가 실행 이 된 후 다시 차례로 자신을 호출했던 복귀 주소로 돌아가 실행 결과를 전달하게 됩니다.

image

이는 콜 스택이라는 형태로 Thread 상에 표현되게 됩니다.
즉 Main의 복귀 주소를 저장한 후 Procedure1 이 실행 되고, 이 Procedure1 실행 중 Procedure2 를 호출하는 경우 다시 Procedure1 의 복귀 주소를 스택에 저장한 후 Procedure2가 실행 되는 거죠. 역시 최종적으로 실행 되던 Procedure2가 실행이 끝나면 저장했던 복귀 주소를 통해 차례로 Procedure1 과 Main 이 실행 됩니다.
(참고로 콜 스택의 현재 실행 위치는 ESP 레지스터에 저장됩니다.)

아래는 사용자가 notepad의 윈도우의 형태를 변경하는 메시지를 입력하는 순간에 잡은 유저 쓰래드입니다.
자 쓰래드를 한번 들여다 보도록 하겠습니다.

아래에서 보시는 것 처럼 notepad.exe의 콜 스택에서 81bddcb0 이 실행 중이였습니다.
kd> !thread
THREAD 81bddcb0 Cid 094c.0950 Teb: 7ffdf000 Win32Thread: e19a7970 RUNNING on processor 0
Owning Process 81e5db78 Image: notepad.exe
ChildEBP RetAddr Args to Child
f7f8ad4c 808234cb 0007fefc 00000000 00000000 win32k!NtUserGetMessage (FPO: [SEH])
f7f8ad4c 7c8285ec 0007fefc 00000000 00000000 nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ f7f8ad64)
WARNING: Stack unwind information not available. Following frames may be wrong.
0007fed8 01002a3b 0007fefc 00000000 00000000 ntdll!KiFastSystemCallRet
0007ff1c 01007527 01000000 00000000 000a24b2 notepad!WinMain+0xe5 (FPO: [4,8,0])
0007ffc0 77e6f23b 00000000 00000000 7ffda000 notepad!WinMainCRTStartup+0x182 (FPO: [SEH])
0007fff0 00000000 010073a5 00000000 78746341 kernel32!ProcessIdToSessionId+0x209

아 보니깐 대충 “모듈명!함수명”으로 되어 있습니다.

그리고 이 콜 스택을 통해 해당 함수의 로컬 변수 뿐 아니라 매개 변수까지 모두 사용할 수 있습니다. 방법은 간단하죠. 하나의 함수가 선언되어 있다면 해당 함수의 복귀 주소가 저장이 될 것이고 차례로 지정해 놓은 변수들이 해당 스택에 쌓이게 될꺼니깐요. 만약 해당 변수를 매개 변수로 받는 다면 현재의 복귀 주소에서 함수 호출 시 사용한 매개 변수가 있는 위치만큼 위치를 더해서(4K 단위로) 값을 가져 올 수 있습니다.

그래서 이용하는 레지스터가 EBP입니다.

ESP는 현재 실행 하고 있는 콜 스택 상의 최 상위 실행 번지 입니다. 문제는 함수가 실행 함에 따라 ESP는 지속적으로 변경이 된다는 거죠. 그렇다면 우리가 사용하는 변수들을 단순위 ESP 값에 번지를 더하는 수준으로는 사용이 어려울 수 있습니다. 그래서 함수가 호출되게 되면 처음에 ESP의 값을 EBP 값으로 변경하고 이 EBP를 기준으로 변수를 접근하게 됩니다.

이렇게 함수들이 실행이 되게 되면 최종적으로는 쌓이다가 다시 꺼내게 되어 있습니다. 이때 사용하는 명령어가 바로 어셈블리어로 Ret 인데, 위의 예제에서는 RetAddr로 표현되고 있습니다. 이 Ret 명령어는 복귀 주소를 ‘Pop’ 즉 꺼내서 ‘Jmp’ 점프 즉 복귀하라는 의미입니다.

스택 트레이싱의 예
(처음에는 그냥 작성 하다 정말 좋은 글을 발견 했습니다. 출처: http://byung.egloos.com/4750412)

제가 간단한 프로그램을 만들어서 해당 프로그램을 통해서 확인 하도록 하겠습니다.
(먼저 스택은 포인터의 집합이고 실제 데이터는 힙에 산계하여 있음을 알려드립니다.)
프로그램은 아래와 같습니다.

#include "stdio.h"
int sum(int a, int b);
int min(int a, int b);
int _tmain(int argc, _TCHAR* argv[])
{

int a, b, c;
a=7;
b=3;
scanf("%d", &c);
sum(a,b);
min(a,b);
return 0;

}

nt sum(int a, int b)
{

int s;
s = a + b;
return s;

}

int min(int a, int b)
{

int m;
m = a - b;
return m;

}

Windbg를 통해서 해당 실행 파일을 실행 합니다.
먼저 int Sum() 함수에 BP 즉 브레이크 포인터를 걸겠습니다.
Kd> Bp DebugTest!sum
Kd> g

해당 함수가 실행 되는 상황에서 콜 스택을 확인 했습니다.
0:000:x86> k
ChildEBP RetAddr
002efdcc 010e1414 DebugTest!sum [d:\000. documents\visual studio 2008\projects\debugtest\debugtest\debugtest.cpp @ 22]
002efecc 010e1a88 DebugTest!wmain+0x54 [d:\000. documents\visual studio 2008\projects\debugtest\debugtest\debugtest.cpp @ 16]
002eff1c 010e18cf DebugTest!__tmainCRTStartup+0x1a8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 579]
002eff24 76fe3677 DebugTest!wmainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 399]
002eff30 77569d72 kernel32!BaseThreadInitThunk+0xe
002eff70 77569d45 ntdll32!__RtlUserThreadStart+0x70
002eff88 00000000 ntdll32!_RtlUserThreadStart+0x1b

자 그렇다면 이때 무슨일이 일어 나고 있는지 CPU 레지스터 정보를 보겠습니다.

0:000:x86> r
eax=00000003 ebx=7efde000 ecx=00000007 edx=700c13e8 esi=002efddc edi=002efecc
eip=010e1490 esp=002efdd0 ebp=002efecc iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246

DebugTest!sum:
10e1490 55 push ebp

위에서 저는 ESP의 한계로 인해 EBP를 이용한다고 말씀 드렸습니다. 막 스택에 EBP를 넣었군요. 그렇다면 다음에는 ESP의 값을 EBP에 넣어주겠죠? 하지만 지금은 그 단계는 아닙니다.

위에서 현재 실행되고 있는 스택의 주소는 002efdd0 입니다.
직접 스택을 보도록 하겠습니다.
로컬 변수 7과 3 역시 스택에 저장되어 있으며, 스택의 꼭대기에는 현재 복귀를 위한 주소가 저장되어 있습니다.

0:000:x86> dds esp
002efdd0 010e1414 DebugTest!wmain+0x54 [d:\000. documents\visual studio 2008\projects\debugtest\debugtest\debugtest.cpp @ 16]
002efdd4 00000007
002efdd8 00000003
002efddc 00000000
002efde0 00000000
002efde4 7efde000

자 스택의 ESP 에 있는 010e1414 함수를 확인 하기 위하여 디스어셈블리 해보도록 하겠습니다.
16 010e140f e84bfcffff call DebugTest!ILT+90(?sumYAHHHZ) (010e105f)
16 010e1414 83c408 add esp,8

그리고 이전에 설명 드린것과 같이 EBP를 기준으로 하여 로컬 변수 7과 3이 저장하는 것을 볼 수 있습니다.
13 010e13de c745f807000000 mov dword ptr [ebp-8],7
14 010e13e5 c745ec03000000 mov dword ptr [ebp-14h],3

보시는 것과 같이 자신의 EBP를 기준으로 포인터를 4 8 C… 즉 4 단위로 이동하면서 지정하는 것을 확인 할 수 있습니다.
위와 같이 DebugTest!ILT 를 호출합니다.
보시는 것과 같이 해당 함수의 다음 라인 즉 함수가 처리된 이후에 리턴 될 주소를 가르키고 있습니다.
ChildEBP RetAddr
002efdcc 010e1414

우리는 레지스터를 통해 현재 실행 중인 명령어 라인을 알 수 있습니다.
0:000:x86> r
eax=00000003 ebx=7efde000 ecx=00000007 edx=700c13e8 esi=002efddc edi=002efecc
eip=010e1490 esp=002efdd0 ebp=002efecc iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246

DebugTest!sum:
010e1490 55 push ebp

마지막으로 이 콜 스택은 위에서부터 프래임 0 번부터 프레임 1 2 3 으로 표현이 됩니다.
우리는 프래임을 확인하여 실제 프레임에서 사용하는 매게 변수를 확인 할 수 있습니다.
0:000:x86> .frame 0
00 002efdcc 010e1414 DebugTest!sum [d:\000. documents\visual studio 2008\projects\debugtest\debugtest\debugtest.cpp @ 22]
0:000:x86> dv /i /V
prv param 002efdd4 @ebp+0x08 a = 7
prv param 002efdd8 @ebp+0x0c b = 3
prv local 002efdc4 @ebp-0x08 s = 3079640
0:000:x86> .frame 1
01 002efecc 010e1a88 DebugTest!wmain+0x54 [d:\000. documents\visual studio 2008\projects\debugtest\debugtest\debugtest.cpp @ 16]
0:000:x86> dv /i /V
prv param 002efed4 @ebp+0x08 argc = 1
prv param 002efed8 @ebp+0x0c argv = 0x001a1350
prv local 002efeac @ebp-0x20 c = –858993460
prv local 002efeb8 @ebp-0x14 b = 3
prv local 002efec4 @ebp-0x08 a = 7

Name   Password   Home   Secret   Submit
 Prev   1   2   3   4   ···   11   Next