레이블이 윈도우인 게시물을 표시합니다. 모든 게시물 표시
레이블이 윈도우인 게시물을 표시합니다. 모든 게시물 표시

2012년 9월 4일 화요일

리버싱 때 유용한 API



파일 생성하거나 열기(Open) : 파일을 생성하거나 열 때

32bit : CreateFileA
wide : CreateFileW


파일 입출력(Read&Write)


ReadFile : 읽기
WriteFile : 쓰기



파일 접근(Access)


SetFilePointer



시스템 디렉터리를 얻어오는 함수


32bit : GetSystemDirectoryA
wide : GetSystemDirectoryW


.ini 파일과 관련된 함수 : ini 구성 설정에 관련된 함수들


32bit : GetPrivateProfileStringA, GetPrivateProfileIntA, 

             WritePrivateProfileStringA, WritePrivateProfileIntAwide : GetPrivateProfileStringW, GetPrivateProfileIntW, 
             WritePrivateProfileStringW, WritePrivateProfileIntW


레지스트리와 관련된 함수 : 레지스트리의 키를 생성 혹은 삭제할 때


32bit : RegCreateKeyA, RegDeleteKeyA
wide : RegCreateKeyW, RegDeleteKeyW


현재 열린 레지스트리 키를 읽을 때

32bit : RegQueryValueA
wide : RegQueryValueW


레지스트리 키를 열 때

32bit : RegCloseKeyA, RegOpenKeyA
wide : RegCloseKeyW, RegOpenKeyW


객체에서 문자열을 읽을 때

32bit : GetWindowTextA, GetDlgItemTextA
wide : GetWindowTextW, GetDlgItemTextW


객체의 텍스트를 지정

32bit : SetWindowTextA, SetDlgItemTextA
wide : SetWindowTextW, SetDlgItemTextW


메시지 박스

32bit : MessageBoxA, MessageBoxExA
wide : MessageBoxW, MessageBoxExW

메시지 관련

32bit : SendMessageA
wide : SendMessageW


날짜와 시간 : 날짜와 시간을 구할 때

GetLocalTime
SystemTimeToFileTime


윈도우를 생성하거나 제거할 때

32bit : CreateWindowA, CreateWindowExA, DialogBoxParamA
wide : CreateWindowW, CreateWindowExW, DialogBoxParamW






참고 : 리버스엔지니어링 : 역분석 구조와 원리 박병익/이강석 공저





2012년 9월 3일 월요일

윈도우즈 시스템 프로그래밍 [8] - 프로세스간 통신 (IPC) 2.




윈도우즈 시스템 프로그래밍을 공부하면서 정리한 글입니다.



7장.  프로세스간 통신 (IPC) 1



I. 프로세스간 통신(IPC)의 의미



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

- 마이크로소프트에서 Windows 운영체제 소스코드를 공개하지 않아 핸들 테이블이 어떻게 관리되는지
   알 방법이 없음
- 리소스 생성 과정에서 커널 오브젝트가 생성되면, 해당 오브젝트의 핸들이 반환

① 핸들과 커널 오브젝트의 리뷰 (Review)


=> 핸들 256을 통해 0x2400번지에 할당 되어 있는 메일 슬롯에 접근 가능
      BUT 핸들 256이 0x2400번지의 커널 오브젝트를 의미한다는 정보가 없음
      => 핸들 테이블 개념을 도입


② 핸들 테이블의 도입


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


2. 핸들의 상속

- CreateProcess 함수 호출 시 다섯번째 인자가 무엇이냐에 따라 부모 프로세스의 핸들 테이블이
   자식 프로세스에게 상속될 수 있음

① 핸들의 상속에 대한 이해

- 자식 프로세스는 부모 프로세스의 핸들 테이블 정보를 상속 받을 수 있음
   but 모든 핸들 정보를 상속 받진 않음
- 핸들 테이블에는 해당 핸들의 상속 여부 컬럼(column)이 존재
- 상속시, 상속 여부에 대한 정보도 변경 없이 그대로 상속됨
   => 자식 프로세스에 상속된 핸들은 또 다른 자식 프로세스 만들때도 상속됨
- 상속되는 핸들을 결정 하는건 프로그래머 몫


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

BOOL CreateProcess (
   LPCTSTR lpApplicationName,
   LPTSTR lpCommandLine,
   ...
   BOOL bInheritHandles // 핸들 상속 인자
   ...
);

bInheritHandles  -> TRUE 전달할 경우 부모 프로세스의 핸들 테이블은 상속됨
  -> BUT 상속 여부가 Y 로 설정된 핸들에 한해서만 상속이 이루어짐


③ 핸들의 상속과 커널 오브젝트의 Usage Count (UC)

- 프로세스가 핸들을 얻게 되었다는 의미 : 핸들 테이블에 대한 정보가 갱신됨

