원본 : 

 

번역본 :



나머지 2개는 좀 한가해지는대로 해서 올리겠습니다. ㅎㅎㅎ 
Posted by Dakuo
원본 : 


번역본 : 


제가 직접 번역을 해보았는데 오역이 있으면 알려주시기 바랍니다.
(고등학교 때도 독해 안해봤는데 ... ㅋㅋㅋ 제가 이런걸 해볼줄이야 ... ㄷㄷ)

그리고 이걸 기본으로 해서 제가 다시 문서를 작서할 생각입니다.

Posted by Dakuo

메모리 상태 :


페이지의 개수 = 가상 메모리의 크기 / 페이지 하나당 크기

페이지 개수는 가상 메모리의 크기에 비례하며(가상 메모리는 몇 비트 환경인지에 비례 (ex. 32비트 4GB)),

모든 페이지는 Reserve, Commit, Free 세가지 중 하나의 상태를 지닌다.

Commit : 물리 메모리에 할당된 상태

Reserve : Free 와 Commit 의 중간상태이다. 해당 번지에 대해 예약을 한다.
              다른 메모리 함수가 물리 메모리에 해당 번지에 할당하지 못하도록 한다.
              하지만 물리 메모리의 소비는 발생하지 않는다.
                
Free : 물리 메모리 할당이 이뤄지지 않은 상태


메모리 할당의 시작점과 단위 확인 :

가상 메모리 시스템은 페이지 단위로 관리된다.

페이지의 중간 위치에서부터 할당을 시작할수 없으며, 페이지 크기의 배수 단위로 할당한다.

Allocation Granularity Boundary : 메모리 할당의 시작 주소가 되는 기본 단위
(참고 : 메모리가 지나치게 조각나는것과 관리의 효율성을 이유로 페이지 크기의 배수보다 더 넓은 값을 가진다)

Allocation Granularity Boundary 과 페이지 크기 확인 :

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

int _tmain(int argc, TCHAR *argv[])
{
          SYSTEM_INFO si;
          DWORD allocGranularity;
          DWORD pageSize;
 
          GetSystemInfo(&si);
          pageSize = si.dwPageSize;
          allocGranularity = si.dwAllocationGranularity;

          _tprintf(_T("Page Size : %u Kbyte \n"), pageSize/1024);
          _tprintf(_T("Allocation granularity : %u Kbyte \n"), allocGranularity/1024);
 
          return 0;
}



가상 메모리 컨트롤 :

페이지 상태를 RESERVE 나 COMMIT 로 변경할때 사용하는 함수

LPVOID VirtualAlloc(
          LPVOID lpAddress,       // 예약 및 할당하고자 하는 메모리의 시작 주소
                                            // (일반적 NULL 전달, RESERVED -> COMMIT 일때 해당페이지 시작 주소 지정)
          SIZE_T dwSize,                // 할당하고자 하는 메모리의 크기를 바이트 단위로 지정
                                                 // 메모리의 할당은 페이지 크기 단위로 결정
          DWORD flAllocationType,    // 메모리 할당의 타입.
                                                 // RESERVE : MEM_MRESERVE,          COMMIT : MEM_COMMIT 
          DWORD flProtect                // 페이지별 접근방식에 제한을 두는 용도
                                                 // RESERVE 상태로 변경할때는 접근을 허용하지 않는 PAGE_NOACCESS ,
                                                 // COMMIT 상태로 변경할때는 읽고/쓰기 모드인 PAGE_READWRITE 전달
);

함수 호출이 성공하면 할당이 이뤄진 메모리의 시작 주소를 리턴


할당된 페이지를 되돌리는 함수

BOOL VirtualFree(
           LPVOID lpAddress,            // 해제할 메모리 공간의 시작 주소 
           SIZE_T dwSize,                // 해제할 메모리 크기를 바이트 단위로 지정
           DWORD dwFreeType         // MEM_DECOMMIT : 해당 페이지의 상태를 RESERVE 상태로 되돌린다.
                                                  // MEM_RELEASE : 해당 페이지의 상태를 FREE 상태로 되돌린다.
                                                  //                           두번째 전달인자 dwSize 는 반드시 0 이어야 한다.
);



힙 컨트롤 :

디폴트 힙(Default Heap) :

디폴트 힙을 구성하는 페이지들은 RESERVE 상태이다. 일부는 성능을 위해 COMMIT 상태일수도 있다.

C 언어의 malloc 함수와 free 함수, C++ 언어의 new와 delete 를 사용해서 힙영역을 사용할 경우

프로세스를 생성할때 생성되는 힙, 즉 1M 바이트 크기의 디폴트 힙에 메모리를 할당한다.
(COMMIT 와 RESERVE 상태를 오간다)

프로세스에 기본적으로 할당되는 힙이며 프로세스 힙(Process Heap)라고도 한다.


디폴트 힙 컨트롤 :

디폴트 힙의 기본 크기는 1MB 이다. 링커(Linker)옵션을 통해 크기를 변경할 수 있다.

/HEAP : reserve, commit  (ex. /HEAP 0x200000, 0x10000  :  디폴트 힙의 크기 : 2MB,       COMMIT 크기 : 64KB)

힙은 동적이라 기본 크기 1MB 로 생성이 된 후 필요에 따라 그 크기가 Windows 시스템에 의해 자동으로 늘어난다.



Windows 시스템 힙(Dynamic Heap) :

Windows 시스템 함수를 통해 여러 힙을 추가로 생성할 수 있다.

가상 메모리 범위 내에서 프로세스 생성시 만들어지는 디폴트 힙 이외에 필요로 하는 힙을 얼마든지 생성할 수 있다.


힙(Dynamic Heap) 생성이 가져다 주는 이점 :

1. 메모리 단편화의 최소화에 따른 성능 향상 :



2. 동기화 문제에서 자유로워짐으로 인한 성능 향상 :

힙은 쓰레드가 공유하는 메모리 영역이다. 따라서 둘 이상의 쓰레드가 동시접근 할때 문제가 발생할수 있으므로

Windows 내부적으로 동기화처리를 해준다.(여기서 동기화는 메모리 할당과 해제)

같은 주소 번지에 둘 이상의 쓰레드가 동시에 메모리를 할당 및 해제하는 상황이 발생할 경우 메모리 오류(Corrupt)가
발생하므로 디폴트 프로세스 힙은 동기화 처리를 하는데 쓰레드마다 독립된 힙을 가지고 있다면

이러한 동기화가 필요없으므로 성능이 향상된다.


힙(Dynamic Heap) 컨트롤 :

힙을 생성하는 함수 :

HANDLE HeapCreate(
         DWORD flOptions,                 // 생성되는 힙의 특성을 부여 (0 을 전달시 가장 일반적인 힙 생성)
                                                   // HEAP_GENERATE_EXCEPTIONS : 오류 발생시 NULL이 아닌 예외 발생
                                                   // HEAP_NO_SERIALIZE : 생성된 힙의 메모리 할당과 해제에 대해
                                                   //                                   동기화 처리를 하지 않는다.
                                                   // 둘 이상의 속성을 비트 단위 연산자 OR( | )로 동시 지정 가능
         SIZE_T dwInitialSize,            // dwMaximumSize 에서 지정한 메모리 중에서
                                                   // 초기에 할당할 COMMIT 페이지를 지정한다 
         SIZE_T dwMaximumSize      // 생성되는 힙의 크기 결정.
                                                   // 지정하는 크기에 해당하는 페이지의 수만큼 RESERVE 상태가 된다.
                                                   // 0이 아닐 경우 힙은 증가가능한 메모리(Growable Heap)가 된다
);

힙을 소멸하는 함수 :

BOOL HeapDestroy(
         HANDLE hHeap                    // 반환하고자 하는 힙의 핸들
); 

힙에 메모리를 할당하는 함수 :

LPVOID HeapAlloc(
         HANDLE hHeap,               // 메모리 할당이 이뤄질 힙의 핸들
         DWORD dwFlags,             // HEAP_GENERATE_EXCEPTIONS : 오류 발생시 NULL이 아닌 예외 발생
                                               // HEAP_NO_SERIALIZE : 함수호출시 동기화 처리되지 않는다.
                                               // (HeapCreate 함수호출에서 지정했다면 중복지정할 필요는 없다)
                                               // HEAP_ZERO_MEMORY : 할당된 메모리는 0으로 초기화
                                               // 둘 이상의 속성을 비트 단위 연산자 OR( | )로 동시 지정 가능
         SIZE_T dwBytes               // 할당하고자 하는 메모리의 크기를 지정
                                               // (증가가능한 힙이 아닐 경우 최대 크기 0x7FFF8)
);

힙에 메모리를 해제하는 함수 :

BOOL HeapFree(
         HANDLE hHeap,                // 해제할 메모리를 담고 있는 힙을 지정
         DWORD dwFlags,              // HEAP_NO_SERIALIZE 가 인자로 올 수 있다,     일반적 : 0
                                                // (HeapCreate 함수호출에서 지정했다면 중복지정할 필요는 없다)
         LPVOID lpMem                 // 해제할 메모리의 시작 주소 지정
);

Posted by Dakuo

메모리 종류 :

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

개념 : File 을 Memory 에 Mapping(연결)


가상 메모리 중 파일에 연결되어 있는 영역에 데이터를 저장한다.

이렇게 메모리에 저장된 데이터는 실제 파일에도 영향을 미친다.

즉, 데이터가 메모리뿐만 아니라 메모리에 연결된 파일에도 저장되는 것이다.


장점 :

1. 프로그래밍 편리 : 메모리상에 저장된 데이터를 조작하는 방식으로 파일 내 데이터를 조작할 수 있다.

2. 성능 향상 : 메모리는 중간에서 파일과 데이터의 캐쉬 역할을 하여 성능을 향상시킨다.





MMF 구현 :

1. 파일 개방 : CreateFile()

2. 파일 연결 오브젝트 생성 : CreateFileMapping()

HANDLE CreateFileMapping(
          HANDLE hFile,                                        // 메모리에 연결할 파일의 핸들
          LPSECURITY_ATTRIBUTES lpAttributes,    // 보안속성
          DWORD flProtect,             // 파일과 연결된 메모리의 접근권한
                                               // PAGE_READONLY : 읽기만 가능
                                               // PAGE_READWRITE : 읽기 쓰기 가능
                                               // PAGE_WRITECOPY : 데이터를 쓸때 복사한다
                                               // SEC_COMMIT , SEC_RESERVE. 비트 단위 OR 연산으로 함께 설정가능        DWORD dwMaximumSizeHigh,                 // 연결할 메모리 최대 크기의 상위 4바이트
                                                                       // 4GB 이상이 아니라면 0
          DWORD dwMaximumSizeLow,                 // 연결할 메모리 최대 크기의 하위 4바이트
                                                                      // 0 전달 : 첫번째 인자로 전달된 핸들의 파일 크기
          LPCTSTR lpName                                   // 파일 연결 커널 오브젝트의 이름
);


3. 가상 메모리에 파일 연결 : MapViewOfFile()

LPVOID MapViewOfFile(
          HANDLE hFileMappingObject,       // 파일 연결 커널 오브젝트의 핸들
          DWORD dwDesiredAccess,          // 연결된 메모리의 접근 권한
          DWORD dwFileOffsetHigh,             // 메모리에 연결할 파일의 오프셋(시작 위치)의 상위 4 바이트 지정
          DWORD dwFileOffsetLow,             // 메모리에 연결할 파일이 오프셋의 하위 4 바이트 지정
          SIZE_T dwNumberOfBytesToMap // 메모리에 연결할 실제 크기를 바이트 단위로 지정
);                                                         // 오프셋을 기준으로 지정된 바이트만큼 메모리에 연결.
                                                            // 0 전달시 오프셋부터 파일의 끝까지 메모리에 연결

4. 해제 : UnmapViewOfFile()

BOOL UnmapViewOfFile(
          LPCVOID lpBaseAddress  // 연결 해제할 메모리의 시작 주소 (MapViewOfFile 함수 호출시 반환된 주소값)
);


