'메모리 블록'에 해당되는 글 2건

  1. 2010.03.24 메모리 계층(Memory Hierarchy)
  2. 2010.02.28 쓰레드(Thread)의 생성과 소멸

메모리 종류 :

1. 메인(Main) 메모리 : 램(RAM) (D램)

2. 레지스터(Register) : CPU 안에 내장되어 있어서 연산을 위한 저장소 제공

3. 캐쉬(Cache) : S램.  CPU와 램사이에서 중간 저장소 역할

4. 하드디스크(Hard Disk)와 이외 장치 : 하드 디스크, I/O 장치 등등



메모리 계층 구조(Memory Hierarchy) :


메모리들은 프로그램이 실행하는 동안 데이터의 입력 및 출력을 담당한다.

메모리들의 차이는 CPU 와의 거리에서 온다.

CPU와의 거리가 가까울수록 빠르고 용량이 작으며 멀수록 느리고 용량이 크다.(기술과 돈의 문제)

하드디스크에 있는 내용은 프로그램의 실행을 위해 메인 메모리로 이동한다.

메인 메모리에 있는 일부 데이터도 실행을 위해 L2 캐시로 이동한다.

L2 캐시에 있는 데이터 일부는 L1 캐시로 이동한다.

L1 캐시에 있는 데이터중 연산에 필요한 데이터는 레지스터로 이동한다.


반대로 연산에 필요한 데이터가 레지스터에 없으면 L1 캐시를 살펴본다. 없으면 L2캐시 없으면 메인 메모리,

그래도 없으면 하드디스크를 참조한다. 하드디스크에서 데이터를 찾은 후 다시 메인 메모리 L2 캐쉬 L1 캐시를 거쳐

레지스터로 데이터가 들어오게 되는데 이경우 극심한 속도저하가 발생한다.

(참고 :

캐시를 없애 중간단계를 줄이는 것이 속도가 빠르지 않냐 생각할수 있는데
L1 캐시와 L2 캐시에, 연산에 필요한 데이터가 존재할 확률이 90% 이상이다.따라서 캐시는 속도향상에 도움을 준다)



L1 캐시와 L2 캐시 :

시스템의 성능을 좌우하는 클럭속도는 느린쪽에 맞춰진다.

CPU는 고속화되었지만 메인 메모리의 처리속도는 이를 따라가지 못한다.

CPU가 연산을 하기 위해선 데이터를 가지고 와서 연산을 한 후 연산결과를 메모리에 저장한 후에

다음작업을 수행할 수 있다.

따라서 아무리 CPU가 빠르게 연산을 수행한다 하더라도 데이터를 가지오고 저장하는 작업이 느리다면

전체적인 처리속도는 결코 빠를수 없다.

L1캐시는 이러한 레지스터와 메인 메모리간의 속도차이에 의한 성능저하를 막기 위해

메인 메모리의 저장된 데이터 중 자주 접근하는 데이터를 저장한다.

L1 캐시는 CPU 내부에 존재하므로 L1 캐시에서 데이터를 참조할 경우 속도저하는 발생하지 않는다.

하지만 여전히 L1 캐시는 메인 메모리의 모든 데이터를 저장할 수 없기에 L1 캐시에 없는 데이터를

CPU가 요구할 경우 속도의 저하로 이어진다.

따라서 캐시를 하나 더둔다.(L1 캐시에 용량을 증가시키는데ㄷ에도 한계가 있다(돈과 기술))

L2 캐시까지 존재함으로써 메인 메모리에 대한 접근은 더욱 줄어든다.

따라서 병목현상은 L1캐시와 메인 메모리에서 L2 캐시와 메인 메모리로 발생지역이 옮겨지게 된다.



캐쉬(Cache)와 캐쉬 알고리즘 :

템퍼럴 로컬리티(Temporal Locality) : 한번 접근이 이뤄진 주소의 메모리 영역은 자주 접근한다.

스페이셜 로컬리티(Spatial Locality) : 접근하는 메모리 영역은 이미 접근이 이루어진 영역의 근처일 확률이 높다.

캐시 프렌드리 코드(Cache Friendly Code) : 템퍼럴 로컬리티와 스페이셜 로컬리티를 최대한 활용하여
                                                             캐시의 도움을 받을수 있도록 구현한 코드



캐시 알고리즘 :


캐시 힛(Cache Hit) : 연산에 필요한 데이터가 L1 캐시에 존재할 경우