ex) CreateMailslot 호출
      ⑴ 메일 슬롯 리소스 생성
      ⑵ 커널 오브젝트 생성
      ⑶ 핸들 정보가 핸들 테이블에 갱신  // 이때부터 프로세스가 핸들 정보를 얻음
      ⑷ CreateMailslot 함수를 빠져 나오면서 핸들값 반환

- 부모 프로세스 핸들 테이블에 있는 커널 오브젝트들 UC가 1
- 자식 프로세스 생성 후 상속여부가 Y 였던 커널 오브젝트는 UC가 1 증가 => UC=2가 됨


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

- 핸들의 상속 여부는 리소스 생성되는 순간 결정 (핸들 테이블의 Y or N)

- LPSECURITY_ATTRIBUTES 구조체 = SECURITY_ATTRIBUTES의 포인터로 정의
- NULL 이 전달되면 상속이 되지 않음
   BUT 구조체 변수를 적절히 초기화해 주소값 전달하면 상속 가능

typedef struct _SECURITY_ATTRIBUTES {
   DWORD nLength;   // 구조체 변수 크기
   LPVOID lpSecurityDescriptor;   // 상속과 관계 없음
   BOOL bInheritHandle;   // 상속 여부 결정   TRUE : Y,  FALSE : N
}

ex)

SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;

HANDLE hMailSlot = CreateMailslot(   ...   , &sa);
CreateProcess(   ...   , &sa,   ...   );




II. Pseudo (가짜) 핸들과 핸들의 중복 (Duplicate)


- 프로세스의 핸들은 Pseudo (가짜) 핸들

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







2012년 8월 29일 수요일

윈도우즈 시스템 프로그래밍 [7] - 프로세스간 통신 (IPC)




윈도우즈 시스템 프로그래밍을 공부하면서 정리한 글입니다.



7장.  프로세스간 통신 (IPC) 1



I. 프로세스간 통신(IPC)의 의미



- IPC = Inter-Process Communication ( 프로세스 사이의 통신 )
- 통신 : 기본적으로 데이터를 주고 받는 행위
- 대부분 하나의 프로그램 = 하나의 프로세스 but  여러개의 프로세스를 가진 프로그램도 종종 있음



1. 프로세스 사이에서 통신이 이뤄지기 위한 조건


- 서로 통신하고자 하는 프로세스가 만날 수 있는 여건 (공유하는 메모리 영역이 존재)이 되어야 함
- 여건이 허락되지 않으면 전화 or 메신저 같은 보조 수단이 필요


2. 프로세스들이 서로 만날 수 없는 이유


- 프로세스는 서로 데이터를 주고 받을 만한 접선 장소가 없기 때문에 데이터 주고받는 것은 불가능
- 프로세스는 자신에게 할당된 메모리 공간 이외의 접근은 불가능


3. 프로세스들이 만나지 못하게 디자인한 이유 


- 안정성을 높이기 위해





II. 메일 슬롯 방식의 IPC




1. 메일 슬롯 (Mail Slot)의 원리


- 파이프와 더불어 대표적인 IPC 기법
- 메일 슬롯 : 편지를 넣을 가느다란 우체통의 입구
- 기본 원리 : 데이터를 주고받기 위해 프로세스가 우체통을 마련


2. 메일 슬롯 (Mail Slot) 구성을 위해 필요한 요소


 ① Receiver가 준비해야할 것

HANDLE CreateMailslot (
   LPCTSTR lpName,
   DWORD nMAXMessageSize,
   DWORD lReadTimeout,
   LPSECUritY_ATTRIBUTES lpSecurityAttributes
);

1) lpName : 생성하는 메일 슬롯의 이름
   - 주소의 기본 형식 : \\computer name\mailslot\[path] name

2) nMaxMessageSize : 메일 슬롯의 버퍼 크기를 지정
   - 0을 전달할 경우 시스템이 허용하는 최대 크기로 지정

3) lReadTimeout : 최대 블로킹 시간을 ms 단위로 전달
   - 메일 슬롯은 전소오딘 데이터를 읽기위해 파일 입/출력 함수 ReadFile 함수를 이용
   - 메일 슬롯이 비어 있다면 데이터가 채워질 때 까지 Blocking 상태에 놓임 
   - 0을 전달하면 데이터가 있든 없든 블로킹 상태를 빠져나와 다음 단계를 실행
   - MAILSLOT_WAIT_FOREVER을 인자로 전달하면 데이터가 존재할 때 까지 블로킹 상태에 놓임

4) lpSecurrityAttributes : 핸들의 상속 여부를 지정

5) 함수의 변환 타입 : HANDLE
   => 메일 슬롯은 커널에 의해 관리되는 리소스 이므로 커널 오브젝트가 생성되며, 그 핸들이 반환됨


② Sender가 준비해야 할 것