(추가 : 메모리에 캐시된 데이터를 즉각 파일에 저장하는 함수 :

BOOL FlushViewOfFile(
          LPCVOID lpBaseAddress,                         // 파일에 저장할 메모리의 시작 주소
          SIZE_T dwNumberOfBytesToFlush            // 파일에 저장할 데이터 크기를 바이트 단위로 지정
);                                                                     // 0 전달 : 연결된 메모리 영역의 끝까지 파일로 저장)

예제보기




Copy-On-Write(COW) :

개념 : 데이터를 쓸때 복사한다

기본 데이터를 참조하다가 변경이 필요할 때 데이터를 복사해서 저장하며 그 이후부터는 복사본을 참조한다.

(참고 : 원본 데이터는 보존된다)


구현 :

CreateFile 함수를 호출할때 읽기/쓰기모드로 개방한다.

CreateFileMapping 함수의 세번째 인자를 PAGE_WRITECOPY 로 설정한다.

MapViewOfFIle 함수의 두번째 인자를 FILE_MAP_COPY 로 설정한다.

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

메모리 컨트롤  (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
디렉터리 컨트롤  (0) 2010.03.08
Posted by Dakuo

라이브러리(Library) : 여러 프로그램에서 자주 사용하는 함수와 데이터들을
                              실행이 가능한 바이너리 형태로 묶어놓은 파일을 의미한다.
(참고 : 즉, 함수와 데이터들의 정의가 컴파일된 바이너리 코드로 라이브러리에 존재한다)

(ex.

 C Run - Time Library  Characteristics
 Libcmt.lib  Multithreaded, static link
 Libcmtd.lib  Multithreaded, static link (debug)
 Msvcrt.lib  Multithreaded, dynamic link
 Msvcrtd.lib  Multithreaded, dynamic link (debug)
---d.lib : 디버그 모드로 컴파일)



Static Library : 정적 라이브러리

개념 :

헤더파일(.h)           ->
                                               ->               라이브러리 파일(.lib)
소스파일(.cpp)       ->     (Compile(컴파일))
                                                                                                    ->          실행파일(.exe)
헤더파일(.h)           ->                                                              (Link(링크))
                                               ->               오브젝트 파일(.obj)
소스파일(.cpp)       ->     (Compile(컴파일))


위와 같이 .lib 파일과 .obj 파일을 합쳐 .exe 실행파일을 만든다.

즉, 라이브러리 파일이 실행파일 안에 포함된다.


정적 라이브러리의 메모리 사용 :

                                   AAA.exe 가상 메모리                      <-정적 라이브러리 영역->
                                  BBB.exe 가상 메모리                      <-정적 라이브러리 영역->
 
동일한 정적 라이브러리를 포함하는 두 개의 프로세스 AAA와 BBB가 있다.

AAA가 실행을 하다가 멈추고 BBB를 실행할 때 메인 메모리에 BBB의 페이지가 올라가게 된다.

페이지 2,3,5 는 BBB의 2,3,5로 교체되며 정적 라이브러리 영역 8,9도 BBB의 페이지로 교체된다.

따라서 메인 메모리의 전체 AAA 프로세스 페이지가 모두 BBB 프로세스 페이지로 바뀐다.

(추가 : 직접 제작한 라이브러리 연동하기
#pragma comment(lib, "포함할 라이브러리 이름") 를 소스코드에 추가한다
라이브러리 파일은 표준 검색 경로를 통해 찾는다)



Dynamic Link Library : 동적 라이브러리

개념 :

실행파일의 일부로 포함되지 않고 독립적으로 저장되는 라이브러리.

DLL을 사용하고자 하는 프로세스는 자신의 가상 메모리 주소에 DLL을 매핑(Mapping)시키고 DLL을 사용한다.


동적 라이브러리의 메모리 사용 :

                                   AAA.exe 가상 메모리
                                    BBB.exe 가상 메모리

DLL 기반의 두개의 프로세스 AAA와 BBB 가 있다.

AAA가 실행을 하다가 멈추고 BBB를 실행할 때 메인 메모리에 BBB의 페이지가 올라가게 된다.

2,3,5 페이지가 BBB의 2,3,5 페이지로 변경된다.

이때 뒤에 8, 9 페이지는 AAA가 사용하던 페이지 그대로 존재한다.

즉, 별도의 파일로 존재하는 DLL을 AAA와 BBB가 공유하게 된다.
(참고 : 둘 이상의 프로세스가 동일한 DLL을 공유할 경우, 메인 메모리에 페이지 단위로 공유가 이뤄진다)

만약에 페이지 8, 9가 아닌, 동일한 DLL의 다른 페이지를 프로세스 BBB가 필요로 한다면 메인 메모리에

로드되있지 않으므로 새로 로드하게 된다.


DLL의 연결 방법 :

1. 암묵적 연결(Implicit Linking) :

__declspec(dllimport) : DLL로부터 제공받을(Import) 함수를 선언할 때 사용한다.

__declspec(dllexport) : 외부에 제공할(Export) 함수를 선언할 때 사용한다.

(참고 : __declspec 는 외부에 제공할 or 제공받을 함수 및 변수 선언에 사용한다.
마이크로소프트에서 제공하는 추가석인 선언문이다)

.lib 파일과 .dll 파일의 용도 :

AAAdll.cpp              ->               AAAdll.obj   
                 (Compile(컴파일))                                ->             AAAdll.exe       < --- >        AAAdll.dll
                                                AAAdll.lib     (Link(링크))                           실행중에 참조

DLL을 생성하는 과정에서 만들어진 .lib 파일은 DLL이 제공하고자 하는 함수 정보(변수나 이름정보)를 지닌다.

즉,   .lib 파일은 링커에게 AAAdll.obj에서 호출하는 함수는 AAAdll.dll 파일에 정의되어 있다고 알려준다.

.lib 파일은 링크시 필요하고 .dll 파일은 실행할 때 필요하다.

(참고 :

정적 라이브러리를 만들때와 마찬가지로 .lib 파일을 연동해야 한다.
#pragma comment(lib, "포함할 라이브러리")를 사용한다. 라이브러리 파일은 표준 검색 경로를 통해 찾는다

네임맹글링 : C++ 컴파일러는 컴파일 과정에서 네임 맹글링(Name Mangling : 이름을 엉망으로 만든다)작업을 한다.

정의된 함수 이름을 정해진 규칙에 의해 바꿔버린다. 따라서 C++ 컴파일러로 컴파일된 DLL을 C 프로그램에서

참조한다면 에러가 발생한다.(컴파일러 회사에 따라서도 네임 맹글링 규칙이 다르다)

따라서 C++ 컴파일러가 네임 맹글링을 하지 못하도록 extern "C" 키워드를 함수 선언부에 붙여준다)


2. 명시적 연결(Explicit Linking) :

소스코드 내에 DLL 연결에 대한 코드가 존재하므로 .lib 파일이 필요가 없다.

DLL                                                                      

           void DLLFunc(int val) 
        |                                 ▲ 
        |                           FreeLibrary              
        |                                 | 
LoadLibrary                          |                    
       ▼                                 |                                                                                  프로세스 가상 메모리

           void DLLFunc(int val)              

                       |
             GetProcAddress
                       |
                       ▼ 
      DLLFunc 함수 포인터 획득


HMODULE LoadLibrary(LPCTSTR lpFileName        // 프로세스 주소 공간으로 매핑시킬 DLL 이름을 지정
);

FARPROC GetProcAddress(
           HMODULE hMoudle;           사용하고자 하는 함수나 변수가 속해 있는 DLL 모듈의 핸들           
           LPCSTR lpProcName          찾고자 하는 함수의 이름을 지정
);      lpProcName 에서 지정한 함수의 함수포인터를 반환

BOOL FreeeLibrary(HMOUDLE hMoudle                 // 반환하고자 하는 DLL 모듈의 핸들
);

(참고 : 프로세스는 내부적으로 DLL의 레퍼런스 카운트(참조 횟수 : Reference Count)를 계산한다.
LoadLibrary 함수 호출 시 지정된 DLL의 레퍼런스 카운트는 1씩 증가하고, FreeLibrary 함수 호출 시
지정된 DLL의 레퍼런스 카운트는 1씩 감소한다. 레퍼런스 카운트가 0이 되면 프로세스의 가상 메모리에서 해제된다)

DLL 명시적 연결방법의 장점 :

1) DLL이 필요한 시점에서 로딩되고, 불필요해지면 반환하기 때문에 메모리가 절약된다.

2) 프로그램 실행 중에 DLL 교체 및 선택이 가능하다.

3) 필요한 순간에 하나씩 DLL을 로딩할 수 있기 때문에 실행까지 걸리는 시간이 짧고,
    DLL 로딩에 걸리는 시간을 분산한다.

(참고 : 암묵적 연결 vs 명시적 연결

명시적 연결에 성능의 장점에도 불구하고 코드의 간결성 때문에 암묵적 연결방법을 더 선호한다)


(추가 : DLL과 메모리

DLL은 물리 메모리에 한번 올라가면, 이 DLL을 참조하는 프로세스가 모두 종료될 때까지 물리 메모리에 존재한다.

(예 :

1. 프로세스 AAA가 실행된다. 이 프로세스는 AAA.dll 을 참조한다.

   따라서 AAA.dll 은 프로세스 AAA에 가상 메모리에 매핑되면서 물리 메모리에 올라간다.

2. AAA.dll 을 참조하는 프로세스 BBB가 실행된다.
 
    AAA.dll 을 프로세스 BBB에 매핑하지만 물리 메모리에는 이미 존재하므로 로드하지 않는다.

3. 프로세스 AAA가 종료되었지만 프로세스 BBB가 AAA.dll 을 참조므로 AAA.dll 은 메모리에 남아있다.

4. 프로세스 BBB가 종료되었다. AAA.dll 을 참조하는 프로세스가 하나도 없으므로 메모리에서 반환된다)

(참고 : 처음 DLL이 빌드될 때 DLL이 할당되어야 할 가상 메모리 주소가 링커(Linker)에 의해 결정도니다.

만약에 AAA.dll을 0x10000000 에 매핑하기로 했다면, 모든 프로세스에서 0x10000000 에 매핑한다.

만약에 이주소에 다른 dll이 매핑되있다면 다른 가상 메모리 주소에 매핑되며

이때는 다른 프로세스에 의해 해당 dll이 이미 메인 메모리에 로드되었더라도 또 로드되게 된다))

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

메모리 계층(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
디렉터리 컨트롤  (0) 2010.03.08
파일 I/O  (2) 2010.03.08
Posted by Dakuo

개념 :


동기 I/O : I/O 작업의 수행을 위해 호출된 함수가 블로킹 상태에 놓이기 때문에 CPU는 블로킹 상태에서
              반환될 때까지 일을 하지못한다.

비동기 I/O : I/O 작업의 수행을 위해 함수를 호출하자마자 반환한다. 따라서 CPU는 그다음 작업을 진행한다.



중첩(Overlapped) I/O :


넌블로킹(Non-Blocking) 함수를 이용하여 여러작업을 동시에 진행한다.
함수가 바로 반환되므로 계속 중첩해서 I/O 요청을 할수가 있다.
(참고 : 동기 방식(블로킹) I/O 에서는 전송을 위해 할당된 내부 메모리 버퍼에
          데이터가 복사가 되면 함수가 반환된다. (데이터 전송이 완료될 때 반환되는 것이 아니다)


OVERAPPED 구조체

typedef struct _OVERLAPPED {
        ULONG_PTR Internal;                // WIndows 시스템 내부 변수 ( 0으로 초기화 )
        ULONG_PTR InternalHigh;          // WIndows 시스템 내부 변수 ( 0으로 초기화 )
        union{
              struct {
                       DWORD Offset;            // 파일포인터 위치 지정
                       DWORD OffsetHigh;      // 파일포인터 위치 지정
              };
              PVOID Pointer;
        };
        HANDLE hEvent;           // I/O 연산이 완료되었음을 확인하기 위한 이벤트 커널 오브젝트
}; OVERLAPPED, *LPOVERLAPPED;


GetOverlappedResult() 함수 :

I/O 연산이 완료된 이후에 전송된 데이터 크기 확인을 위해 호출한다.

BOOL GetOverlappedResult(        //  
          HANDLE hFile,                                          // 입력 및 출력결과를 확인할 오브젝트의 핸들을 지정
          LPOVERLAPPED lpOverlapped,                   // OVERLAPPED 구조체 변수의 주소값을 전달
          LPDWORD lpNumberOfBytesTransferred,      // 실제 전송이 완료된 바이트 크기를 얻기 위한 변수주소
          BOOL bWait                                               // I/O 연산이 끝나지 않은 상황에서의 처리 결정
);                                                                       // TRUE : I/O 연산이 끝날때까지 기다린다.
                                                                          // FALSE : I/O 연산이 끝나지 않아도 반환한다.


중첩 I/O 예제 : 파이프 통신 기반.

(참고 : http://dakuo.tistory.com/entry/파이프Pipe-IPC-통신-소스 에서 클라이언트는 그대로고
          서버만 중첩 I/O 기반으로 수정했습니다. 변경된 부분은 진하게 썻습니다)

중첩 I/O PipeServer 예제

(참고 :

overlappedInst.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); 에서 생성된 이벤트 오브젝트는

Signaled 상태로 생성되며, 사용자 리셋 모드의 이벤트 오브젝트이다.

OVERLAPPED 구조체 변수의 멤버로 등록된 이벤트 커널 오브젝트는 ReadFile, WriteFile 함수 호출시

자동으로 Non-Signaled 상태가 된다.(초기 상태와 자동 리셋모드가 의미가 없다)) 

중첩 I/O 에서는 WriteFile, ReadFile 함수가 NULL을 반환해도
(과도한 중첩된 I/O 연산으로에 의해 발생한 병목현상 일수도 있기 때문에 (NULL 반환)
블로킹 함수를 사용한 단일 I/O 연산에서는 하나의 I/O 연산만 수행하므로 병목현상이 발생하지 않는다)

GetLastError 함수 호출을 통해

과도한 I/O 요청에 의한 문제인지 (반환값이 ERROR_IO_PENDING) 아닌지를 검사해야 한다)



완료루틴(Completion Routine) 기반 확장 I/O


완료루틴(Completion Routine) : I/O 연산이 완료되었을 때 실행되는 루틴
(ex. I/O A 연산이 완료되면 Routine D 가 실행된다.
       I/O B 연산이 완료되면 Routine E 가 실행된다)


확장 I/O 제공 기능 = 중첩 I/O 기능 + α(알파)

확장 I/O 기반 함수 :

BOOL WriteFileEx(
        HANDLE hFile,                            // 데이터 전송의 대상 핸들 (FILE_FLAG_OVERLAPPED 속성)
        LPCVOID lpBuffer,                       // 전송할 데이터를 지니는 버퍼주소
        DWORD nNumberOfBytesToWrite,  // 전송할 데이터의 크기
        LPOVERLAPPED lpOverlapped,     // OVERLAPPED 구조체 변수의 포인터
        LPOVERLAPPED_COMPLETION_ROUTINE lpCOmpletionRoutine  // 연산 완료시 호출되는 완료루틴
);


LPOVERLAPPED_COMPLETION_ROUTINE 선언

typedef VOID (WINAPI *LPOVERLAPPED_COMPLETION_ROUTINE)(
          DWORD dwErrorCode,                               // 에러코드
          DWORD dwNumberOfBytesTransfered,        // 완료된 I/O 연산 데이터의 크기
          LPOVERLAPPED lpOverlapped                   // OVERLAPPED 구조체 변수의 포인터
);


BOOL ReadFileEx(
         HANDLE hFIle,                                // 데이터 수신의 대상 핸들 ( FILE_FLAG_OVERLAPPED 속성 )
         LPVOID lpBuffer,                              // 데이터 수신을 위한 버퍼주소
         DWORD nNumberOfBytesToRead,      // 읽어 들일 데이터의 최대 크기를 바이트 단위로 지정
         LPOVERLAPPED lpOverlapped,          // OVERLAPPED 구조체 변수 포인터
         LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine  // 연산 완료시 호출되는 완료루틴
);



알림가능한 상태(Alertable State) :
 
Windows 는 I/O 연산이 완료된 후 완료루틴 실행 타이밍을 프로그래머가 결정할수 있도록 하고 있다.

DWORD SleepEx(
          DWORD dwMilliseconds,     // 전달되는 시간만큼 블로킹에 들어간다.
          BOOL bAlertable                 // FALSE : Sleep 함수와 동일
);                                                // TURE : 이 함수를 호출한 쓰레드를 알림 가능항 상태로 변경

WaitForSingleObjectEx(HANDLE hHandle, dwMilliseconds, BOOL bAlertable)

WaitForMultipleObjcetsEx(DWORD nCount, COSET HANDLE *lpHandles, BOOL fWaitAll,
                                     DWORD dwMilliseconds, BOOL bAlertable)



파일 포인터의 위치 정보 :

비동기 I/O 에서는 함수를 호출 하자마자 반환하므로 파일 포인터의 위치가 옮겨지지 않아

커널 오브젝트의 존재하는 파일의 포인터 위치 정보는 의미가 없다.

따라서 OVERLAPPED 구조체의 멤버 (Offset, OffsetHigh)에 직접 데이터 입·출력 위치를 계산해서 대입한다.

예제보기



타이머 완료루틴 :

CreateWaitableTimer(
           LPSECURITY_ATTRIBUTES lpTimerAttributes,             // 보안속성
           BOOL bManualRest,                                                 // TRUE : 타이머를 수동 리셋 모드로 생성
                                                                                         // FALSE : 타이머를 자동 리셋 모드로 생성
           LPCTSTR lpTimerName                                            // 타이머 커널 오브젝트에 붙일 이름
);


BOOL SetWaitableTimer(
           HANDLE hTimer, // 알람을 설정할 타이머 오브젝트의 핸들 (정해진 시간이되면 커널 오브젝트는 Signaled)
           const LARGE_INTEGER *pDueTime,               // 알람이 울리는 시간 ( + : 절대시간,   - : 상대 시간) 
           LONG lPeriod,          // 주기적으로 알람을 울리게 할 때 사용 (주기가 되는 시간 전달,  0일 경우 사용안함)
           PTIMERAPCROUTINE pfnCompletionRoutine,  // 완료루틴을 지정 
           LPVOID lpArgToCompletionRoutine,                // 타이머 완료루틴의 첫 번째 전달인자가 그대로 전달된다
           BOOL fResume                                            // 전원관리. 기본적으로 FALSE 전달
);


타이머의 완료 루틴 :

VOID CALLBACK TimerAPCProc(
          LPVOID lpArgToCompletionRoutine,
          DWORD dwTimerLowValue,
          DWORD dwTimerHighValue
);

전달인자 dwTimerLowValue 와 dwTimerHighValue 를 통해서 타이머가 Signaled 상태가 된 시간 정보가 전달되며

첫번째 전달인자 lpArgToCompletionRoutine 은 SetWaitableTimer 함수의 다섯 번째 인자로 그대로 전달된다.



APC(Asynchronous Procedure Call) : 비동기 함수 호출 매커니즘

User-mode APC :

모든 쓰레드는 자신만의 APC Queue (쓰레드별로 독립)를 가지고 있다.


APC Queue 의 접근 :

DWORD QueueUserAPC(
           PAPCFUNC pfnAPC,  // 비동기로 호출될 함수 ( VOID CALLBACK APCProc(ULONG_PTR dwParam)
           HANDLE hThread,     // 비동기 함수 정보를 추가할 APC Queue (APC Queue 를 소유하는 쓰레드의 핸들)
           ULONG_PTR dwData // APC Queue 에 등록된 함수 호출시 전달할 인자
);

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

MMF(Memory Mapped File)  (0) 2010.03.12
라이브러리(Library)  (0) 2010.03.11
비동기 I/O 와 APC  (0) 2010.03.08
디렉터리 컨트롤  (0) 2010.03.08
파일 I/O  (2) 2010.03.08
가상 메모리(Virtual Memory)  (0) 2010.03.07
Posted by Dakuo