캐시 미스(Cache Miss) : 연산에 필요한 데이터가 L1 캐시에 존재 하지 않을 경우
(참고 : 이경우 L2 캐시를 검사하며 L2 캐시 미스가 발생하면 메인 메모리에서 데이터를 가져온다)


데이터의 이동은 블록 단위로 진행하여 스페이셜 로컬리티의 특성을 성능향상에 활용한다.
(예 : 0x10000 번지의 데이터를 요청하면 0x10000을 포함한 블록 전체가 전송된다)

(참고 : 현재 L2 캐시는 CPU 내부에 존재한다)

메모리 계층 아래로 갈수록 전송되는 블록 크기가 커진다.

아래에 존재하는 메모리에 대한 접근 횟수를 줄여준다.


캐시 교체 정책(Cache's Replacement Policy) :

프로그램이 실행된느 동안 모든 메모리는 항상 채워져 있다.

메모리가 꽉 채워져 있어요 요구하는 데이터를 가지고 있을 확률이 높아지기 때문이다.

이때문에 가지고 있지 않은 데이터를 요구할 경우 메모리가 꽉 찾기 때문에 메모리 블록을 교체해야 한다.

블록 교체 알고리즘은 캐시 교체 정책에 의해 달라진다.
(참고 :

대표적 블록 교체 알고리즘 :
LRU(Least-Recently Used) : 가장 오래 전에 참조된 블록을 밀어내는 알고리즘)

'Windows > _System Programming' 카테고리의 다른 글

StackBasedOverflows-Windows-Part1 (기본 개념)  (2) 2011.06.07
메모리 컨트롤  (0) 2010.03.24
메모리 계층(Memory Hierarchy)  (0) 2010.03.24
MMF(Memory Mapped File)  (0) 2010.03.12
라이브러리(Library)  (0) 2010.03.11
비동기 I/O 와 APC  (0) 2010.03.08
Posted by Dakuo

쓰레드 생성 :

HANDLE CreateThread(
     LPSECURITY_ATTRIBUTES lpThreadAttributes,  // 보안속성(TRUE : 상속, NULL : 상속제외)
     SIZE_T dwStackSize,                                      // 쓰레드의 스택 크기
     LPTHREAD_START_ROUTINE lpStartAddress,    // 쓰레드로 동작하기 위한 함수(쓰레드 main)
                                                                          // 리턴타입 DWORD, 매개변수 타입 LPVOID(void*)
     LPVOID lpParameter,                                       // 쓰레드 함수에 전달할 인자 지정
     DWORD dwCreationFlags,                                // 쓰레드 생성 및 실행을 조절
     LPDWORD lpThreadId                                      // 쓰레드 ID를 전달받기 위한 변수의 주소값
);



쓰레드 소멸 :

1. return(일반적, 안정적) :

쓰레드 함수에서 return 은 쓰레드 종료

main 쓰레드의 return 은 프로세스 종료

쓰레드 함수의 return 값은 그 쓰레드의 커널 오브젝트에 저장된다.

리턴값 획득 :

BOOL GetExitCodeThread(
      HANDLE hThread,                 // 종료코드를 얻기 위한 쓰레드의 핸들
      LPDWORD lpExitCode            // 얻게 되는 종료코드를 저장할 메모리 주소
);


2. ExitThread(특정 위치에서 쓰레드의 실행을 종료) :

현재 실행 중인 쓰레드를 종료

VOID ExitThread(
      DWORD dwExitCode             // 커널 오브젝트에 등록되는 쓰레드 종료 코드를 지정
);                                             // GetExitCodeThread 함수를 통해 종료코드 획득 가능

이 함수를 사용하면 언제 어디서나 쓰레드를 종료 시킬수 있다.


(참고 : C++ 프로그래밍을 할때 주의해야 한다.

예를 들어 A, B 함수에 C++ 객체가 존재한다고 할때 C 함수에서 ExitThread 함수로 쓰레드를 종료할 경우
A, B 함수의 스택 프레임에 존재하는 소멸자가 호출되지 않아 메모리 유출 현상이 발생할 수도 있다.
return 문에 의한 쓰레드 종료가 가장 좋다)


3. TerminateThread(외부에서 쓰레드를 종료) :

강제종료 함수

따라서 종료에 필요한 여러 가지 일들을 처리하지 못하고 바로 종료된다.

BOOL TerminateThread(
      HANDLE hThread,                 // 강제 종료할 쓰레드의 핸들
      DWORD dwExitCode              // 종료할 쓰레드의 종료코드를 인자로 전달
);


4. 프로세스로부터 쓰레드 분리

쓰레드는 생성과 동시에 프로세스의 핸들 테이블에 그 핸들이 등록되므로 Usage Count 가 2가 된다.

따라서 쓰레드 생성시 얻은 핸들을 CloseHandle 로 반환하면 Usage Count = 1 이 된다.
 
쓰레드를 종료하면 Usage Count = 0 이 되어 쓰레드 커널 오브젝트가 소멸된다.



쓰레드의 성격과 특성 :

공유 영역 사용 :

ex)