- 메일 슬롯 이름을 알아야 함 (&주소 ( 디렉토리))

ex)

   HANDLE hMailSlot;
   hMailSlot=CreateFile("\\\\.\\mailslot\\ ", ...);   // 파일을 생성하거나 개방할때 사용
   CHAR message[50];
   WriteFile(hMailSlot, message, ...);

- CreateFile, WriteFile : Windows에서 제공하는 파일 관련 시스템 함수 ( <-> ANSI 표준함수)
- CreateFile을 통해 메일 슬롯으로 보내기 위한 통로(리소스)를 형성
   => 데이터 전송을위한 Data Stream을 형성
- 메일 슬롯은 파일이 아니지만 파일 입/출력 함수를 사용
   -> Windows 파일 시스템을 기반으로 구현되어 있기 때문

- 주소 골격 : \\computername\mailslot\[path] name
- 주소에서 'mailslot' 부분은 바뀌지 않음
- computername에 '.'의 의미 : 로컬 컴퓨터






- Sender 프로세스는 우체통(메일 슬롯)의 주소를 알고 있어야 데이터 전송이 가능
- 메일 슬롯 = 메모리 공유 역할
- 데이터 전송이 양방향이 아닌 단방향(우체통)
- 여러개의 리시버가 같은 주소를 가질 수 있음
   => 센더가 그 주소로 전송하면 모든 메일 슬롯에 데이터 전송이 가능 (브로드 케스팅)





- CreateFile : 파일 만들거나 오픈
- WriteFile : 파일에 데이터 작성

- IPC 메일 슬롯을 이용하지 않고 파일만으로 데이터를 주고받을 수 있음
   ex)
       프로세스 A가 파일 a.txt를 만들고 프로세스 B가 a.txt를 읽음
        => BUT 안정성에 문제가 있음 ->  메일 슬롯을 이용

ex) Receiver.cpp

#define SLOT_NAME _T("\\\\.\\mailslot\\mailbox");

int _tmain(int argc, LPTSTR argv[]) {
   HANDLE hMailSlot;
   TCHAR messageBox[50];
   DWORD bytesRead;

   hMailSlot = CreateMailSlot (
      SLOT_NAME,
      0,
      MAILSLOT_WAIT_FOREVER,
      NULL
   );

   ReadFile(hMailSlot, messageBox, sizeof(TCHAR)*50, &bytesRead, NULL);

   closeHandle(hMailSlot);   // UC 1줄여 커널 오브젝트의 소멸을 도움

ex) Sender.cpp

#define SLOT_NAME _T("\\\\.\\mailslot\\mailbox");

int _tmain(int argc, LPTSRT argv[]) {
   HANDLE hMailSlot;
   TCHAR message[50];
   DWORD bytesWritten;

   hMailSlot=CreateFile (  //  Receiver과 연결을 만들기 위해
      SLOT_NAME,
      GENERIC_WRITE,  // 여기서부터
      FILE_SHARE_READ,
      NULL,
      OPEN_EXISTING,
      FILE_ATTRIBUTE_NORMAL,
      NULL   // 여기까지 거의 고정
   );

   _fgetts(message, sizeof(messsage)/sizeof(TCHAR), stdin);
   WriteFile(hMail, message, _tcslen(message)*sizeof(TCHAR), &bytesWritten, NULL);

   CloseHandle(hMailSlot);

- 메일 슬롯 주소에서 '.'는 컴퓨터 자신, '*'는 모든 컴퓨터
  => 브로드 케스팅 이용 가능




III. Signaled vs Non-Signaled => 커널 오브젝트의 상태




1. 상태에 대한 이해


- 상태(state)라는 용어를 사용하는 이유는 변하기 때문
- signaled (신호를 받는 상태)와 Non-Signaled (신호를 받지 않는 상태)
- 커널 오브젝트를 구성하는 멤버 변수 중 하나가 상태 정보를 저장함
   => TRUE : Signaled   FALSE : Non-Signaled


2. 프로세스 커널 오브젝트 상태 (state)에 대한 이해


- 프로세스가 생성될 때 프로세스 커널 오브젝트가 생성됨
   => 처음 상태 : Non-signaled
- 프로세스가 종료되면 Signaled 상태로 변경됨
- Signaled 된 커널 오브젝트는 Non-Signaled 상태로 변경되지 않음
   => 종료된 프로세스를 다시 재개하지 못하기 때문 ( 새로운 프로세스를 다시 실행해야 함)


3. 커널 오브젝트의 두가지 상태를 확인하는 용도의 함수


- 커널 오브젝트의 상태를 확인하는 대표적인 함수

ex) DWORD WaitForSingleObject (
         HANDLE hHandle,   // 확인하려는 커널 오브젝트의 핸들
         DWORD dwMilliseconds   // 커널 오브젝트가 Signaled 될 때 까지 기다릴 수 있는 최대 시간
       );