디렉터리의 생성과 소멸 :

BOOL CreateDirectory(
        LPCTSTR lpPathName,                                            // 생성하려는 디렉터리 이름
        LPSECURITY_ATTRIBUTES lpSecurityAttributes          // 보안속성
);

BOOL RemoveDirectory(
        LPCTSTR lpPathName          // 소멸하려는 디렉터리 이름
);

(참고 : 절대경로 지정시 마지막 디렉터리만 생성한다.
          즉, C:\\AAA\BBB 일때 (AAA 디렉터리는 존재하지 않는다) 컴퓨터는 절대로 AAA 디렉터리를 만들어 주지
          않는다. 따라서 BBB 디렉터리도 만들어지지 않는다. AAA 디렉터리가 존재해야 BBB 디렉터리가 생성된다.



현재 디렉터리, 시스템 디렉터리, Windows 디렉터리 :

현재 디렉터리(Current Directory)

: 처음에는 프로그램이 로드(Load)된 디렉터리(실행파일이 존재하는 디렉터리)로 설정되며, 이후 변경 가능하다

DWORD GetCurrentDirectory(
         DWORD nBufferLength,           // 버퍼의 길이를 지정 
         LPTSTR lpBuffer                    // 현재 디렉터리 정보를 저장할 버퍼의 주소값을 지정
);

BOOL SetCurrentDirectory(
         LPCTSTR lpPathName          // 변경하고자 하는 현재 디렉터리 정보 지정
);


시스템 디렉터리(System Directory) & Windows 디렉터리

System Directory : C:\\WINDOWS\system32      // 시스템에 중요한 파일들이 존재하는 위치
Windows Directory : C:\\WINDOWS                     // 초기화 및 실행파일들이 존재하는 위치

변경이 불가능하다.



디렉터리에서 파일 찾기 :

DWORD SearchPath(
         LPCTSTR lpPath,                // 대상 경로 지정 ( 지정된 경로에서 파일을 찾게된다 )
                                                  // NULL : 표준 검색 경로로 검색
         LPCTSTR lpFileName,         // 찾고자 하는 파일 이름
         LPCTSTR lpExtension,         // 확장자를 지정하는 파라미터
         DWORD nBufferLength,         // 완전경로명을 저장할 버퍼의 길이
         LPCTSTR lpBuffer,              // 완전경로명을 저장할 버퍼의 주소
         LPCTSTR *lpFilePart           // 완전경로명에서 파일이름을 가리키는 포인터값
);

예제보기

(참고 :

표준 검색 경로 :
1. 실행 중인 프로그램이 로드(Load)된 디렉터리(실행파일이 존재하는 디렉터리)
2. 현재 디렉터리
3. 시스템 디렉터리
4. Windows 디렉터리
5. 환경변수 PATH에 등록된 디렉터리



지정된 디렉터리 내에 존재하는 파일목록 :

1. HANDLE FindFirstFile(
           LPCTSTR lpFileName,                        // 파일이나 디렉터리 이름을 지정
           LPWIN32_FIND_DATA lpFindFileData     // 발견된 파일이나 디렉터리 정보를 담을
);                                                                // WIN32_FIND_DATA 구조체 변수주소

2. BOOL FindNextFile(
           HANDLE hFindFile,                            // FindFirstFile 함수 호출을 통해 얻은 핸들을 전달 
           LPWIN32_FIND_DATA lpFindFileData    // 발견된 파일이나 디렉터리 정보를 담을
);                                                               // WIN32_FIND_DATA 구조체 변수주소 

3. BOOL FindClose(
           HANDLE hFindFile                            // FIndFirstFile 함수를 통해 얻은 핸들 반환
);


FindFirstFile 함수 호출을 통해서 생성되는 커널 오브젝트가 담고 있는 정보는 검색된 첫번째 디렉터리(파일)이다.

이어서 FindNextFile 함수를 호출하면 다음에 검색되는 디렉터리에 대한 정보가 채워진다.
(또 검색될 디렉터리가 있다면 FindNextFile 함수를 또 호출한다)

커널 오브젝트가 생성되었기에 FindClose 함수로 리소스를 반환해야 한다.

예제보기

(참고 : WIN32_FIND_DATA :

typedef struct _WIN32_FIND_DATA {
           DWORD dwFileAttritubes;
           FILETIME ftCreationTime;
           FILETIME ftLastAccessTime;
           FILETIME ftLastWriteTime;
           DWORD nFileSizeHigh;
           DWORD nFileSizeLow;
           DWORD dwReserved0;
           DWORD dwReserved1;
           TCHAR cFileName[MAX_PATH];
           TCHAR cAlternateFileName[14];
} WIN32_FIND_DATA, *PWIN32_FIND+DATA;                )

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

라이브러리(Library)  (0) 2010.03.11
비동기 I/O 와 APC  (0) 2010.03.08
디렉터리 컨트롤  (0) 2010.03.08
파일 I/O  (2) 2010.03.08
가상 메모리(Virtual Memory)  (0) 2010.03.07
쓰레드(Thread) 동기화 예제 소스  (0) 2010.03.06
Posted by Dakuo
파일 열기 :

HANDLE CreateFile(
      LPCTSTR lpFileName,                // 개방(open)할 파일 이름
      DWORD dwDesiredAccess,          // 읽기/쓰기 모드를 지정한다.(or (l) 연산으로 결합)
                                                          GENERIC_READ : 읽기 모드
                                                          GENERIC_WRITE : 쓰기 모드
      DWORD dwShareMode,               // 파일 공유방식 
                                                          0 : 다른 프로세스에 절대 공유 불가. 중복 개방 불가
                                                          FILE_SHARE_READ : 다른 프로세스에서 동시 읽기 접근 가능
                                                          FILE_SHARE_WRITE : 다른 프로세스에서 동기 쓰기 접근 가능
      LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 보안 속성
      DWORD dwCreationDisposition,    // 파일의 생성되는 방법을 지정
                                                          CREATE_ALWAYS : 항상 새 파일을 생성
                                                          CREATE_NEW : 새 파일 생성, 같은 이름의 파일이 존재하면 생성 실패
                                                          OPEN_ALWAYS : 기존 파일 개방. 없으면 생성
                                                          OPEN_EXISTING : 기존 파일 개방, 존재하지 않으면 함수 호출 실패
                                                          TRUNCATE_EXISTING : 기존 파일의 내용 지우고 개방.
      DWORD dwFlagsAndAttributes,     // 파일의 특성 정보를 설정 (기본적 FILE_ATTRIBUTE_NORMAL 사용)
      HANDLE hTemplateFile               // 기존에 존재하는 파일과 동일한 특성을 가지는 새 파일을 만들때 사용
);


파일 쓰기(저장) :

BOOL WriteFile(
      HANDLE hFile,                                  // 데이터를 읽어 들일 파일을 지정(Mail Slot)
      LPCVOID lpBuffer,                             // 전송할 데이터가 저장되어 있는 버퍼 지정
      DWORD nNumberOfBytesToWrite,       // 전송할 데이터 크기를 지정한다  
      LPDWORD lpNumberOfBytesWritten,    // 함수 호출 완료 후 전송된 실제 데이터의 크기를 바이트 단위로
                                                                얻기 위한 변수의 주소 지정
      LPOVERLAPPED lpOverlapped           //
);


파일 읽기 :

BOOL ReadFile(
      HANDLE hFile,                                  // 해당 파일(Mail Slot)에 존재하는 데이터를 읽어들인다
      LPCVOID lpBuffer,                             // 읽어 들인 데이터를 저장할 버퍼 지정
      DWORD nNumberOfBytesToWrite,       // 읽어 들일 데이터의 최대 크기 지정  
      LPDWORD lpNumberOfBytesWritten,    // 함수 호출 완료 후 읽어 들인 실제 데이터의 크기를 바이트 단위로
                                                                얻기 위한 변수의 주소 지정
      LPOVERLAPPED lpOverlapped           //
);

파일입출력 예제



파일 포인터의 이동 :

DWORD SetFilePointer(
          HANDLE hFile,                                  // 파일 포인터 위치를 이동시킬 대상 파일의 핸들을 지정
          LONG lDistanceToMove,                    // 이동시킬 거리를 지정 ( 64 비트 환경에서 하위 4 바이트)
          PLONG lpDistanceToMoveHigh,          // 64 비트 기반의 대용량 파일에서만 의미 (상위 4 바이트 표현 )
                                                                 // 32비트 환경에서는 NULL
          DWORD dwMoveMethod                     // 파일 포인터 이동시 기준 위치 지정 : 앞 : FILE_BEGIN
);                                                                                                                     현재위치 : FILE_CURRENT
                                                                                                                       뒤 : FILE_END
예외처리 :

32비트 : if(dwPtr == INVALID_SET_FILE_POINTER)       64비트 : if((dwPtrLow == INVALID_SET_FILE_POINTER)
            {                                                                                && (GetLatError() != NO_ERROR)) 
                        예외처리                                                     {
            }                                                                                          예외처리
                                                                                         }
(참고 : 64비트에서 4G바이트 이상의 대용량파일을 다루기 때문에 파일의 위치가 0xFFFFFFFF 가 되는데 이는
          INVALID_SET_FILE_POINTER 값과 같으므로 GetLastError 함수호출을 통해 반환값을 확인해줘야한다)



파일의 시간 정보 얻어오기 :

BOOL GetFileTime(
         HANDLE hFile,                             // 시간 관련 정보를 얻을 대상 파일의 핸들
         LPFILETIME lpCreationTime,         // 파일이 생성된 시간을 얻기 위한 FILETIME 구조체 변수주소
         LPFILETIME lpLastAccessTime,    // 파일의 마지막 접근 시간을 얻기 위한 FILETIME 구조체 변수주소
         LPFILETIME lpLastWriteTime,        // 파일의 마지막 데이터 갱신(덮어 쓰기 포함) 시간을 얻기 위한
};                                                         // FILETIME 구조체 변수주소
(참고 : 세가지 시간 정보중 원하지 않는 시간 정보에 NULL 전달)


FILETIME 구조체 :                                 SYSTEMTIME 구조체 :

typedef struct _FILETIME {                     typedef struct _SYSTEMTIME {
         DWORD dwLowDateTime;                      WORD wYear;
         DWORD dwHighDateTime;                      WORD wMonth;
} FILETIME, *PFILETIME;                               WORD wDayOfWeek;
                                                                    WORD wDay;
                                                                    WORD wHour;
                                                                    WORD wMinute;
                                                                    WORD wSecond;
                                                                    WORD wMilliseconds;
                                                          } SYSTEMTIME, *PSYSTEMTIME;

FILEITME 구조체는 시간 정보를 UTC 기반으로 나타내는 8바이트 자료형(DWORD*2)이다.
(참고 : UTC 시간 : 1601년 1월 1일을 기준으로 100 나노초 단위 기준으로 지나간 시간을 계산
SYSTEMTIME 구조체도 UTC 시간 기반이다)


BOOL FileTimeToSystemTime(                     // FILETIME 포맷을 SYSTEMTIME 포맷으로 변경하는 함수.
           CONST FILETIME *lpFileTime,           // UTC 정보가 들어있는 FILETIME 구조체 변수주소
           LPSYSTEMTIME lpSystemTime         // SYSTEMTIME 구조체 변수주소
);


BOOL SystemTimeToTzSpecificLocalTime(                 // UTC 기반시간을 현재 시스템시간으로 변경하는 함수.
            LPTIME_ZONE_INFORMATION lpTimeZone,    // TIME_ZONE_INFORMATION 구조체 변수주소
                                                                               // NULL : 현재 시스템의 지역정보
            LPSYSTEMTIME lpUniversalTime,            // UTC 시간정보가 들어있는 SYSTEMTIME 구조체 변수주소
            LPSYSTEMTIME lpLocalTime                 // 지역시간을 넘겨받을 SYSTEMTIME 구조체 변수주소
);

파일시간 예제


(추가 : SetFileTIme 함수를 이용하면 함수의 시간 정보를 변경할수 있다.

BOOL SetFileTime(
HANDLE hFile,                                            // 시간 정보를 변경하고자 하는 파일의 핸들
CONST FILETIME *lpCreationTime,               // 파일의 생성시간 변경
CONST FILETIME *lpLastAccessTime,          // 파일의 마지막 접근시간 변경
CONST FILETIME *lpLastWriteTime               // 파일의 마지막 데이터 갱신 시간 변경
);



파일 사이즈 얻어오기 :

BOOL GetFileSizeEx(                    
        HANDLE hFile,                        // 파일 핸들을 지정한다.  
        PLARGE_INTEGER lpFileSize   // 파일 크기를 저장하기 위한 변수주소(LARGE_INTEGER = 8바이트 자료형)
);


파일의 특성(Attribute) 정보 얻어오기 :


DWORD GetFileAttributes(
        LPCTSTR lpFileName      // 특성정보를 얻고자 하는 파일의 핸들을 지정.
);  // 파일의 특성 정보를 DWORD 로 반환 (비트 단위로 의미 부여)

ex.    #define FILE_ATTRIBUTE_READONLY 0x00000001
         #define FILE_ATTRIBUTE_HIDDEN      0x00000002


BOOL SetFileAttributes(
       LPCTSTR lpFileName,         // 특성 정보를 변경하고픈 파일의 이름을 지정
       DWORD dwFileAttributes       // 변경할 특성 정보 전달
);


BOOL GetFileInformationByHandle(
     HANDLE hFile,                                                        // 정보를 얻고자 하는 파일의 핸들
     LPBY_HANDLE_FILE_INFORMATION lpFileInformation // BY_HANDLE_FILE_INFORMATION 구조체 변수주소
);

파일의 특성정보와 시간정보 파일 크기 정보를 비롯한 추가적인 파일 시스템 정보들도 얻을 수 있다.

typedef struct _BY_HANDLE_FILE_INFORMATION
{
         DWORD dwFileAttributes;
         FILETIME ftCreationTime;
         FILETIME ftLastAccessTime;
         FILETIME ftLastWriteTime;
         DWORD dwVolumSerialNumber;
         DWORD nFileSizeHigh;
         DWORD nFileSizeLow;
         DWORD nNumberOfLinks;
         DWORD nFileIndexHigh;
         DWORD nFileIndexLow;
} BY_HANDLE_FILE_INFORMATION, * BY_HANDLE_FILE_INFORMATION;

파일 정보 출력 예제



파일의 경로(PATH) 얻기 :

DWORD GetFullPathName(
           LPCTSTR lpFileName,           // 완전경로(Full Path)를 확인하고자 하는 파일 이름을 전달한다.
           DWORD nBufferLength,           // 완전경로를 저장할 버퍼에 저장 가능한 문자열 길이를 지정한다.
           LPTSTR lpBuffer,                   // 완전경로를 저장할 버퍼의 주소값을 지정한다.
           LPTSTR *lpFilePart                // 버퍼의 저장된 값중 파일의 이름값을 가리키는 포인터
);

파일경로 얻기 예제

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

비동기 I/O 와 APC  (0) 2010.03.08
디렉터리 컨트롤  (0) 2010.03.08
파일 I/O  (2) 2010.03.08
가상 메모리(Virtual Memory)  (0) 2010.03.07
쓰레드(Thread) 동기화 예제 소스  (0) 2010.03.06
쓰레드(Thread) 동기화 개념  (0) 2010.03.04
Posted by Dakuo

개념 :

(가정 : 16비트 시스템(0 ~ 64K -1 주소), 프로세스별로 64KB의 가상 메모리 할당, 메인 메모리 16KB)


MMU(Memory Management Unit) : 실제로 존재하는 메모리 크기를 가상 메모리 크기만큼 존재하는 것처럼
CPU가 느끼도록 컨트롤 한다.(ex. 1GB 램의 메모리를 4GB의 메모리가 존재하는 것처럼 CPU가 느끼도록 한다)
가상 메모리 구성에 필요한 시스템의 기본 기능(메모리 할당 및 변환)을 제공한다.
(CPU와 함께 하나로 패키징(Packaging) 되있다)

CPU                 - >                     MMU : 메인 메모리에서 사용되지 않는 메모리 블록 하나를 골라서 할당
       (1K부터 20바이트 할당요청)

20바이트를 할당 요청했음에도 메모리 블록은 4K 바이트 단위라서 4 K 를 할당받게 된다.
(참고 : 비록 4K - 20 바이트 만큼의 메모리가 낭비되지만 MMU가 간단한 연산만 수행해도 되서 속도가 향상된다.
          그리고 프로그램의 스페셜 로컬리티 특성에 의해 나머지 공간도 채워질 가능성이 높다)


페이지 테이블 :

메모리 블록의 크기는 시스템에 따라서 다양하며 실제 메인 메모리 블록을 페이지 프레임(Page Frame),

가상 메모리 블록을 페이지(Page)라 한다. (페이지 프레임 크기 = 페이지의 크기)


가상 메모리와 실제 메모리의 매핑(Mapping) 모습이다.

0K - 4K 페이지는 제일 위쪽 페이지 프레임에 매핑되있다.

CPU가 0K - 4K 사이에 존재하는 데이터를 요구할 경우 MMU는 매핑된 물리 메모리를 참조해서 데이터를 전송한다.

위와 같은 구조를 MMU는 내부적으로 페이지 테이블을 만들어 키와 값으로 구성하여 관리한다.

(ex. 페이지 테이블
 페이지 14  메인 메모리 0 - 4K
    1      1     1      0     0      0      0      0      0      0     0      0      0      0      0      1
<-----------------------> <--------------------------------------------------------------------------->
   페이지 프레임 결정(번호)                                        페이지 프레임 내의 위치 결정


스왑(Swap) :


메인 메모리에 여유 공간이 없는 상황에서 새로운 메모리를 할당하고자 할때

특정 메모리 블록(사용될 확률이 가장 낮은 블록)을 하드디스크에 저장하고 나서 그 영역을 새롭게 할당한다.

만약 저장한 영역에 대해 접근이 요청되면 다시 특정 블록을 하드디스크에 저장하고 요청된 영역을

메인 메모리에 로드한다.

(참고 : 램과 하드디스크 사이에 데이터 이동 단위는 페이지 프레임(페이지)의 크기와 같다)

프로세스 A 가 실행을 멈추고 프로세스 B를 실행한다.

램(RAM)에는 프로세스 A를 실행시키기 위한 데이터가 존재한다. 하지만 프로세스 B를 실행해야 하므로

프로세스 A의 실행을 위한 데이터를 프로세스 A의 스왑파일에 저장한다. 그리고 프로세스 B의 스왑파일을

램에 로드한다.

이러한 스왑으로 인해 두 프로세스가 각각 4GB의 메모리를 할당한다.

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

디렉터리 컨트롤  (0) 2010.03.08
파일 I/O  (2) 2010.03.08
가상 메모리(Virtual Memory)  (0) 2010.03.07
쓰레드(Thread) 동기화 예제 소스  (0) 2010.03.06
쓰레드(Thread) 동기화 개념  (0) 2010.03.04
쓰레드(Thread) 실행순서 동기화  (0) 2010.03.04
Posted by Dakuo

메모리 접근 동기화 :

1. 크리티컬 섹션(Critical Section) 기반의 동기화 - 유저 모드


2. 인터락 함수(Interlocked Family Of Function) 기반의 동기화 - 유저 모드


3. 뮤텍스(Mutex) 기반의 동기화 - 커널 모드


4. 세마포어(Semaphore) 기반의 동기화 - 커널 모드


5. 이름있는 뮤텍스(Named Mutex) 기반의 프로세스 동기화 - 커널 모드





실행 순서의 동기화 :

1. 이벤트 기반의 동기화


2. 이벤트 + 뮤텍스 동기화

Posted by Dakuo

쓰레드 동기화 : 쓰레드 간의 질서가 잘 지켜지고 있음을 의미


쓰레드 동기화의 방법 :

유저 모드 동기화 :
 
동기화가 진행되는 과정에서 커널 코드가 실행되지 않는다.

커널 모드로의 전환이 이뤄지지 않으므로 성능상에 이점이 있지만 기능상의 제한도 있다.


커널 모드 동기화 :
 
커널에서 제공하는 동기화 기능을 활용한다.(유저 모드에서 제공하지 못하는 기능을 제공받을 수 있다)

커널 모드로의 전환으로 인해 성능이 저하된다.



두가지 관점에서의 쓰레드 동기화 :

메모리 접근에 대한 동기화 :

쓰레드들의 임계 영역 동시접근을 막아 동기화한다.

(참고 :

임계 영역(Critical Section) : 배타적 접근(한 순간에 하나의 쓰레드만 접근)이 요구되는

공유 리소스(전역변수와 같은)에 접근하는 코드 블록)

(예 :

쓰레드 A, B 가 전역변수 total 에 값을 변경한다.

이 경우 동시에 total 에 접근할 경우 문제가 생길수 있다.

따라서 total 을 변경하는 코드(임계 영역)에 하나의 쓰레드만 접근할 수 있도록 동기화한다)


동기화 기법 :

1. 크리티컬 섹션(Critical Section) 기반의 동기화 - 유저 모드

2. 인터락 함수(Interlocked Family Of Function) 기반의 동기화 - 유저 모드

3. 뮤텍스(Mutex) 기반의 동기화 - 커널 모드

4. 세마포어(Semaphore) 기반의 동기화 - 커널 모드

5. 이름있는 뮤텍스(Named Mutex) 기반의 프로세스 동기화 - 커널 모드



실행 순서의 동기화 :

메모리에 접근하는 쓰레드의 실행순서를 동기화한다.

(예 :

A 쓰레드는 입력을 받는다.

B 쓰레드는 입력받은 것을 출력한다.

그렇다면 A -> B 실행순서가 지켜지도록 동기화해야 한다)


동기화 기법 :

1. 이벤트(Event) 기반의 동기화 - 커널 모드

Posted by Dakuo

이벤트(Event) 기반 동기화 :

개념 :

생산자는 문자열을 생성한다(입력받는다), 소비자는 문자열을 소비한다(출력한다)

생산자가 문자열을 생성하고 나면, 이 상태를 감지한 소비자가 문자열을 가져가 소비한다.


이벤트 커널 오브젝트의 특성 :

1. 이벤트 커널 오브젝트는 파라미터에 의해 Signaled or Non-Signaled 상태로 생성된다.

2. 프로그래머의 요청에 의해 Signaled 상태가 된다.

3. 자동 리셋 모드 일시 WaitForSingleObject 함수 호출로 Signaled -> Non-Signaled 상태로 변경된다.
    수동 리셋 모드 일시 추가로 ResetEvent 함수를 사용한다.


사용법 :

HANDLE CreateEvent(
          LPSECURITY_ATTRIBUTES lpEventAttributes, // 보안속성
          BOOL bManualReset,  // TRUE : 수동 리셋 모드(Manual-Reset)
                                           // FALSE : 자동 리셋 모드(Auto-Reset)
          BOOL bInitialState,       // TRUE : Signaled 상태의 이벤트 커널 오브젝트 생성
                                           // FALSE : Non-Signaled 상태의 이벤트 커널 오브젝트 생성
          LPCTSTR lpName       // 이벤트 커널 오브젝트의 이름(NULL : 이름없는 이벤트 커널 오브젝트)
);
 
BOOL ResetEvent(
          HANDLE hEvent  // 이벤트 커널 오브젝트의 핸들 ( Non-Signaled 상태가 된다)
);

BOOL SetEvent(
          HANDLE hEvent  // 이벤트 커널 오브젝트의 핸들 (Signaled 상태가 된다)
);

CloseHandle() 함수로 핸들을 반환하여 끝낸다.



이벤트(Event) + 뮤텍스(Mutex) :

개념 :

생산자 하나에 소비자가 둘이다.

생산자가 생산하고 나면 소비자 둘은 동기화를 통해 소비해야 한다.


사용법 :

1. 생산자와 소비자간의 이벤트 기반 동기화를 한다. (실행순서 동기화)

2. 소비자간의 임계 영역에 뮤텍스(임계 영역 동기화 기법)로 동기화 한다. (메모리 접근 동기화)

Posted by Dakuo
1. 크리티컬 섹션 기반의 동기화(유저 모드) :

개념 :

열쇠(Critical Section)를 생성한다.

쓰레드가 임계 영역에 접근하기 위해서는 열쇠를 얻어야 한다.

열쇠를 얻어 임계 영역에 접근한 쓰레드는 일을 마친후 다음 쓰레드를 위해 열쇠를 반환한다.


사용법 :

CRITICAL_SECTION gCriticalSection;              // 열쇠를 생성한다
                   // 크리티컬 섹션 오브젝트 선언후에는 반드시 초기화 해야한다.
void InitializeCriticalSection(                           // 크리티컬 섹션 오브젝트 초기화 함수   
        LPCRITICAL_SECTION lpCriticalSection  // 초기화 하려는 크리티컬 섹션 오브젝트의 주소값
);

void EnterCriticalSection(
        LPCRITICAL_SECTION lpCriticalSection // 획득하려는 크리티컬 섹션 오브젝트의 주소값
);

열쇠(크리티컬 섹션 오브젝트)를 획득한다.

다른 쓰레드가 열쇠를 소유한 상태이면 반환할 때까지 블로킹된다.

열쇠가 반환되면 블로킹 상태에서 빠져나와 열쇠를 획득하여 임계 영역에 접근한다.
(이때 쓰레드가 크리티컬 섹션 오브젝트를 획득했다고 한다)

void LeaveCriticalSection(
        LPCRITICAL_SECTION lpCriticalSection // 반환하려는 크리티컬 섹션 오브젝트의 주소값
);

임계 영역을 빠져 나와 열쇠를 반환한다.

호출이 완료되면 크리티컬 섹션 오브젝트를 반환했다고 표현한다

만약 EnterCriticalSection 을 호출하고 블로킹 상태에 놓인 쓰레드가 있다면

이 함수 호출로 인해 블로킹 상태에서 빠져 나와 열쇠를 획득해 임계 영역에 접근한다.

void DeleteCriticalSection(
        LPCRITICAL_SECTION lpCriticalSection // 리소스 해제할 크리티컬 섹션 오브젝트의 주소값
);

크리티컬 섹션 오브젝트 초기화 함수가 호출되는 과정에서 할당된 리소스들을 반환한다.

(참고 : 임계 영역 접근 동기화를 위하여 열쇠(CriticalSection)을 사용한다.

하지만 여러 쓰레드들이 이 열쇠를 획득하려 하면(EnterCriticalSection 함수 호출) 어떻게 될까?

이 함수에 대해서도 동기화를 해야 하지 않을까?

답은 '필요없다' 이다.

이 함수는 컨텍스트 스위칭을 막으므로 동기화 해줄 필요가 없다)



2. 인터락 함수(Interlocked Family Of Function) 기반의 동기화(유저 모드) :

개념 :

전역변수 하나의 접근방식을 동기화 할때 사용한다.

내부적으로 한 순간에 하나의 쓰레드만 실행되도록 동기화 되어 있다.(원자적 접근을 보장한다)

(원자적 접근(Atomic Access) : 한순간에 하나의 쓰레드만 접근)


사용법 :

LONG InterlockedIncrement(
        LONG volatile *Addend       // 값을 하나 증가시킬 32비트 변수의 주소값
);

LONG InterlockedDecrement(
        LONG volatile *Addend       // 값을 하나 감소시킬 32비트 변수의 주소값
);

(참고 : 이외에도 MS 에서는 다양한 인터락 함수들을 제공한다. MSDN 을 참조하자

volatile 의미 :

1) 최적화를 수행하지 마라.   // 해당 소스 그대로 컴파일 된다.
(a=1, a=2, a=3  최적화 컴파일->  a=3,      a=1, a=2, a=3  컴파일->  a=1, a=2, a=3)

2) 메모리에 직접 연산하라.   // 해당 데이터는 캐시되지 않고 데이터 전송이 바로 이뤄진다)



3. 뮤텍스(Mutex) 기반의 동기화(커널 모드) :

개념 :

크리티컬 섹션 오브젝트와 똑같다.(대상이 CriticalSection -> 뮤텍스)


뮤텍스 커널 오브젝트의 상태 :

Non-Signaled : 뮤텍스가 쓰레드에 의해 획득되어 있을 때

Signaled : 뮤텍스가 쓰레드에 의해 획득이 가능할 때


사용법 :

HANDLE CreateMutex(
         LPSECURITY_ATTRIBUTES lpMutexAttributes, // 뮤텍스 보안속성
         BOOL bInitialOwner,  // TURE : 뮤텍스를 생성하는 쓰레드가 먼저 기회를 얻는다.
                                        // FALSE : 뮤텍스를 먼저 획득하는 쓰레드가 기회를 얻는다.
         LPCTSTR lpName     // 뮤텍스에 이름을 붙여주기 위해 사용한다.(NULL 이 아닐경우 NamedMutex)
);

뮤텍스는 획득 가능할 때 Signaled 상태이므로

WaitForSingleObject 함수로 뮤텍스를 획득한다.

함수를 호출하고 나면 Signaled -> Non-Signaled 상태로 자동 변경된다.

BOOL ReleaseMutex(
         HANDLE hMutex       // 반환할 뮤텍스의 핸들 (Non-Signlaed -> Signaled)
);


사용을 완전히 마친 뮤텍스는

CloseHandle() 함수를 사용하여 핸들을 반환한다.



4. 세마포어(Semaphore) 기반의 동기화(커널 모드) :

개념 :

세마포어는 뮤텍스 + 카운트(Count) 이다.

즉, 쓰레드의 메모리 동시접근을 지정한 갯수만큼 허용한다는 것이다.
(뮤텍스는 하나의 쓰레드만 가능하다)

세마포어는 lInitialCount 에 의해 초기 카운트가 결정된다.

임계 영역에 접근한 쓰레드가 생기면 카운트 -1 이 되고
임계 영역에서 빠져 나온 쓰레드가 생기면 카운트 +1 이 된다.

카운트 0 : Non-Signaled (획득 불가능한 상태)
카운트 1 이상 : Signaled (획득 가능한 상태)


사용법 :

HANDLE CreateSemaphore(
         LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,  // 보안속성
         LONG lInitialCount,          // 임계 영역에 접근 가능한 쓰레드의 개수
         LONG lMaximumCount,   // 세마포어가 지닐수 있는 값의 최대 크기(1일경우 뮤텍스와 동일한 기능)
         LPCTSTR lpName           // 세마포어에 이름(NULL 아닐 경우 이름있는 세마포어(Named Semaphore)
);

WaitForSingleObject 함수로 임계영역에 접근한다(세마포어 카운트 -1)

BOOL ReleaseSemaphore(
         HANDLE hSemaphore,          // 반환하고자 하는 세마포어의 핸들
         LONG lReleaseCount,           // 증가시킬 카운트 값의 크기(세마포어 생성시 결정한
                                                                                            최대 카운트값을 넘길 경우 FALSE 반환)
         LPLONG lpPreviousCount      // 변경되기 전 세마포어 카운트 값을 저장할 변수(필요없다면 NULL)
);


사용을 완전히 마친 세마포어는

CloseHandle() 함수를 사용하여 핸들을 반환한다.



5. 이름있는 뮤텍스(or 세마포어) 기반의 프로세스 동기화(커널 모드) :

개념 :

뮤텍스와 세마포어를 생성할 때 이름을 붙여 줄수 있다.

이름이 붙여지면 서로 다른 프로세스에 존재하는 쓰레드들끼리의 동기화가 가능하다.

사용법 :

HANDLE OpenMutex(
         DWORD dwDesiredAccess,    // 이름 있는 뮤텍스로의 접근권한 (MUTEX_ALL_ACCESS 전달)
         BOOL bInheritHandle,            // 핸들의 상속 유무 결정 
         LPCTSTR lpName                // 얻고자 하는 핸들 정보의 커널 오브젝트 이름(핸들 테이블에 등록된다)
);


(추가 : 뮤텍스의 소유와 WAIT_ABANDONED

두 개의 쓰레드 A, B 와 뮤텍스 C가 존재한다.

쓰레드 A가 뮤텍스 C를 획득했다.

쓰레드 B는 쓰레드 A가 뮤텍스 C를 반환하기를 기다리고 있다.

이때 쓰레드 A가 미처 반환하지 못하고 종료되어 버렸다.

그러면 Windows 가 쓰레드의 상태와 뮤텍스의 상태를 예의 주시하다가 문제가 발생했음을 인식하고

쓰레드 A 대신에 뮤텍스 C를 반환하여 쓰레드 B 가 획득할수 있게 한다.

이때 쓰레드 B는 WAIT_ABANDONED 을 반환받는다.

참고로 뮤텍스는 뮤텍스를 획득한 쓰레드가 직접 반환하는 것이 원칙이지만 세마포어와 그 이외의 동기화 오브젝트는 획득한 쓰레드가 직접 반환하지 않더라도(누가 대신 반환해도) 문제가 되지 않는다)

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

쓰레드(Thread) 동기화 개념  (0) 2010.03.04
쓰레드(Thread) 실행순서 동기화  (0) 2010.03.04
쓰레드(Thread) 메모리 접근 동기화  (0) 2010.03.04
쓰레드(Thread)의 생성과 소멸  (0) 2010.02.28
쓰레드(Thread)  (2) 2010.02.28
환경변수  (0) 2010.02.25
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

쓰레드(Thread) : 하나의 프로그램 내에서 여러 개의 실행 흐름을 두기 위한 모델


1. 독립적인 스택(Stack)을 가진다.

2. 코드 영역을 공유한다.

(위와 같이 쓰레드 main 함수는 코드 영역에 존재하는 모든 함수를 호출할 수 있다)

3. 데이터 영역과 힙을 공유한다.(전역변수와 동적 할당된 메모리 공간 공유 가능)

4. 프로세스 핸들 테이블 공유

프로세스 핸들 테이블에 대한 핸들 정보는

프로세스 내의 쓰레드 들에게 공유되어 각 쓰레드가 그 핸들에 대해 접근이 가능하다
(참고 : 같은 프로세스내의 쓰레드들은 스택 이외의 모든 것을 공유한다)



프로세스와 쓰레드

Windows 에서 프로세스는 단순히 쓰레드를 담는 상자다.

실제 프로그램 흐름을 형성하는 것은 쓰레드이다.


따라서 스케줄러(Windows)가 볼때 2개의 프로세스가 아닌
 
실행 흐름을 가지고 있는 쓰레드 단위로 판단을 한다. 5개의 쓰레드를 인식한 후 스케줄링 한다.

(참고 : 컨텍스트 스위칭 시간

프로세스 A 내의 쓰레드 끼리의 컨텍스트 스위칭은 공유 영역이 많아 시간이 적게 걸린다.

하지만 프로세스 A와 프로세스 B 내의 쓰레드 끼리의 컨텍스트 스위칭은 공유 부분이 없어(프로세스끼리는 독립)

시간이 더 오래 걸린다)



커널(Kernel) 영역과 유저(User) 영역 :


유저 영역 : 사용자가 구현한 프로그램 동작시 사용하게 되는 메모리 영역

커널 영역 : 운영체제 동작시 사용하게 되는 메모리 영역


커널 레벨 쓰레드 :

쓰레드를 생성 및 스케줄링하는 주체가 커널


장점 : 안전성과 다양한 기능

단점 : 유저 모드와 커널 모드로의 전환으로 인해 성능이 저하된다.

(참고 : 사용자가 구현한 프로그램은 기본적으로 유저모드에서 동작하다가 Windows 커널이 실행되어야 하는 경우
커널 모드로의 전환이 일어나고 일을 마치면 다시 유저 모드로 전환된다)


유저 레벨 쓰레드 :

커널에 의존적이지 않은 형태로 쓰레드의 기능을 제공하는 라이브러리를 활용

(참고 : 스케줄러는 쓰레드를 지원하지 않아 쓰레드의 존재를 모른다. 따라서 프로세스 단에서 스케줄링을 한다
따라서 쓰레드 끼리의 스케줄링은 유저가 구현해야 한다) 

장점 : 유저모드에서 커널모드로의 전환이 필요없다. 성능이 좋다

단점 : 프로그래밍 하기 어렵고 커널 레벨 쓰레드에 비해 결과 예측이 어렵다.

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

쓰레드(Thread) 메모리 접근 동기화  (0) 2010.03.04
쓰레드(Thread)의 생성과 소멸  (0) 2010.02.28
쓰레드(Thread)  (2) 2010.02.28
환경변수  (0) 2010.02.25
스케줄링 알고리즘과 우선순위  (0) 2010.02.25
파이프(Pipe) IPC 통신 소스  (0) 2010.02.24
Posted by Dakuo
환경변수 :

컴퓨터에서 동작하는 방식에 영향을 미치는 동적인 값.

프로세스별로 별도의 메모리 공간에 저장하고 관리하는 데이터.
(key = value 의 형태)


윈도우즈의 환경변수

1. 기본 제공 시스템 변수
2. HKEY_LOCAL_MACHINE 하이브에 있는 시스템 변수
3. HKEY_CURRENT_USER 하이브에 있는 로컬 변수
4. 모든 환경 변수 및 Autoexec.bat 파일에서 설정된 경로
5. 모든 환경 변수 및 로그온 스크립트에서 설정된 경로
6. 스크립트 또는 배치 파일과 상호 작용하는 변수



함수를 통한 환경변수 :

환경변수 등록 :

BOOL SetEnvironmentVariable(
         LPCTSTR lpName,           // key 에 해당하는 값
         LPCTSTR lpValue            // value 에 해당하는 값
);

환경변수 참조 :

DWORD GetEnvironmentVariable(
        LPCTSTR lpName,           // key 를 전달, key 에 해당하는 value 를 얻게 된다
        LPCTSTR lpBuffer,           // value 값 저장하기 위한 메모리의 주소 지정
        DWORD nSize                  // lpBuffer 가 가리키는 메모리의 크기를 지정
); 

성공시 lpBuffer 에 저장된 문자열의 길이를 리턴



내컴퓨터 속성을 통한 환경변수 :

내컴퓨터 -> 우클릭 -> 속성




사용자 변수와 시스템 변수를 생성, 관리할 수 있다.

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

쓰레드(Thread)의 생성과 소멸  (0) 2010.02.28
쓰레드(Thread)  (2) 2010.02.28
환경변수  (0) 2010.02.25
스케줄링 알고리즘과 우선순위  (0) 2010.02.25
파이프(Pipe) IPC 통신 소스  (0) 2010.02.24
메일슬롯(MailSlot) IPC 통신 소스  (0) 2010.02.24
Posted by Dakuo

우선순위(Priority) 스케줄링(Scheduling) 알고리즘 :

각각의 프로세스마다 우선순위를 부여해서 우선순위가 높은 프로세스를 먼저 실행한다.

우선순위가 다른 두 프로세스를 동시 실행할 때,

우선순위가 높은 프로세스가 작업을 마치지 않는다면(블로킹 상태가 되거나 I/O 작업을 하지 않는다면)
우선순위가 낮은 프로세스는 절대로 실행되지 않는다. (기아 상태 : Starvation)



라운드 로빈(Round-Robin) 스케줄링 알고리즘 :

우선순위가 동일한 프로세스들의 형평성 유지를 위해
정해진 시간 간격(타임 슬라이스(Time Slice), 퀀텀(Quantum))만큼만 실행하고 CPU 할당을 넘긴다.

타임 슬라이스 ↑ -> 반응속도 ↓
타임 슬라이스 ↓ -> 성능 ↓ (잦은 컨텍스트 스위칭 발생으로 인하여 시스템이 무리가 가서 성능이 저하된다)



Windows의 스케줄링 알고리즘 :




스케줄링이 진행되는 시점 :

1. 매 타임 슬라이스마다 스케줄러 동작.
2. 프로세스가 생성 및 소멸될 때마다 스케줄러 동작
3. 현재 실행 중인 프로세스가 블로킹 상태에 놓일 때마다 스케줄러 동작



Windows 프로세스 우선순위 :

 Priority  의미
 IDLE_PRIORITY_CLASS  기존 우선순위 4
 NORMAL_PRIORITY_CLASS  기존 우선순위 9
 HIGH_PRIORITY_CLASS  기존 우선순위 13
 REALTIME_PRIORITY_CLASS  기존 우선순위 24
 ABOVE_NORMAL_PRIORITY_CLASS  NORMAL_PRIORITY_CLASS 보다 높고
 HIGH_PRIORITY_CLASS 보다 낮다.( 9< x < 13 )
 BELOW_NORMAL_PRIORITY_CLASS  IDLE_PRIORITY_CLASS 보다 높고
 NORMAL_PRIORITY_CLASS 보다 낮다. ( 4 < x < 9 )

프로세스 우선순위 변경 :

BOOL SetPriorityClass(
        HANDLE hProcess,              // 우선순위를 변경할 프로세스의 핸들
        DWORD dwPriorityClass        // 새롭게 적용할 우선순위 상수값
);


Priority Inversion :

프로세스의 우선순위가 뒤바뀌는 상황

(예 :

프로세스 A > 프로세스 B > 프로세스 C (우선순위)

프로세스 A가 실행이 되다가 프로세스 C에게 데이터를 받을 일이 생겼다.

프로세스 C가 실행이 되게 하기 위해 자신이 Blocked 상태가 되었다.

하지만 프로세스 C보다 우선순위가 높은 프로세스 B가 실행이 되어 프로세스 C가 실행이 되지 않으므로

프로세스 A 또한 실행이 될수 없게 된다.
(즉, 프로세스 A 보다 우선순위가 낮은 프로세스 B가 우선순위가 가장 높은것처럼 되었다))

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

쓰레드(Thread)  (2) 2010.02.28
환경변수  (0) 2010.02.25
스케줄링 알고리즘과 우선순위  (0) 2010.02.25
파이프(Pipe) IPC 통신 소스  (0) 2010.02.24
메일슬롯(MailSlot) IPC 통신 소스  (0) 2010.02.24
커널 오브젝트(Kernel Object) - 2  (0) 2010.02.23
Posted by Dakuo
/*
            이름없는 파이프(Anonymous Pipe)
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
INT_PTR _tmain(INT_PTR argc, TCHAR *argv[])
{
            HANDLE hReadPipe, hWritePipe;
            
            TCHAR sendString[] = _T("Anonymous Pipe");
            TCHAR recvString[100] = {0};
            ULONG_PTR bytesWritten;
            ULONG_PTR bytesRead;
            
            CreatePipe(&hReadPipe, &hWritePipe, NULL, 0);    // Pipe 생성
         
            WriteFile(hWritePipe, sendString, _tcslen(sendString)*sizeof(TCHAR), &bytesWritten, NULL);
            _tprintf(_T("String Send : %s \n"), sendString);
            ReadFile(hReadPipe, recvString, bytesWritten, &bytesRead, NULL);
            recvString[bytesRead/sizeof(TCHAR)] = 0;
            _tprintf(_T("String Recv : %s \n"), recvString);
 
            CloseHandle(hReadPipe);
            CloseHandle(hWritePipe);
            return 0;
}
/*
            이름있는 파이프(Named Pipe) Server
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#define BUF_SIZE 1024
INT_PTR CommToClient(HANDLE hPipe);
INT_PTR _tmain(INT_PTR argc, TCHAR *argv[])
{
          LPTSTR pipeName = _T("\\\\.\\pipe\\pipe_server");
          HANDLE hPipe;
          
          while(1)
          {
                   hPipe = CreateNamedPipe(pipeName, PIPE_ACCESS_DUPLEX,
                                                         PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
                                                         PIPE_UNLIMITED_INSTANCES, BUF_SIZE, BUF_SIZE, 10000, NULL);
                  
                   if(hPipe==INVALID_HANDLE_VALUE)
                   {
                              _tprintf(_T("CreatePipe Faild"));
                              return -1;
                   }
       
                   BOOL isSuccess = 0;
                   isSuccess = ConnectNamedPipe(hPipe, NULL)
                                      ? TRUE : (GetLastError()==ERROR_PIPE_CONNECTED);
                   if(isSuccess)
                   {
                              CommToClient(hPipe);
                    }
 
                   else
                   {
                              CloseHandle(hPipe);
                    }
          }
          return 1;
}
INT_PTR CommToClient(HANDLE hPipe)
{
          TCHAR readDataBuf[BUF_SIZE+1];
          ULONG_PTR bytesWritten = 0;
          ULONG_PTR bytesRead = 0;
          
          if(!WriteFile(hPipe, _T("Hi Client"), bytesRead, &bytesWritten, NULL))
          {
                   _tprintf(_T("Pipe Write Message Error!\n"));
                   return -1;
          }
          while(1)
          {
                    if(!ReadFile(hPipe, readDataBuf, BUF_SIZE*sizeof(TCHAR), &bytesRead, NULL))
                    {
                            _tprintf(_T("Unable to Read!\n"));
                            CloseHandle(hPipe);
                            return -1;
                    }
                    if(!_tcsncmp(readDataBuf, _T("exit"), 4))          // 받은 문자열이 "exit" 일때 종료
                    {
                            _tprintf(_T("Good Bye\n"));
                            break;
                    }
                 
                    readDataBuf[bytesRead/sizeof(TCHAR)]=0;                   
                    _tprintf(_T("recv data : %s \n"), readDataBuf);
           }          
           FlushFileBuffers(hPipe);
           DisconnectNamedPipe(hPipe);
           CloseHandle(hPipe);
           return 1;
}
/*
           이름있는 파이프(Named Pipe) Client
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#define BUF_SIZE 1024
INT_PTR _tmain(INT_PTR argc, TCHAR *argv[])
{
            HANDLE hPipe;
            TCHAR readDataBuf[BUF_SIZE +1];
            TCHAR dataBuf[BUF_SIZE];
            LPTSTR pipeName = _T("\\\\.\\pipe\\pipe_server");
            ULONG_PTR bytesWritten = 0;
            ULONG_PTR bytesRead = 0;
            while(1)
            {
                     hPipe = CreateFile(pipeName, GENERIC_READ | GENERIC_WRITE,
                                                0, NULL, OPEN_EXISTING, 0 , NULL);
                     if(hPipe != INVALID_HANDLE_VALUE)
                     {
                              break;
                     }
             
                     if(GetLastError() != ERROR_PIPE_BUSY)
                     {
                              _tprintf(_T("Could not open Pipe\n"));
                              return 0;
                      }
                     
                      if(!WaitNamedPipe(pipeName, 10000))
                      {
                               _tprintf(_T("Could not open Pipe\n"));
                               return 0;
                      } 
            }
            if(!ReadFile(hPipe, readDataBuf, BUF_SIZE*sizeof(TCHAR), &bytesRead, NULL))
            {
                      _tprintf(_T("Read Message Faild"));
                      return 0;
             }
             _tprintf(_T("recv data : %s\n"), readDataBuf); 
             while(1)
             {
                      _tprintf(_T("My Cmd>>"));
                      _fgetts(dataBuf, sizeof(dataBuf)/sizeof(TCHAR), stdin);
                   
                      if(!WriteFile(hPipe, dataBuf, _tcslen(dataBuf)*sizeof(TCHAR), &bytesWritten, NULL))
                      {
                               _tprintf(_T("Unable to Write!\n"));
                               CloseHandle(hPipe);
                      }
        
                      if(!_tcscmp(dataBuf, _T("exit")))
                      {
                               _tprintf(_T("Good Bye!"));
                               break;
                      }
              }
              CloseHandle(hPipe);
              return 0;
}
Posted by Dakuo
/*
            MailRecevier
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#define SLOT_NAME _T("\\\\.\\mailslot\\mailbox")
INT_PTR _tmain(INT_PTR argc, TCHAR *argv[])
{
         HANDLE hMailSlot;
         TCHAR messageBox[50];
         ULONG_PTR bytesRead;            //DWORD
         hMailSlot = CreateMailslot(SLOT_NAME, 0, MAILSLOT_WAIT_FOREVER, NULL);    // mailslot 생성
         if(hMailSlot==INVALID_HANDLE_VALUE)           
         {
                 _tprintf(_T("Unable to Create MailSlot!\n"));
                 return 1;
          }
          _tprintf(_T("******** Message ********\n"));
          
          while(1)
          {
                  if(!ReadFile(hMailSlot, messageBox, sizeof(TCHAR)*50, &bytesRead, NULL))  // 데이터 수신
                  {
                            _tprintf(_T("Unable to Read!\n"));
                            CloseHandle(hMailSlot);
                            return 1;
                   }
    
                   if(!_tcsncmp(messageBox, _T("exit"), 4))          // 받은 문자열이 "exit" 일때 종료
                   {
                            _tprintf(_T("Good Bye\n"));
                            break;
                   }
                   
                   messageBox[bytesRead/sizeof(TCHAR)]=0;       // NULL 문자 삽입
                   _tprintf(_T("%s\n"), messageBox);
          }
   
          CloseHandle(hMailSlot);
          return 0;
}
/*
             MailSender
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#define SLOT_NAME _T("\\\\.\\mailslot\\mailbox")
INT_PTR _tmain(INT_PTR argc, TCHAR *argv[])
{
          HANDLE hMailSlot;
          TCHAR message[50];
          ULONG_PTR bytesWritten;
          
          hMailSlot = CreateFile(SLOT_NAME, GENERIC_WRITE, FILE_SHARE_READ, NULL,
                                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
          if(hMailSlot==INVALID_HANDLE_VALUE)
          {
                  _tprintf(_T("Unable to Create MailSlot!\n"));
                  return 1;
           }

           while(1)
           {
                   _tprintf(_T("My Cmd>>"));         // _fputts(_T("My Cmd>>"), stdout);
                   _tscanf(_T("%s"), message);     // _fgetts(message, sizeof(message)/sizeof(TCHAR), stdin);
                   
                   if(!WriteFile(hMailSlot, message, _tcslen(message)*sizeof(TCHAR), &bytesWritten, NULL))
                   {
                           _tprintf(_T("Unable to Write!\n"));
                           CloseHandle(hMailSlot);
                    }
        
                   if(!_tcscmp(message, _T("exit")))
                   {
                           _tprintf(_T("Good Bye!"));
                           break;
                   }
            }
            CloseHandle(hMailSlot);
            return 0;
}
Posted by Dakuo

커널 오브젝트의 상태(State) :

리소스에 특정 상황이 발생함에 따라 Signaled 상태(신호를 받은 상태)와
Non-Signaled 상태(신호를 받지 않은 상태)로 나뉜다.

(예 : 프로세스 커널 오브젝트의 상태


종료된 프로세스는 다시 살릴수 없다. 따라서 프로세스 커널 오브젝트의 상태는 Signaled -> Non-Signaled 로
변경될 수 없다)


커널 오브젝트의 상태를 확인하는 함수 :

DWORD WaitForSingleObject(
           HANDLE hHandle,              // 상태 확인을 원하는 커널 오브젝트의 핸들
           DWORD dwMilliseconds      // hHandle가 가리키는 커널 오브젝트가 Signaled 상태가 될 때까지
                                                      기다릴수 있는 최대 시간(INFINITE 전달 : Signaled 상태가 될 때까지 기다림
);

 함수의 리턴값  의미
 WAIT_OBJECT_0  커널 오브젝트가 Signaled 상태가 되었을때 반환값
 WAIT_TIMEOUT  dwMilliseconds 인자를 통해서 설정된 시간이
 다 지난 경우 반환값
 WAIT_ABANDONED  소유 관계와 관련하여 함수가 정상적이지 못한
 오류 발생에 의한 반환값


커널 오브젝트가 둘 이상인 경우 : (핸들이 배열로 묶여있다면) 

DWORD WaitForMultipleObjects(
           DWORD nCount,                      // 배열에 저장되어 있는 핸들 개수
           const HANDLE *lpHandles,      // 핸들을 저장하고 있는 배열의 주소 정보
           BOOL bWaitAll,                       // 관찰 대상이 모두 Signaled 상태가 되기를 기다리는지(TRUE 전달)
                                                            하나라도 Signaled 상태가 되면 반환할 것인지(FALSE 전달) 결정
           DWORD dwMilliseconds           // 타임아웃(Time Out)을 설정하는 인자
);


종료 코드(Exit Code) :

프로세스가 종료되면서 전달하는 값으로 프로세스의 커널 오브젝트에 저장된다
(예 : exit(1);, return 0;)

부모 프로세스는 GetExitCodeProcess 함수 호출을 통하여 자식 프로세스의 종료 코드를 획득할 수 있다.

BOOL GetExitCodeProcess(
          HANDLE hProcess,                // 커널 오브젝트 핸들
          LPDWORD lpdwExitCode        // 종료 코드를 저장할 변수
                                                         (프로세스가 실행중이면 STILL_ACTIVE 반환)
);



프로세스의 커널 오브젝트 핸들 테이블 :

핸들 테이블 : 핸들 정보를 저장하고 있는 테이블로서 프로세스별로 독립적이다.



핸들의 상속 : 자식 프로세스는 부모 프로세스의 핸들 테이블에 등록되어 있는 핸들 정보를 상속받을 수 있다.


상속 여부가  Y인 핸들만 상속이 가능하다.
또 상속 여부에 대한 정보도 변경 없이 그대로 상속된다.(자식 프로세스가 또 그 자식 프로세스에게 상속 가능하다)


핸들의 상속을 위한 전달 인자 :

BOOL CreataProcess 의 5번째 파라미터 BOOL bInheritHandle 은
부모 프로세스가 소유하고 있는 핸들 테이블 정보(상속 여부가 Y인 핸들만)의 상속 여부를 결정한다.
(TRUE : 상속, FALSE : 상속 안함)


핸들의 상속과 커널 오브젝트의 Usage Count :

프로세스의 커널 오브젝트의 핸들 테이블에 해당 핸들에 대한 정보가 갱신(추가)되었을 때
프로세스가 핸들을 얻게 되었다고 한다.


상속이 되기 위한 핸들의 조건 :

핸들의 상속 여부(핸들 테이블에서의 Y or N)는 리소스가 생성되는 순간에 결정된다.
(리소스의 보안 속성을 지정하면 된다)

typedef struct _SECURITY_ATTRIBUTES {
         DWORD nLength;                  // 구조체 변수 크기를 바이트 단위로 설정(반드시 구조체 변수 크기로 초기화)
         LPVOID lpSecurityDescriptor; // NULL로 초기화. 핸들의 상속 관점에서 의미를 지니지 않는다
         BOOL bInheritHandle;            // TRUE로 설정. 상속 여부를 결정 짓는 요소
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;

(예 : CreateProcess 함수를 통해 생성되는 자식프로세스 핸들 상속

SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
……
CreateProcess(. . . . , &sa,  . . , TRUE, . . . . );        // &sa 3번째 파라미터(상속 여부 Y)
                                                                               TRUE 5번째 파라미터(핸들 테이블 상속)
……
)



Pseudo 핸들과 핸들의 중복(Duplicate) :

프로세스가 자신의 핸들을 얻는 방법으로 GetCurrentProcess 함수를 사용한다.
하지만 이 함수를 통해 얻은 핸들은 가짜 핸들(Pseudo 핸들)이다.

핸들 테이블에 등록되어 있지 않은 핸들이며,
다만 현재 실행 중인 프로세스를 참조하기 위한 용도로 정의해 놓은 핸들이다.(약속된 상수로 -1 이 반환)
(참고 : CloseHandle 함수의 파라미터로 전달하더라도 아무 일도 발생하지 않는다)

BOOL DuplicateHandle(              // 진짜 핸들 획득
          HANDLE hSourceProcessHandle,    // 복제할 핸들을 소유하는 프로세스를 지정
          HANDLE hSourceHandle,                // 복제할 핸들을 지정
          HANDLE hTargetProcessHandle,     // 복제된 핸들을 소유할 프로세스를 지정
          LPHANDLE lpTargetHandle,            // 복제된 핸들값을 저장할 변수의 주소를 지정
          DWORD dwDesiredAccess,             // 복제된 핸들의 접근 권한을 지정
          BOOL bInheritHandle,                     // 복제된 핸들의 상속 여부를 지정 
          DWORD dwOptions          // DUPLICATE_SAME_ACCESS : 원본 핸들과 동일한 접근 권한
                                                                                               dwDesiredAccess 파라미터를 무시한다.
                                                   DUPLICATE_CLOSE_SOURCE : CloseHandle 함수와 같은 기능
                                                   이 두 옵션은 비트 단위 OR 연산자를 통해 동시 전달 가능
);

(예 :

DuplicateHandle(
        프로세스 A 핸들,      // 프로세스 A에 존재하는
        256,                        // 핸들 256의 정보를 
        프로세스 B 핸들,      // 프로세스 B의 핸들 테이블에 등록된다.
        &val,                      // 등록된 핸들의 값은 변수 val에 저장한다.
        ……
);

DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &hProcess, 0, TRUE,
                        DUPLICATE_SAME_ACCESS);
// GetCurrentProcess() 함수를 통해서 얻은 가짜 핸들을 진짜 핸들로 구성해서 핸들 테이블에 등록

복사된다고 해서 핸들값까지 똑같진 않다.
그리고 서로 독립된 프로세스 영역의 핸들이기 때문에 값이 같건 다르건 별 의미를 지니지 않는다.

자신의 핸들을 복사할 경우 자식 프로세스에게 자신의 프로세스 커널 오브젝트 핸들을 상속할 수 있다
GetCurrentProcess 함수을 통해 얻은 핸들을 복사할 경우 진짜 핸들이 생성되어 핸들 테이블에 등록된다

DuplicateHandle 함수에 의해서 핸들이 복사되고 나면, UC(Usage Count)는 증가한다.
따라서 복사된 핸들에 대해서도 CloseHandle 함수를 통해 핸들을 반환해야 한다)
Posted by Dakuo

이름없는 파이프(Anonymous Pipe) :

단방향 통신방식이며, 파이프를 통해서 생성된 핸들을 기반으로 통신하기에 프로세스들 사이에 관계가 있어야 한다.

BOOL CreatePipe(
          PHANDLE hReadPipe,     // 데이터를 읽기 위한 파이프 끝 핸들
          PHANDLE hWritePipe,     // 데이터를 쓰기 위한 파이프 끝 핸들
          LPSECURITY_ATTRIBUTES lpPipeAttributtes, // 보안 정보
          DWORD nSize                // 파이프의 버퍼 사이즈
);

(참고 : 파이프는 두 개의 끝을 가지고 있다.
한쪽 끝에는 데이터가 들어가고 다른 한쪽에서는 들어간 데이터가 흘러 나온다)



이름있는 파이프(Named Pipe) :

주소 정보가 있어 관게없는 프로세스들 사이에서도 통신이 가능하며 양방향 통신을 지원한다.


서버 영역 :

CreateNamedPipe 함수를 통해 파이프를 생성한다.

HANDLE CreateNamedPipe(
          LPCTSTR lpName,             // 파이프 이름을 지정한다.
          DWORD dwOpenMode,       // 파이프 개방시 읽기/쓰기 모드를 지정한다.
                                                     PIPE_ACCESS_DUPLEX : 읽기, 쓰기가 모두 가능하도록 설정.
                                                     PIPE_ACCESS_INBOUND : 읽기만 가능
                                                     PIPE_ACCESS_OUTBOUND : 쓰기만 가능
          DWORD dwPipeMode,        // 데이터 송·수신 타입, 블로킹 모드. 총 3가지를 설정
          DWORD nMaxInstances,     // 생성할 수 있는 파이프의 최대 개수
          DWORD nOutBufferSize,      // 출력 버퍼 사이즈
          DWORD nInBufferSize,        // 입력 버퍼 사이즈
          DWORD nDefaultTimeOut,   // 연결을 기다리는 시간(밀리세컨드)
          LPSECURITY_ATTRIBUTES lpSecurityAttributes // 보안 속성
);  

DWORD dwPipeMode :

데이터 전송방식 : PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE
데이터 전송 시 바이너리 형태로 전송할 것인지, 텍스트 형태로 전송할 것인지 결정.

데이터 수신방식 : PIPE_READMODE_BYTE, PIPE_READMODE_MESSAGE
데이터 수신 시 바이너리 방식으로 읽을 것인지, 텍스트 방식으로 읽을 것인지 결정.

함수 리턴 방식 : PIPE_WAIT(블로킹), PIPE_NOWAIT(넌-블로킹)
무조건 PIPE_WAIT 가 전달된다.
(참고 : PIPE_NOWAIT 는 Microsoft LAN Manager version 2.0과 호환성을 위해 제공되는 파라미터이다)


ConnectNamedPipe 함수를 호출하여 연결 요청을 기다리는 파이프로 상태를 변경한다.

BOOL ConnectNamedPipe(
         HANDLE hNamedPipe,                    // CreateNamedPipe 함수 호출을 통해 생성한 파이프의 핸들을 전달
         LPOVERLAPPED lpOverlapped         // 중첩 I/O를 위하여 전달
);



클라이언트 영역 :

CreateFile 함수를 통해 파이프로 연결 요청을 한다.


WaitNamedPipe 함수를 통해 서버와의 파이프 연결을 기다린다.

BOOL WaitNamedPipe(
         LPCTSTR lpNamedPipeName,          // 상태 확인의 대상이 되는 파이프 이름
         DWORD nTimeOut                            // 타임-아웃 시간 설정
                                                                  이 시간이 지나서 함수를 빠져나올 경우 FALSE 리턴
                                                                  NMPWAIT_WAIT_FOREVER : 연결 가능한 상태가 될때까지 기다림
                                                                  NMPWAIT_USE_DEFAULT_WAIT : 디폴트 시간만큼 기다림
);



(추가 :

파이프의 속성을 변경시키는 함수 : SetNamedPipeHandleState

BOOL SetNamedPipeHandleState(
          HANDLE hNamedPipe,                      // 파이프와의 연결 속성을 변경시키기 위한 핸들
          LPDWORD lpMode,                           // 데이터 송·수신 모드와 함수의 리턴모드에 대한 값 전달
          LPDWORD lpMaxCollectionCount,       // 버퍼링할 수 있는 최대 바이트 크기
                                                                    클라이언트와 서버가 같은 PC 일 경우 NULL 을 전달
          LPDWORD lpCollectDataTimeOut        // 버퍼링을 허용하는 최대 시간(밀리세컨드)
                                                                    클라이언트와 서버가 같은 PC 일 경우 NULL 을 전달
);)

Posted by Dakuo

IPC (Inter-Process Communication) : 프로세스 사이의 통신


메일 슬롯(Mail Slot)의 원리 :

프로세스는 자신에게 할당된 메모리 공간 이외에는 접근할 수가 없다.

따라서 Mail Slot (우체통)을 이용하여 데이터를 송·수신한다.

데이터를 수신하고자 하는 프로세스 A(Receiver)가 Mail Slot 을 생성한다.

데이터를 송신하고자 하는 프로세스 B(Sender)가 프로세스 A의 Mail Slot 의 주소로 데이터를 송신한다.

프로세스 A가 자신의 Mail Slot 을 통해 데이터를 얻게 된다.



메일 슬롯(Mail Slot) 구성 :

Recevier :

Mail Slot 를 생성한다.

HANDLE CreateMailslot(
      LPCTSTR lpName,                 // 메일슬롯 주소
      DWORD nMaxMessageSize,    // 메일슬롯의 버퍼 크기
      DWORD lReadTimeout,            // 최대 블로킹 시간(밀리세컨드 : ms) 
      LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 핸들 상속 여부
};

Mail Slot 에 들어있는 데이터를 읽어온다.

BOOL ReadFile(
      HANDLE hFile,                                  // 해당 파일(Mail Slot)에 존재하는 데이터를 읽어들인다
      LPCVOID lpBuffer,                             // 읽어 들인 데이터를 저장할 버퍼 지정
      DWORD nNumberOfBytesToWrite,       // 읽어 들일 데이터의 최대 크기 지정  
      LPDWORD lpNumberOfBytesWritten,    // 함수 호출 완료 후 읽어 들인 실제 데이터의 크기를 바이트 단위로
                                                                얻기 위한 변수의 주소 지정
      LPOVERLAPPED lpOverlapped           //
);


Sender :

Mail Slot 의 주소를 얻는다.
(예: "\\\\컴퓨터이름\\mailslot\\[path]name", (아스키코드 기반), 로컬컴퓨터 : . (컴퓨터이름))

해당 Mail Slot 과 연결한다.

HANDLE CreateFile(
      LPCTSTR lpFileName,                // 개방(open)할 파일 이름
      DWORD dwDesiredAccess,          // 읽기/쓰기 모드를 지정한다.(or (l) 연산으로 결합)
                                                          GENERIC_READ : 읽기 모드
                                                          GENERIC_WRITE : 쓰기 모드
      DWORD dwShareMode,               // 파일 공유방식 
                                                          0 : 다른 프로세스에 절대 공유 불가. 중복 개방 불가
                                                          FILE_SHARE_READ : 다른 프로세스에서 동시 읽기 접근 가능
                                                          FILE_SHARE_WRITE : 다른 프로세스에서 동기 쓰기 접근 가능
      LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 보안 속성
      DWORD dwCreationDisposition,    // 파일의 생성되는 방법을 지정
                                                          CREATE_ALWAYS : 항상 새 파일을 생성
                                                          CREATE_NEW : 새 파일 생성, 같은 이름의 파일이 존재하면 생성 실패
                                                          OPEN_ALWAYS : 기존 파일 개방. 없으면 생성
                                                          OPEN_EXISTING : 기존 파일 개방, 존재하지 않으면 함수 호출 실패
                                                          TRUNCATE_EXISTING : 기존 파일의 내용 지우고 개방.
      DWORD dwFlagsAndAttributes,     // 파일의 특성 정보를 설정 (기본적 FILE_ATTRIBUTE_NORMAL 사용)
      HANDLE hTemplateFile               // 기존에 존재하는 파일과 동일한 특성을 가지는 새 파일을 만들때 사용
);

해당 Mail Slot 에 데이터를 전송한다.

BOOL WriteFile(
      HANDLE hFile,                                  // 데이터를 읽어 들일 파일을 지정(Mail Slot)
      LPCVOID lpBuffer,                             // 전송할 데이터가 저장되어 있는 버퍼 지정
      DWORD nNumberOfBytesToWrite,       // 전송할 데이터 크기를 지정한다  
      LPDWORD lpNumberOfBytesWritten,    // 함수 호출 완료 후 전송된 실제 데이터의 크기를 바이트 단위로
                                                                얻기 위한 변수의 주소 지정
      LPOVERLAPPED lpOverlapped           //
);



메일 슬롯의 특징 :

메일슬롯에 할당된 주소를 기반으로 통신하기 떄문에 관계없는 프로세스들 사이에서도 통신이 가능하다.

(네트워크 연결되어 통신하는 프로세스들이나 부모 자식간의 연관 관계가 없는 프로세스들)


한쪽 방향으로만 메시지를 전달할 수 있다.

(서로 데이터를 주고 받기 위해서는 각각 메일슬롯을 생성해야 한다)


브로드캐스팅(Broadcasting) 방식의 통신을 지원한다.

Sender 는 한번의 메시지 전송으로 여러 Receiver 에게 동일한 메시지를 동시에 전송하는 것이 가능하다.

(예 :

컴퓨터 이름이 와야 하는 부분에 '*' 을 사용하면 네트워크에 연결되어 있는 모든 컴퓨터를 지칭한다.

따라서 모든 컴퓨터에 해당 주소로 생성된 메일슬롯에 동일한 메시지가 전달된다)

Posted by Dakuo

커널 오브젝트 : Windows 운영체제에서 리소스(Resource : 프로세스, 쓰레드, 파일)들을
                      관리하기 위한 데이터를 저장하는 메모리 블록



Windows에서 관리하는 리소스 특성을 변경하기 위해서는 해당 리소스의 커널 오브젝트를 조작해야한다.

해당 리소스의 정보를 저장하고 있는 커널 오브젝트에 접근하기 위해서는

커널 오브젝트들을 구별하는 커널 오브젝트 핸들(Object Handle)을 얻어야 한다.



커널 오브젝트와 핸들 :

커널 오브젝트는 Windows 운영체제에 종속적이다.

사용자가 리소스 생성을 요청하면 운영체제에서 사용자가 요청한 대로 리소스를 생성하게 되고

이를 관리하기 위해 커널 오브젝트를 생성한다.

즉, 커널 오브젝트는 Windows 운영체제에 의해 생성 및 소멸이 결정된다.


리소스가 생성되어 커널 오브젝트가 생성되면 커널 오브젝트에 핸들이 부여되고

부모 리소스도 구조체 변수를 통해 자식의 커널 오브젝트 핸들을 획득할 수 있다.

즉, 부모와 자식이 자식의 커널 오브젝트를 공유한다.


예)

부모 프로세스가 자식 프로세스의 커널 오브젝트 핸들 획득.

typedef struct _PROCESS_INFORMATION
{
      HANDLE hProcess;            // 커널 오브젝트 구분 위한 핸들
      HANDLE hThread;
      DWORD dwProcessId;        // 프로세스 구분 위한 ID
      DWORD dwThreadId;         
} PROCESS_INFORMAION;

위 구조체는 CreateProcess 호출할때 인자로 전달되는 구조체이다.(&pi)
호출이 완료되면 이 구조체에 자식 프로세스의 커널 오브젝트 핸들값이 hProcess 에 저장된다.

자신의 커널 오브젝트 핸들 획득.

GetCurrentProcess()

위함수를 사용하면 자신의 커널 오브젝트 핸들값을 리턴한다.



커널 오브젝트와 Usage Count :

운영체제는 해당 커널 오브젝트를 참조하는 대상이 하나도 없을때 커널 오브젝트를 소멸시킨다.

참조하는 대상은 Usage Count(참조 횟수)로 관리한다.

즉, Usage Count = 0 일 때 커널 오브젝트를 소멸한다.

리소스의 생성과 동시에 해당 커널 오브젝트의 Usage Count = 1 이된다.
생성이 완료되면 부모가 자식 리소스 커널 오브젝트의 핸들을 획득하기에 Usage Count = 2 가 된다.

만약 자식 리소스를 소멸하면 Usage Count 가 하나 줄어든다.

여기서 문제가 발생한다. 리소스가 소멸됬음에도 그 리소스를 관리하는 커널 오브젝트는 소멸되지 않는다.
(Usage Count 가 1이므로)

Usage Count = 0 을 만들기 위해선

부모의 자식의 커널 오브젝트 참조를 해제해야 한다.

CloseHandle() 함수를 사용하면 자식의 커널 오브젝트 핸들을 반환하여 참조를 해제한다.

즉, Usage Count 가 하나 줄어든다.

따라서 커널 오브젝트를 소멸하기 위해서는

부모가 소유한 자식의 커널 오브젝트 핸들을 반환해야 한다.


예)

부모 프로세스가 자식의 커널 오브젝트 핸들을 반환하지 않아 커널 오브젝트가 소멸되지 않았다고 가정할때

계산기 프로세스를 생성하면 그에 따른 커널 오브젝트가 생성된다.

계산기 프로세스를 종료시켜도 커널 오브젝트는 소멸되지 않는다(Usage Count =  1이므로 )
 
실행-> 종료, 실행 -> 종료 를 반복한다고 할때

커널 오브젝트는 실행의 수만큼 존재한다.

따라서 이 문제를 해결하기 위해 CloseHandle 을 통해 부모의 자식의 커널 오브젝트 참조를 해제함으로써

자식 프로세스가 종료될때 Usage Count = 0 이 되어 커널 오브젝트가 소멸된다)


(참고 :

바탕화면에서 아이콘을 통해 프로세스를 생성할 경우에도 Usage Count = 2 다.

바탕화면 자체도 프로세스이기 때문이다. 이 때는 바탕화면이 부모 프로세스가 된다.
(Cmd 에서 실행하면 Cmd 가 부모 프로세스)

즉, 프로세스는 생성과 동시에 Usage Count = 2 가 된다)

Posted by Dakuo
CreateProcess

Process A                     ------->             Process B
(부모 프로세스)           CreateProcess        (자식 프로세스)

BOOL CreateProcess(
    LPCTSTR lpApplicationName,    // 생성될 프로세스의 이름
    LPTSTR lpCommandLine,         // 생성될 프로세스에 인자 전달(변수만 가능)
    LPSECURITY_ATTRIBUTES lpProcessAttributes, // 프로세스의 보안 속성 지정
    LPSECURITY_ATTRIBUTES lpThreadAttributes,   // 쓰레드의 보안 속성 지정
    BOOL bInheritHandles,                   // TRUE : 부모 프로세스가 소유하는 상속 가능한 핸들을 상속한다.
    DWORD dwCreationFlags,              // 생성하는 프로세스의 특성을 결정짓는 옵션(우선순위)
    LPVOID lpEnvironment,                  // 생성하는 프로세스의 Environment Block 지정
                                                           NULL : 부모 프로세스의 환경 블록 복사
    LPCTSTR lpCurrentDirectory,          // 생성하는 프로세스의 현재 디렉터리 설정
                                                            NULL : 부모 프로세스의 현재 디렉터리
    LPSTARTUPINFO lpStartupInfo,       // STARTUPINFO 구조체 변수 초기화한 후
                                                           변수의 포인터를 인자로 전달
    LPPROCESS_INFORMATION lpProcessInformation
                                                      //생성하는 프로세스의 정보를 얻기 위한 인자
                                                      //PROCESS_INFORMATION 구조체 변수의 주소값을 인자로 전달
);

첫번째 인자에 실행파일을 전달할 경우 현재 디렉터리를 기준으로 실행파일을 검색한다.
두번째 인자에 실행파일을 전달할 경우 표준 검색경로 순서대로 실행파일을 검색한다.



STARTUPINFO 구조체

typedef struct _STARTUPINFO {
    DWORD  cb;                           //구조체 변수 크기
    LPTSTR lpReserved;
    LPTSTR lpDesktop;
    LPTSTR lpTitle;                      // 타이틀 바 제목
    DWORD  dwX;                        // 프로세스 윈도우의 x좌표
    DWORD  dwY;                        // 프로세스 윈도우의 y좌표
    DWORD  dwXSize;                  // 프로세스 윈도우의 가로길이
    DWORD  dwYSize;                  // 프로세스 윈도우의 세로길이
    DWORD  dwXCountChars;
    DWORD  dwYCountChars;
    DWORD  dwFillAttribute;
    DWORD  dwFlags;                   // 설정된 멤버의 정보
    WORD   wShowWindow;
    WORD   cbReserved2;
    LPBYTE lpReserved2;
    HANDLE hStdInput;           
    HANDLE hStdOutput;        
    HANDLE hStdError;          
} STARTUPINFO, *LPSTARTUPINFO;



현재 디텍터리(Current Directory) 설정

현재 디렉터리 확인 :

DWORD GetCurrentDirectory(
       DWORD nBufferLength,      // 현재 디렉터리 정보가 저장될 메모리 버퍼의 크기(문자열 길이정보)
       LPTSTR lpBuffer               // 현재 디텍터리 정보가 저장될 메모리 버퍼의 포인터
};


현재 디렉터리 변경 :

BOOL SetCurrentDirectory(
      LPCTSTR lpPathName      // 디렉터리 경로
};



표준 검색경로(실행 파일 이름(경로없이)만 전달할 경우 다음의 순서대로 실행파일을 찾는다)

1. 실행 중인 프로세스의 실행파일이 존재하는 디렉터리
2. 실행 중인 프로세스의 현재 디렉터리(Current Directory)
3. Windows 시스템 디렉터리(System Directory)
4. Windows 디렉터리(Windows Directory)
5. 환경변수 PATH에 지정되어 있는 디렉터리


(추가 :

WinExec() 함수

CreateProcess 의 많은 옵션을 따로 지정할 필요가 없다면

WinExec() 함수로 간단하게 프로세스를 생성할 수 있다.

WinExec(__in LPCSTR lpCmdLine, __in UINT uCmdShow)

예)

WinExec("C:\Windows\System32\winmine.exe", SW_SHOW);
Posted by Dakuo
/*       자식 프로세스(Child Process) */
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <locale.h>
INT_PTR _tmain(INT_PTR argc, TCHAR* argv[])
{
        _tsetlocale(LC_ALL, _T("Korean"));
        _tprintf(_T("자식 프로세스(Child Process) \n"));
        system("pause");
        return 0;
}

/*
      부모 프로세스(Parent Process : CreateProcess 이용)
*/
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <locale.h>
#define DIR_LEN MAX_PATH+1
INT_PTR _tmain(INT_PTR argc, TCHAR* argv[])
{
         _tsetlocale(LC_ALL, _T("Korean"));
         STARTUPINFO si={0}; 
         PROCESS_INFORMATION pi;
 
         si.cb = sizeof(si);
         si.dwFlags = STARTF_USEPOSITION | STARTF_USESIZE;
         si.dwX=100;
         si.dwY=200;
         si.dwXSize = 300;
         si.dwYSize = 200;
         si.lpTitle = _T("Hacker Dakuo");
 
        _tprintf(_T("부모 프로세스(Parent Process) \n"));
        TCHAR command[] = _T("dakuo_C_Process.exe");
        BOOL state;
        state = CreateProcess(NULL, command, NULL, NULL, TRUE,
                                        CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
        // CREATE_NEW_CONSOLE : 자식 프로세스를 새로운 콘솔 윈도우로 생성한다.
        // 콘솔을 기반으로 하지 않는 프로세스에는 의미가 없다
        // NULL : 부모 프로세스의 콘솔 윈도우를 자식 프로세스와 공유        
        
        if(state!=0)
        {
               _tprintf(_T("Creation Sucess\n"));
        }
        else
        {
              _tprintf(_T("Creation Fail\n"));
        }
        system("pause");
        return 0;
}

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

커널 오브젝트(Kernel Object)  (2) 2010.02.11
CreateProcess 함수  (0) 2010.02.10
프로세스(Process) 생성 소스  (0) 2010.02.10
프로세스의 생성과 소멸  (0) 2010.02.09
명령 프롬프트(Cmd) 작성  (0) 2010.02.09
64비트 기반 프로그래밍  (0) 2010.02.08
Posted by Dakuo

프로세스(Process) : 메모리에 로드되어 실행중인 프로그램

1. 프로세스를 구성하는 요소

메모리 구조 : 프로세스 생성시 만들어진다.


Register Set : 프로세스 실행을 위해 필요한 데이터들로 채워진다.



2. 프로세스의 스케줄링과 상태 변화

프로세스의 스케줄링(Scheduling) : 프로세스의 CPU 할당 순서 및 방법을 결정짓는 일
(스케줄링 알고리즘(Scheduling Algorithms) : 스케줄링에 사용되는 알고리즘)

스케줄러(Scheduler) : 스케줄링 알고리즘을 적용해서 실제로 프로세스를 관리하는 운영체제 요소(모듈)


(참고 : 멀티 프로세스(Multi-Process) 운영체제의 CPU 사용

실행해야 할 프로세스 여러개라면
멀티 프로세스는 모든 프로세스를 일단 실행시킨 후 운영체제의 스케줄러에 의해 프로세스들을 관리한다.

일반적으로 프로그램은 많은 시간을 I/O(입출력)에 할당한다.
프로그램이 I/O 를 하고 있을때 CPU는 일을 하지 않고 가만히 있는다.

이때 스케줄러가 다른 프로세스로 교체를 하여 CPU를 쉬지 않게 하는것이 멀티 프로세스 기반이다)


프로세스의 상태 변화 :


1. S(Start) : 프로세스 생성 -> Ready

프로세스는 생성과 동시에 Ready 상태로 들어간다.
Ready 상태에 있는 프로세스는 CPU(정확히 말하면 운영체제의 스케줄러)에 의해 실행되기를 기다린다.

2. Ready -> Running

Ready 상태에 있는 프로세스들은 스케줄러에 의해 관리된다.
스케줄러는 Ready 상태에 있는 프로세스중 하나를 선택해서 CPU에 의해 실행될 수 있도록 한다.
(스케줄링 알고리즘 기반)

스케줄러에 의해 선택된 프로세스는 Running 상태가 되어 실행된다.

3. Running -> Ready

일의 중요도에 따라 프로세스들은 생성시 우선순위(Priority)가 매겨진다.
우선순위가 더 높은 프로세스가 Ready 상태에 있다면 스케줄러는 실행중인 프로세스를 Ready상태로 내리고
실행시킨다. Ready 상태로 간 프로세스는 우선순위가 더 높은 프로세스가 종료되기만을 기다린다.

4. Runnimg -> Blocked

실행 중에 있는 프로세스가 실행을 맘추는 상태(Blocked)로 들어간다.
I/O 에 관련된 일을 하는 경우(CPU에 의해 더이상 실행될 수 없다) 스케줄러는 Blocked 상태로 프로세스를 내리고
Ready 상태인 프로세스 중 하나를 실행시킨다.

5. Blocked -> Ready

Blocked 상태는 스케줄러에 의해서 선택될 수 없다.
따라서 Blocked 에 있는 프로세스는 다시 실행될 수 있는 Ready 상태로 간다.

예) :  I/O 가 완료된 프로세스

6. Blocked -> E(Exit)

프로세스를 종료시킬 경우 Blocked 상태를 거쳐 E(Exit)상태로 간다.


컨텍스트 스위칭(Context Switching)

CPU 내에 존재하는 레지스터들은 현재 실행 중에 있는 프로세스 관련 데이터들로 채워진다.

스케줄러에 의해 실행중인 프로세스가 변경되면 CPU 내의 존재하는 레지스터들도 컨텍스트 스위칭에 의해

데이터를 변경한다.


(참고 :

실행되는 프로세스의 변경과정에서 발생하는 컨텍스트 스위칭은
레지스터 갯수가 많을수록 프로세스별로 관리되어야 할 데이터 종류가 많을수록 시스템에 많은 부담을 준다)

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

CreateProcess 함수  (0) 2010.02.10
프로세스(Process) 생성 소스  (0) 2010.02.10
프로세스의 생성과 소멸  (0) 2010.02.09
명령 프롬프트(Cmd) 작성  (0) 2010.02.09
64비트 기반 프로그래밍  (0) 2010.02.08
Windows에서의 문자셋(Character Sets)  (0) 2010.02.08
Posted by Dakuo
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <tchar.h>
#include <locale.h>
#define STR_LEN 256                                                   // 문자열 길이
#define CMD_TOKEN_NUM 10                                      // 입력가능한 최대 문자열 개수
TCHAR ERROR_CMD[100] = _T(" '%s'는 실행할 수 있는 프로그램이 아닙니다. \n");
INT_PTR CmdProcessing();
TCHAR* StrLower(TCHAR*);
INT_PTR _tmain(INT_PTR argc, TCHAR* argv[])
{
       _tsetlocale(LC_ALL, _T("Korean"));                         // 유니코드 한글을 입력받기 위해
       INT_PTR isExit;
       while(TRUE)                                                         // 무한루프
       {
                isExit = CmdProcessing();
                if(isExit == TRUE)
                {
                          _tprintf(_T("종료합니다. \n"));
                          break;
                }
       }
       return 0;
}
TCHAR cmdString[STR_LEN] = {0};
TCHAR CmdTokenList[CMD_TOKEN_NUM][STR_LEN];
TCHAR seps[] = _T(" , \t \n");
INT_PTR CmdProcessing()                                           // 명령어 입력받아서 해당 명령어 기능 수행 함수
{
        _tprintf(_T("Command Prompt >> "));
        _tscanf(_T("%s"), cmdString);                               // 명령어 입력받음

        TCHAR* token = _tcstok(cmdString, seps);             // 명령어를 분리함, ex) copy a b  -> copy,    a,      b         INT_PTR tokenNum = 0;         while(token != NULL)         {                 _tcscpy(CmdTokenList[tokenNum++], StrLower(token));  // 분리된 문자열이 없을때까지                                                                                                    // 모두 소문자로 변경한 명령어를 붙여넣음                 token = _tcstok(NULL, seps);         }         if(!_tcscmp(CmdTokenList[0], _T("exit")))                                         {                  return TRUE;         }         else if(!_tcscmp(CmdTokenList[0], _T("추가명령어")))             // 명령어가 개수만큼 else if 추가         {         }                else                                                                                   // 해당명령어가 없으면 에러출력         {               _tprintf(ERROR_CMD, CmdTokenList[0]);          }          return 0; } TCHAR* StrLower(TCHAR* pStr)                                      // 대문자 -> 소문자 (대소문자 구별없게 하기 위해서) {          TCHAR* ret = pStr;                  while(*pStr)                                                          // NULL 만날때까지 반복,          {                  if(_istupper(*pStr))                                         // 대문자이면                  {                          *pStr = _tolower(*pStr);                           // 소문자로 변경                  }                  pStr++;           }                 return ret; }
Posted by Dakuo

1. 64비트와 32비트

구분 :
 
1. I/O BUS를 통해서 한번에 송 · 수신할 수 있는 데이터 크기

2. 데이터 처리능력


32비트 -> 64비트 :

1. 더 넓은 메모리 공간(가상 메모리) 활용(32비트 : 표현할수 있는 주소 4GB -> 64비트 : 표현할수 있는 주소 16TB)

2. 연산속도의 증가(32비트 : 최대 32비트 데이터 처리 --> 64비트 : 최대 64비트 데이터 처리)



2. 64비트 기반 프로그래밍

1) LLP64, LP64 :

 운영체제  모델  char  short  int   long   포이터
 Windows  LLP64  1바이트  2바이트  4바이트  4바이트  8바이트
 UNIX  LP64  1바이트  2바이트  4바이트  8바이트  8바이트

Windows에서는 포인터만 8바이트로 표현함으로써 32비트 시스템과의 호환성을 중시한다.


2) 64비트와 32비트 호환의 문제점 :

64비트 시스템에서는 포인터가 8바이트 이므로 int, long 등 정수형 데이터로 변환하게 되면 4바이트의 데이터 손실이 발생한다.
(하지만 변환하고자 하는 포인터가 4GB안에 있으면 4바이트로도 표현이 가능하므로 데이터 손실이 발생하지 않는다)


3) 64비트 표현 Windows 스타일 자료형 :

 WINDOWS 자료형  의미  정의 형태
 BOOL  Boolean variable  typedef int BOOL
 DWORD  32-bit unsigned integer  typedef unsigned long DWORD;
 DWORD32  32-bit unsigned integer  typedef unsigned int DWORD32
 DWORD64  64-bit unsigned integer  typedef unsigned __int64 DWORD64
 INT  32-bit signed integer  typedef int INT;
 INT32  32-bit signed integer  typedef signed int INT32
 INT64  64-bit signed integer  typedef signed __int64 INT64
 LONG  32-bit signed integer  typedef long LONG
 LONG32  32-bit signed integer  typedef signed int LONG32
 LONG64  64-bit signed integer  typedef signed __int64 LONG64
 UINT  Unsigned INT  typedef int UINT
 UINT32  Unsigned INT32  typedef unsigned int UINT32
 UINT64  Unsigned INT64  typedef unsigned __int64 UINT64
 ULONG  Unsigned LONG  typedef unsigned int ULONG
 ULONG32  Unsigned LONG32  typedef unsigned int ULONG32
 ULONG64  Unsigned LONG64  typedef unsigned __int64 LONG64
 PINT  INT에 대한 포인터  typedef int* PINT
 PINT32  INT32에 대한 포인터  typedef signed int* PINT32
 PINT64  INT64에 대한 포인터  typedef signed __int64* PINT64