total 을 전역변수로 선언했다. 따라서 모든 쓰레드는 공유 total 을 가지고 덧셈연산을 진행한다.

최종결과를 main 쓰레드가 참조한다.


동시접근의 문제점 :

위 ex) 에서 둘 이상의 쓰레드가 전역변수 total 에 동시접근할 경우 문제점이 생긴다.


컨텍스트 스위칭은 연중에도 빈번히 일어날 수 있기에 둘 이상의 쓰레드가 같은 메모리 영역을 동시에 참조하는 것은 문제를 발생시킨다.

따라서 멀티 쓰레드 기반의 쓰레드를 생성해야 한다.

uintptr_t _beginthreadex(   // CreateThread 함수와 파라미터 순서와 의미가 같다.(자료형과 리턴형의 차이가 있다)
         void *security,
         unsigned stack_size,
         unsigned (*start_address)(void *),
         void *arglist,
         unsigned initflag,
         unsigned *thrdaddr
); 

_beginthreadex 함수를 사용하면

쓰레드 함수에 여러 파라미터를 전달할 수 있다.

각각의 쓰레드를 위해서 독립적인 메모리 블록을 할당한다.
(스택 공간 + 고유한 레지스터 값 + 현재 명령 주소)    // CreatThread 함수 (스택 공간)

따라서 return 과 endthreadex 함수를 사용하여 할당한 메모리를 해제해야 한다.

void _endthreadex(unsigned retval);  // ExitThread 함수와 동일



쓰레드의 상태 컨트롤 :

쓰레드의 상태 변화(Suspend & Resume) : // 프로세스 상태 변화와 개념이 같다.
                                                            // http://dakuo.tistory.com/entry/프로세스의-생성과-소멸

쓰레드의 커널 오브젝트 멤버 Suspend Count 는 쓰레드의 상태를 나타낸다.

Suspend Count = 0 : 현재 실행중인 쓰레드(Ready)

Suspend Count > 0 : Block 상태

DWORD SuspendThread(
        HANDLE hThread          // Suspend Count++ 할 쓰레드의 핸들
);

DWORD ResumeThread(
        HANDLE hThread          // Suspend Count-- 할 쓰레드의 핸들
);

이 함수들은 변경되기 이전에 Suspend Count 를 리턴한다.


쓰레드의 우선순위 컨트롤 :

프로세스의 우선순위는 기존 우선순위라고 표현한다.

쓰레드는 추가로 상대적 우선순위를 갖는다.

 Priority   의미 
 THREAD_PRIORITY_LOWEST  -2
 THREAD_PRIORITY_BELOW_NORMAL  -1
 THREAD_PRIORITY_NORMAL  0 (Default)
 THREAD_PRIORITY_ABOVE_NORMAL  +1
 THREAD_PRIORITY_HIGHEST  +2

예)

NORMAL_PRIORITY_CLASS (기존 우선순위 9) + THREAD_PRIORITY_LOWEST(-2) = 7

즉, 이 쓰레드의 최종우선순위는 7이다


쓰레드의 상대적 우선순위 변경 :

BOOL SetThreadPriority(
        HANDLE hThread,        // 우선순위를 변경할 쓰레드의 핸들
        int nPriority                  // 우선순위 상수값
);

int GetThreadPriority(
        HANDLE hThread         // 우선순위를 확인할 쓰레드의 핸들
);

'Windows > _System Programming' 카테고리의 다른 글

쓰레드(Thread) 실행순서 동기화  (0) 2010.03.04
쓰레드(Thread) 메모리 접근 동기화  (0) 2010.03.04
쓰레드(Thread)의 생성과 소멸  (0) 2010.02.28
쓰레드(Thread)  (2) 2010.02.28
환경변수  (0) 2010.02.25
스케줄링 알고리즘과 우선순위  (0) 2010.02.25
Posted by Dakuo


티스토리 툴바