- dwMilliseconds에 IFINITE 인자 전달시 Signaled 될 때까지 기다림

- WaitForSingleObject 반환값
   1) WAIT_OBJECT_0 : 커널 오브젝트가 Signaled 되었을 때 반환값
   2) WAIT_TIMEOUT : Signaled 되지 않고 dwMillisecons가 다 되었을 때
   3) WAIT_ABANDONED : 오류 발생시



4.  커널 오브젝트의 상태 확인이 필요한 상황

      => 하나의 프로그램이 여러개의 프로세스로 구성되는 경우 (CPU 코어 수의 증가에 따라)


- 이런 상황에서 WaitForSingleObject 사용







참고 : 뇌를 자극하는 윈도우즈 시스템 프로그래밍. 윤성우 저 





2012년 8월 27일 월요일

윈도우즈 시스템 프로그래밍 [6] - 커널 오브젝트와 오브젝트 핸들




윈도우즈 시스템 프로그래밍을 공부하면서 정리한 글입니다.



6장. 커널 오브젝트와 오브젝트 핸들



I. 커널 오브젝트에 대한 이해


- 커널 : 컴퓨터를 운영하는데 있어서 중심이 되는 운영체제의 핵심 부분
   -> 일반적으로 커널과 운영체제를 같은 의미로 사용

- 커널 오브젝트 : 커널에서 관리하는 중요한 정보들을 담아둔 데이터 블록


1. 커널 오브젝트의 이해


- 프로세스의 생성과 소멸, 상태 벼화, 관리는 모두 운영체제 몫
- 여러개의 프로세스르 관리하면 고정적으로 저장하고 갱신할 정보가 존재
ex) 프로세스 상태 정보 ( Running, Blocked, Ready 상태 ), 우선순위 정보
   => 운영체제 내부에 저장되어야 할 정보, 변경될 때 마다 갱신되어야 함
   -> 프로세스 스케쥴러가 이를 바탕으로 프로세스를 관리

- 프로세스에 관련된 정보를 저장, 참조 및 변경할 수 있는 구조체 존재 
  (편의상 '프로세스 관리 구조체'라 함)

- 프로세스가 생성된 후 정보를 초기화 한 것이 커널 오브젝트


- 프로세스 관리 구조체는 어떻게 구성되어 있는지 알 수 없음 (마이크로소프트가 공개하지 않음)
   => 프로세스의 커널 오브젝트는 프로그래머가 직접 생성하거나 조작하는 것을 막음



2. 그 이외의 커널 오브젝트


- 프로세스 내에 쓰레드를 생성할 때, IPC를 위해 사용하는 파이프나 메일 슬롯을 생성할 때도
   커널 오브젝트를 생성해야 함

- 커널 오브젝트 종류에 따라 다른 구조체를 생성
ex) 파일 커널 오브젝트 -> 파일 속성 정보 (Read모드, Read / Write모드 ... )
       프로세스 커널 오브젝트 -> 프로세스 우선 순위 정보 (스케쥴링을 위해)

=> ∴ Windows 운영체제는 프로세스, 쓰레드와 파일 같은 Resource 들을 원활히 관리하기 위해
          데이터를 저장하는 메모리 블록이 존재 => 커널 오브젝트

- 데이트 블록 : 커널 오브젝트   |   프로세스나 파일 : Resource

- Windows 커널에 의해 관리되는 리소스 수 만큼 커널 오브젝트도 생성됨




II. 오브젝트 핸들(Handle)을 이용한 커널 오브젝트의 조작


- 프로그래머는 직접 커널 오브젝트를 조작할 수 없음
- 리소스의 특성을 변경하기 위해선 (프로세스 우선 순위 변경 같은)
   해당 리소스 커널 오브젝트를 조작해야함
   -> 마이크로소프트에서 제공하는 시스템 함수를 이용 (안정적인 방식)

1. 프로세스 우선순위 (Priority) 변경


ex)

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

- 핸들 (Handle) : 커널 오브젝트에 할당되는 숫자 (정수형)


2. 커널 오브젝트에 할당되는 숫자 = 핸들 (Handle)


- Windows는 커널 오브젝트를 생성할 때 마다 핸들을 하나씩 부여


3. 핸들 정보


- 핸들 정보를 얻는 방법은 커널 오브젝트 종류에 따라서 다양함 ( 프로세스, 파일, 쓰레드 ...)
- GetCurrentProces 함수 -> 현재 실행되고 있는 프로세스의 핸들을 얻을 떄 사용
  => 항상 같은 값을 리턴(-1)
- '-1'은 자기 자신을 의미
- 함수가 호출되어 실행되는 중에 (함수는 여러개의 명령어로 구성) CPU 할당시간을
  다른 프로세스에 넘겨줄 수 있음
  => 우선순위를 넘기는 기준은 CPU 관점에서 명령어 단위가 기준