4) Polymorphic 자료형(32비트 64비트 동시지원) :

#if defined(_WIN64)

     typedef __int64 LONG_PTR;
     typedef unsigned __int64 ULONG_PTR;

     typedef __int64 INT_PTR;
     typedef unsigned __int64 UINT_PTR;

#else

     typedef long LONG_PTR;
     typedef unsigned long ULONG_PTR;

     typedef int INT_PTR;
     typedef unsigned int UINT_PTR;

#endif

(참고 : 64비트 기반 프로젝트 구성시 _WIN64 매크로는 는 자동으로 삽입된다
          32비트 기반 프로젝트 구성시 _WIN32 매크로 자동 삽입)


5) 실제 적용 :

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

UINT_PTR CalDistance(UINT_PTR val1, UINT_PTR val2);

int _tmain()
{
        INT_PTR num1 = 10;
        INT_PTR num2 = 20;

       _tprintf(_T("Distance : %d UINT_PTR SIZE : %d \n"),
                CalDistance((UINT_PTR)&num1, (UINT_PTR)&num2), sizeof(num1));

        return 0;
}

UINT_PTR CalDistance(UINT_PTR val1, UINT_PTR val2)
{
          return val1 - val2;
}

sizeof에 의해서 컴파일 환경이 몇비트인지 알수 있다.