III. 커널 오브젝트와 핸들의 종속관계



1. 커널 오브젝트의 종속 관계


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

① 커널 오브젝트에 종속적인 것이 아니라, 운영체제에 종속적인 관계로
     커널 오브젝트의 소멸 시점은 운영체제에 의해 결정됨
② 커널 오브젝트는 여러 프로세스에 의해 접근이 가능 (물론 함수 호출을 통한 간접 호출)

2. 핸들의 종속 관계


- 핸들 (핸들 테이블) 은 운영체제에 종속적이지 않고 프로세스에 종속적 
   ex) 프로세스의 우선 순위를 높이기 위해서는 핸들을 이용해 커널 오브젝트에 접근.

- PROCESS_INFORMATION 구조체

typedef struct _PROCESS_INFORMATION {
   HANDLE hProcess;   // 프로세스 핸들
   HANDLE hThread;   // 쓰레드 핸들
   DWORD dwProcessID;   // 프로세스의 ID
   DWORD dwThreadID;   // 쓰레드의 ID
} PROCESS_INFORMATION;


3. 프로세스 핸들과 ID


- 운영체제는 프로세스 생성시 프로세스들을 구분짓기 위해 ID(식별자)를 할당
- 프로세스 핸들 : 프로세스의 커널 오브젝트를 가리키기(구분짓기) 위함
- 프로세스 ID : 커널 오브젝트가 아니라 프로세스 자체를 구분짓기 위함
- CreateProcess 함수를 통해 프로세스를 생성하면 쓰레드라는 시스템 리소스도 생성됨




IV. 커널 오브젝트와 Usage Unit



1. CloseHandle 함수에대한 정확한 이해



CreateProcess 함수 호출이 커널 오브젝트의 생성 원인이나 단지 프로세스의 생성을 요청할 뿐
프로세스 생성의 주체는 운영체제이다. (함수가 아님)
- 프로세스 생성시, 그 프로세스르 위한 커널 오브젝트가 생성됨
- but 프로세스가 소멸한다고 해서 커널 오브젝트가 소멸하진 않음
   => 소멸할 수도 있으나 하지 않을 수도 있음

- BOOL CloseHandle (
      Handle hObject   // 핸들을 닫음(반환)  ->  핸들의 소멸, 리소스 해제를 의미하지 않음
   );



2. 커널 오브젝트와 Usage Count


- 자식 프로세스의 종료코드는 자식 프로세스 커널 오브젝트에 저장됨
- if 자식 프로세스가 종료될 때 커널 오브젝트도 소멸되면 종료코드는 사라짐
   =>  ∴ 프로세스가 종료되어도 커널 오브젝트는 동시에 소멸하지 않음

- 해당 커널 오브젝트를 참조하는 대상이 없을 때 소멸하는 것이 이상적
   => windows가 커널 오브젝트를 소멸 시키는 방법

- 프로세스의 생성과 동시에 커널 오브젝트의 Usage Count=1
- 프로세스에 접근 가능한 핸들 수가 늘어날때 마다 Usage Count 증가
- 자식프로세스의 Usage Count=2 (부모 프로세스 참조 + 자기자신 참조)
   => 부모프로세스가 CreateProcess 함수 호출과정에서  PROCESS_INFORMATION
      구조체를 통해 자식 프로세스의 핸들을 얻기 때문


- 핸들값은 커널 오브젝트에 매핑되지 않음 -> 각각의 핸들 테이블에서 핸들 값을 사용
   => ∴ 핸들 값, 핸들 테이블은 프로세스에 종속적

- 자식프로세스가 사라지면 프로세스에 종속적인 핸들 테이블이 사라지나
  부모 프로세스에서 자식 프로세스 커널 오브젝트를 보고 있으므로 (핸들 테이블에서 사용중) 
  커널 오브젝트는 사라지지 않고 단지 Usage Count를 2에서 1로 감소시킴
  => UC(Usage Count)가 존재하는 이유

- 운영체제 영역에서 커널 오브젝트가 있음 = 커널 오브젝트의 소멸은 운영체제게 UC를 통해 정함
- UC가 0이 되면 커널 오브젝트는 소멸 ( but 파일은 물리적으로 남아있음)


Q1. 프로세스 생성시 자기 자신의 커널 오브젝트를 핸들 테이블에서 참조하는가?

- 프로세스 생성시 자기 자신의 커널 오브젝트에 대한 핸들정보는 핸들 테이블에 저장하지 않음
   but GetCurrentProcess는 상수(-1)을 리턴
- 함수들은 -1이 리턴 되면 현재 프로세스를 지칭 한다고 인식함 (windows의 약속)

Q2. 자식프로세스가 종료된 후 부모 프로세스가 자식 프로세스의 커널 오브젝트를 참조한다고 해서
       커널 오브젝트를 남겨둘 이유가 있는가?