Posted by Dakuo

1. 문자셋의 종류와 특성

SBCS(Single Byte Character Set) : 1바이트로 문자를 표현한다. 대표적으로 아스키 코드(ASCII CODE)가 있다.

MBCS(Multi Byte Character Set) : 문자를 표현 하는데 있어서 ASCII CODE 는 1바이트로 그 외에는 2바이트로 처리한다.

WBCS(Wide Byte Character Set) : 모든 문자를 2바이트로 표현한다. 대표적으로 유니코드(UNICODE)가 있다.


2. WBCS 기반 프로그래밍

기본적으로 운영체제에서는 문자열을  MBCS 기반으로 처리한다.

예)

#include <stdio.h>
#include <string.h>

int main()
{
         char str[] = "AB가나";
         int len = strlen(str);
         printf("%d", len);

         return 0;
}

실행 결과

이 코드의 실행 결과에서 MBCS 기반으로 프로그래밍 했을때 생기는 문제점을 알게될 것이다.
(문자열마다 할당 바이트가 다르기 때문에 생기는 문제점)

이러한 문제점의 한 해결책이 WBCS 기반 프로그래밍이다.

WBCS 기반으로 프로그래밍 하기 위해선 SBCS 기반의 함수를 WBCS 기반의 함수로 바꿔줘야 한다.

 SBCS 함수  WBCS 함수
 main  wmain
 char  wchar_t
 "상수"  L"상수"
 strlen   wcslen
 strcpy  wcscpy
 strncpy  wcsncpy
 strcat  wcscat
 strncat  wcsncat
 strcmp  wcscmp
 strncmp  wcsncmp
 printf  wprintf
 scanf  wscanf
 fgets  fgetws
 fputs  fputws