- 부모 프로세스는 자식 프로세스가 정상 종료 햇는가가 궁금함
- 자식 프로세스가 소멸되자 마자 자식 프로세스의 커널 오브젝트가 소멸되면
   부모 프로세스는 자식 프로세스의 정상 종료 여부를 알지 못함
   =>  ∴ UC를 통해 운영체제가 커널 오브젝트를 관리!


- STARTUPINFO si = {0, };   // 프로세스 생성시 초기 정보를 전달하는 목적
   si.cb= sizeof(si);

- PROCESS_INFORMATION pi; /// 생성된 프로세스의 정보를 얻기 위한 구조체 (핸들값 얻을 수있음)

- 자식 프로세스가 종료되더라도 부모 프로세스의 참조에 의해 자식 프로세스의 커널 오브젝트는 남음
   -> but 종료코드를 얻고 더이상 커널 오브젝트를 참조할 필요가 없다면 UC를 0으로 만들어 운영체제가 소멸시키도록 해야함
      ( 자원 낭비를 막기위해 )
   => CloseHandle (pi.hProcess);   // 함수를 이용해 UC를 하나 줄여 메모리 낭비 방지





참고 : 뇌를 자극하는 윈도우즈 시스템 프로그래밍. 윤성우 저 



2012년 8월 25일 토요일

윈도우즈 시스템 프로그래밍 [5] - 프로세스의 생성과 소멸



윈도우즈 시스템 프로그래밍을 공부하면서 정리한 글입니다.



5장. 프로세스의 생성과 소멸



I. 프로세스 ( Process ) 의 이해



1. 프로세스란 무엇인가?


- 프로세스 = 실행 중에 있는 프로그램


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


① execution of "C" program

- Data 영역 : 전역 변수, static 변수의 할당을 위한 공간
- Stack 영역 : 지역 변수, 함수 호출시 전달되는 인자 값의 저장을 위한 공간
- Heap 영역 : 동적 할당 (malloc, calloc) 함수를 위한 공간
- Code 영역 : 시행 파일을 구성하는 명령어들이 올라가는 메모리 영역

② register Set

- 프로그램 실행을 위해선 레지스터가 절대적으로 필요
- 레지스터의 상태도 프로세스의 일부로 포함시킬 수 있음
   => 컨텍스트 스위칭(context switching)의 이해에 중요!

 

II. 프로세스의 스케쥴링과 상태 변화


1. 프로세스의 스케쥴링


- 하나의 CPU가 여러개의 프로세스를 번갈아가며 실행
  => 고속으로 실행하므로 사용자는 동시라고 느낌
∴ 여러개의 프로세스가 CPU 할당 시간을 나누기 때문에 동시 실행처럼 보임


2. 스케쥴링의 기본 원리


- 스케쥴링 : 프로세스의 CPU 할당 순서 및 방법을 결정짓는 일
- 스케쥴러 : 스케쥴링 알고리즘을 적용해 프로세스를 관리하는 운영체제 요소 ( 모듈)


3. 멀티 프로세스는 CPU를 바쁘게 함


- A프로세스가 I/O에 관련된 일( ex. 네트워크 송수신 )을 할 경우, 운영체제는 스케쥴러를 통해
   다른 프로세스가 실행되도록 스케쥴링 함

- I/O가 프로그램 실행에서 많은 부분을 차지


4. 프로세스의 상태변화



① S(Start)에서 Ready 상태로의 전이
- S는 프로세스가 생성되었음을 의미 => 생성과 동시에 Ready 상태가 됨
- Ready 상태 = CPU에 할당 되기를 기다리는 상태
   -> 멀티 프로세스 운영체제 => 실행 중인 프로세스가 있을것

② Ready 상태에서 Running 상태로의 전이
- 스케쥴러는 Ready 상태의 프로세스 중 하나를 실행할 수 있음

③ Running 상태에서 Ready 상태로 전이
- 프로세스는 생성시 중요도에 따라 우선순위 (Priority) 가 매겨짐
   => 다시 Ready상태가 된 프로세스는 다른 프로세스(우선순위가 높은)가 종료되기를 기다려야 함

④ Running 상태에서 Blocked 상태로 전이
- 실행 중에 있는 프로세스가 멈추는 (Blocked) 상태로 들어가는 것
- 데이터 입/출력에 관련된 일을 하는 경우 발생
   => 입/출력에 시간이 많이 걸림 -> 입/출력 진행 중인 프로세스는 잠시 Blocked 상태로 돌리고
   Ready 상태의 프로세스를 대신 실행시킴

 ⑴ 프로세스 A가 파일을 입/출력 해야하는 상황을 만남
 ⑵ Blocked 상태가 되어 파일 입/출력을 진행
 ⑶ 프로세스 A가 Blocked 되는 동시에 Ready 상태의 프로세스가 Running 됨 

⑤ Blocked 상태에서 Ready 상태로의 전이
- Blocked 상태는 스케쥴러에 의해 선택될 수 없는 상태
   => Ready 상태와의 차이점. Ready 상태에선 스케쥴러에 의해 실행 가능
- 입/출력이 완료된 Blocked 프로세스는 다시 Ready 상태가 되어 스케쥴러의 선택을 기다려야 함.




5. 프로세스의 상태 변화, 시나리오 다시 이해


- Running -> Ready : 해야할 일이 있으나, 어쩔 수 없이 양보해야하는 상황
  ex) 우선순위가 높은 프로세스의 등장

  but 항상 스케쥴러의 관심 대상이 됨

- Running -> Blocked : 해야할 일을 하고, 다른 프로세스에 Running 상태를 양보
  ex) I/O 관련 작업 -> CPU 연산을 필요로 하지 않음 => 다른 프로세스의 CPU 연산을 양보

but 스케쥴러의 관심 대상이 되지 않음 -> Blocked 작업이 끝난 후 Ready로 가서 선택을 기다림





III. 컨텍스트 스위칭 (Context Switching)


- 실행중인 프로세스의 벼녕은 시스템에 많은 부하를 줌
- CPU 내에 레지스터는 현재 실행 중인 프로세스 데이터로 채워짐
   =>  프로세스가 바뀌면 레지스터 값도 백업 후 변경되어야 함.
- Running -> Ready 될 때 레지스터 값은 메모리에 저장해 두어야 함 (Back Up)

- Running 프로세스와 Ready 프로세스가 바뀔 떄 => 컨텍스트 스위칭 발생
  -> 실행되는 프로세스의 변경과정에서의 컨텍스트 스위칭은 시스템에 부담을 줌
  => 레지스터가 많을수록, 프로세스별 관리 데이터가 많을 수록 더 함 -> 멀티 프로세스의 단점.



IV. 프로세스의 생성


1. 프로세스의 생성


방법 1 : 실행 파일을 더블 클릭해 독립적인 프로세스를 생성
방법 2 : 프로그램 실행 중에 또 하나의 프로세스를 생성


2. CreateProcess 함수의 이해


- Windows에서 프로세스 생성을 위해 제공되는 함수
- CreateProcess 함수를 호출하는 프로세스 -> 부모 프로세스 (Parent Process)
- 호출 당하는 프로세스 -> 자식 프로세스 (Child Process)

BOOL CreateProcess (
   LPCTSTR lpApplicationName,   // 
   LPTCSTR lpCommandLine,   // 
   LPSECURITY_ATTRIBUTES lpProcessAttributes,
   LPSECURITY_ATTRIBUTES lpThreadAttributes,
   BOOL bInheritHandles,
   DWORD dwCreationFlags,
   LPVOID lpEnvironment,
   LPCTSTR lpCurrentDirectory,
   LPSTARTUPINFO lpStartupInfo,   // 
   LPPROCESS_INFORMATION lpProcessInformation   // 
)

① lpApplicationName : 생성할 프로세스의 실행파일 이름
- 경로명을 지정할 수 있음
- 경로를 지정하지 않으면 프로그램의 현재 디렉토리에서 찾게 됨

② lpCommandLine : 
- argc, argv와 같이 새로 생성하는 프로세스에 인자를 전달할 때
- lpApplicationName이 NULL일 경우 표준 검색경로에 따라 실행할 파일을 찾음

③ lpStartupInfo :
- STARTUPINFO 구조체 변수들을 초기화 한 후, 이 변수의 포인터를 전달함

④ lpProcessInformation
- 생성하는 프로세스의 정보를 얻기 위해 사용되는 인자
- PROCESS_INFORMATION 구조체 변수의 주소값을 인자로 전달






참고 : 뇌를 자극하는 윈도우즈 시스템 프로그래밍. 윤성우 저 


2012년 8월 24일 금요일

윈도우즈 시스템 프로그래밍 [4] - 컴퓨터 구조 2



윈도우즈 시스템 프로그래밍을 공부하면서 정리한 글입니다.



4장. 컴퓨터 구조 2



I. 컴퓨터 구조의 접근 방법



1. 컴퓨터를 디자인 하자


- CPU의 기본 요소 : ALU, Control Unit, Registers
- 이중 register 만을 디자인 대상으로 삼음
- 시스템 프로그래머(어셈블리 프로그래머)가 CPU를 볼 때 register에 초점을 둠


2. 레지스터를 디자인하자


① 몇 비트로 구성?    16비트
② 몇 개의 레지스터로 구성?    8개 (r0 ~ r7)
③ 레지스터 각각의 용도?









- ir : instruction register

- sp : stack pointer

- lr : link register 

- pc : program counter




3. 명령어 구조를 디자인 하자