(참고 : wprintf, fputws 와 같은 함수들을 통해서 유니코드 기반으로 한글을 출력하고 싶다면 함수를 호출하기 전에
_wsetlocale(LC_ALL, L"korean");        // #include <locale.h>
_wsetlocale는 프로그램이 실행되는 나라 및 지역에 대한 정보를 설정하는 함수이다.)

WBCS 함수를 사용할때는 파라미터도 모두 유니코드 기반 문자열이어야 한다.

예)

#include <stdio.h>

int wmain(int argc, wchar_t* argv[])
{
       for(int i = 1 ; i < argc; i++)
       {
                wprintf(L"%s", argv[i]);
       }
       return 0;
}



3. MBCS, WBCS 동시지원 프로그래밍

1) Windows에서 정의하고 있는 새로운 자료형 // #include <windows.h>

typedef      char                      CHAR;
typedef      wchar_t                 WCHAR;
 
#define      CONST                  const;

typedef     CHAR*                    LPSTR;
typedef     CONST CHAR*        LPCSTR;

typedef     WCHAR*                 LPWSTR;
typedef     CONST WCHAR*     LPCWSTR;

(Tip : 새로운 자료형을 정의하는 이유 :
1. 선언의 편리성 : typedef unsigned int size_t (긴 문장을 간결하게 대체하며 의미도 강하게 부여된다)
2. 확장의 용이성 : typedef unsigned char STR_LEN -> typedef unsigned int STR_LEN
(한번의 수정으로 STR_LEN 자료형을 쓰는 모든 변수가 char->int 로 확장되었다))


2) MBCS와 WBCS를 동시에 지원하기 위한 매크로

(매크로에 대한 개념 : http://dakuo.tistory.com/~~~)

Windows에서 MBCS와 WBCS를 동시에 수용하는 형태의 프로그램 구현을 위해서 매크로를 정의하고 있다.

#ifdef   UNICODE
     typedef WCHAR         TCHAR;
     typedef LPWSTR        LPTSTR;
     typedef LPCWSTR      LPCTSTR;
#else
     typedef CHAR           TCHAR;
     typedef LPSTR          LPTSTR;
     typedef LPCSTR        LPCTSTR;
#endif

다음은 tchar.h 에 선언내어 있는 내용이다.  // #include <tchar.h>

#ifdef   _UNICODE
     #define  __T(x)  L  ##  x
#else
     #define  __T(x)  x
#endif

#define  _T(x)        __T(x)
#define  _TEXT(x)  __T(x)


3) MBCS와 WBCS를 동시에 지원하기 위한 함수

tchar.h 에 선언되어 있는 함수이름 매크로이다.

#ifdef _UNICODE 
  #define _tmain      wmain 
  #define _tcslen     wcslen 
  #define _tcscat     wcscat 
  #define _tcscpy    wcscpy 
  #define _tcsncpy  wcsncpy
  #define _tcscmp   wcscmp 
  #define _tcsncmp  wcsncmp
  #define _tprintf      wprintf 
  #define _tscanf     wscanf 
  #define _fgetts      fgetws 
  #define _fputts      fputws