- CPU 구성 형태 ( 레지스터 구성 형태 )에 따라 명령어 구조가 달라짐

∴ 레지스터를 디자인 한 후 명령어를 디자인 함.

- 어셈블리어로 구현된 프로그램은 구조가 다른 CPU로 인식이 불가능
- 16비트를 최대한 효율적으로 활용할 수 있도록 구성
   2^16 = 65536개
   C / C++ 연산자 = 100개를 넘지 않음

ex) 레지스터 r1에 있는 값과 7을 더해 r2에 저장하라

=> 총 4개의 정보 필요 : "덧셈", "레지스터 r1", "레지스터 r2", "숫자 7"


- 연산자 :

덧셈  :  ADD,  001
뺄셈  :  SUB,  010
곱셈  :  MUL,  011
나눗셈  :   DIV,  100

- 저장소 : 레지스터가 8개 이므로 3비트 할당

- 피연산자 1, 2 : 숫자가 들어갈 수 있도록 디자인

- 피연산자 부분 -> 저장된 데이터가 숫자인지 레지스터인지 구분이 어려움
  => ∴ 네개의 비트 중 첫번째 비트가 1이면 레지스터 정보를 나타냄

ex) 1001 : r1,  0001 : 숫자 1

but 숫자를 8개 밖에 표현하지 못하는 제약 사항이 있음


- 명령어는 ir (instruction resiter )에 저장됨
   => 다음번 실행될 명령어를 미리 가져다 놓는 용도

- 구성하는 명령어의 형태에 따라 Control Unit ( 명령어를 해석하는 역할 ) 의 구조가 결정됨

- 여러가지 제약 사항들은 CPU의 종합적 성능이 고려되는 가운데 등장하게 됨
   ex) 첫번째 피연산자 위치에는 레지스터 이름만 와야함


① CISC 구조 (Complex Istruction Set Computer)

-  복잡한 명령어 체계를 가지는 컴퓨터
- 다양한 명령어를 제공 -> 수십줄에 걸쳐 구현해야할 명령어를 단 한줄로 구성할 수 있음
- 메모리를 효율적으로 사용할 수 있음
   but CPU구조가 복잡해져야 함 => 성능 향상에 제한


② RISC 구조 (Reduced Instruction Set Computer)

- CISC 구조에서 10%의 명령어만 주로 사용됨에 착안
- 명령어 수를 줄이고 길이를 일정하게 디자인
- 근래 CPU, 임베디드 환경에서 사용되는 대부분의 CPU
   => 클럭당 처리하는 명령어 수를 늘여줌 ( Pipelining ) => 성능 향상 ( 물론 초당 클럭수도 중요 )




II. LOAD & STORE 명령어 디자인



1. LOAD & STORE 명령어의 필요성


- 지금까진 " 사칙 연산 결과를 레지스터에만 저장할 수 있다 "는 제약을 둠
   => ∴ 피연산자에 메모리 주소를 사용할 수 없음


2. LOAD & STORE 명령어의 디자인




- 이전에 디자인 한 사칙 연산에 비해 피연산자가 두개(메인 메모리, 레지스터) 만 필요
- LOAD -> 110

ex) Load r3, 0x07
|  00  |  110  |  011  |  00000111  |

- STORE -> 111   // destination 과 source가 반대


ex) "c = a + b"

LOAD r1, 0x10
LOAD r2, 0x20
ADD r3, r1, r2
STORE r3, 0x30






III. Direct 모드와 Indirect 모드



1. Direct 모드의 문제점과 Indirect 모드의 제안


- 하나의 명령어에 여러 정보를 담음 -> 데이터 크기의 제한

ex) destination에는 레지스터가 8개나 되므로 문제가 없으나
source 부분에는 0x0000 ~ 0x00ff까지만 표현할 수 있음

- Direct 모드 : 할당된 비트 수 만큼의 메모리 영역만 접근 가능  =>  주소값을 명령어에 '직접' 표현




2. Indirect 모드의 이해


- 명령어에서 지정하는 번지에 저장된 값을 주소값으로 참조하게 됨




3. Indirect 모드의 활용 예제


ex)

   int a = 10;   // 0x0010번지에 할당
   int b = 20;   // 0x0100번지에 할당 => Direct 모드로는 불가능
   int c = 0;      // 0x0020번지에 할당
   c = a + b;


=====================

LOAD r1, 0x0010
MUL r0, 4, 4
MUL r2, 4, 4
MUL r3, r0, r2

STORE r3, 0x0030
(LOAD r2, r3는 사용 불가능 => source 부분에는 주소 정보만 사용가능 하기 때문)

LOAD r2, [0x0030]    // 0x0030에는 0x0100이 저장되어 있음

ADD r3, r1, r2




참고 : 뇌를 자극하는 윈도우즈 시스템 프로그래밍. 윤성우 저