#else
  #define _tmain      main 
  #define _tcslen     strlen 
  #define _tcscat     strcat 
  #define _tcscpy    strcpy 
  #define _tcsncpy  strncpy
  #define _tcscmp   strcmp 
  #define _tcsncmp  strncmp
  #define _tprintf      printf 
  #define _tscanf     scanf 
  #define _fgetts      fgets 
  #define _fputts      fputs
#endif


4) 실제 적용( 위에 1), 2), 3)의 선언들을 잘 참조하면서 하나씩 순서대로 적용해본다)

TCHAR str[5];

#define UNICODE (유니코드가 정의되어 있다면)
TCHAR str[5];  ->  WCHAR str[5];  ->  wchar_t str[5];

유니코드가 정의되어 있지 않다면
TCHAR str[5];  ->  CHAR str[5];    ->  char str[5];

_T("DAKUO");

_T("DAKUO");  -->  __T("DAKUO");

#define _UNICODE
__T("DAKUO");  -->  L"DAKUO";

유니코드가 정의되어 있지 않다면
__T("DAKUO");  -->  "DAKUO";

_tmain

#define _UNICODE
_tmain  -->  wmain

유니코드가 정의되어 있지 않다면
_tmain  -->  main


예)

#define UNICODE       // WBCS 지원, 만약 이부분을 주석처리할 경우 MBCS 지원
#define _UNICODE     // WBCS 지원, 만약 이부분을 주석처리할 경우 MBCS 지원

#include <stdio.h>
#include <windows.h>
#include <tchar.h>

int _tmain(int argc, TCHAR* argv[])
{
        LPTSTR str1 = _T("HACKER");
        TCHAR str2[100] = _T("DAKUO");
        TCHAR str3[20];        

        LPCTSTR pstr = str1;
        
        _tprintf(_T("str1 len = %d \n"), _tcslen(str1));
        _tscanf(_T("%s"), str3);
        
        _tcscat(str2, str3);
        _tprintf(_T("%s\n"), str2);
 
        return 0;
}

Posted by Dakuo


티스토리 툴바