IT공부/IT서적

[뇌를 자극하는 윈도우즈 시스템 프로그래밍] 5장. 프로세스의 생성과 소멸

shine94 2024. 12. 24. 13:41

* 해당 글은 윤성우의 뇌를 자극하는 윈도우즈 시스템 프로그래밍 도서를 읽고 정리한 글입니다

   https://product.kyobobook.co.kr/detail/S000001223395

 

뇌를 자극하는 윈도우즈 시스템 프로그래밍 | 윤성우 - 교보문고

뇌를 자극하는 윈도우즈 시스템 프로그래밍 |

product.kyobobook.co.kr

 

 

 

 

 

* 우리가 사용하는 Windows는

   멀티 프로세스(Multi-Process) 운영체제, 프로세스라는 것이 여러개 존재할 수 있는 운영체제이다

 

* 프로세스

   실행 중인 프로그램

 

* 실행파일(.exe, Executable File)

   단순히 데이터만 담고 있는 파일과 달리,

   컴퓨터가 읽고 실행할 수 있는 명령어가 담긴 파일로, 컴퓨터에게 특정 작업을 지시하고 실행하도록 만드는 파일

   ㄴ 실행과정

         운영체제의 프로그램 로더가 실행파일을 읽고 메모리에 적재

         CPU는 메모리에 적재된 명령어를 읽고 실행하여 프로그램을 동작시킴

   ㄴ 특징

        ① CPU가 이해할 수 있는 기계어로 구성

            실행파일은 컴파일된 이진 코드로 작성된다

        ② 운영체제와의 종속성

            실행파일은 특정 운영체제에서만 동작한다

             - Windows: .exe

             - Linux/Unix: ELF 파일

             - macOS: Mach-O 파일

        ③ 실행 가능한 코드와 데이터 포함

            명령어(코드), 데이터(변수, 상수), 리소스(아이콘, 문자열 등) 포함

 

* 프로세스를 구성하는 요소

 : Code 영역, Data영역, Heap 영역, Stack 영역

   ① 코드(Code) 영역

      프로세스가 실행할 코드와 매크로 상수가 기계어 형태로 저장된 공간

      CPU는 코드 영역에 저장된 명령어를 하나씩 가져와서 실행

      컴파일 타임에 결정되고, 중간에 코드를 바꿀 수 없어 Read-Only로 지정

      → 이 영역을 세분화하면 code와 rodata로 나눠짐

      1) code - 오직 읽기 전용

          우리가 작성한 코드가 컴파일된 후 기계어로 번역되어 저장되어 있는 곳

          EIP 레지스터를 통해 읽어서 코드가 실행됨

          프로그램이 끝날 때까지 메모리에 남아 있음

      2) rodata - 오직 읽기 전용

          읽기 전용인 데이터가 들어 있음

          ㄴ 주로 상수, 상수형 문자열, printf의 중괄호 부분이 들어감

   ② 데이터(Data) 영역

      코드에서 선언한 전역변수와 정적변수가(static)가 저장되는 영역

      ㄴ 전역변수(단, 다른 파일에서 접근하려면 extern 선언 필요) - 프로그램 전체에서 공유

      ㄴ static 변수 - 특정 함수에서 값을 유지

      ㄴ 전역 static 변수 - 특정 파일 내에서만 사용 가능한 값을 유지

      프로그램 시작과 함께 할당되며, 프로그램이 종료되면 소멸

      실행 도중에 값이 변경될 수 없게 Read-Write로 지정

      → 이 영역을 세분화하면 BSS와 Data로 나눠짐

      1) BSS

          초기화 값이 없는 값

      2) data

          초기화 값이 있는 값

   ③ 스택(Stack) 영역

      함수의 호출과 관계되는 지역변수와 매개변수가 저장되는 영역

      함수의 호출과 함께 할당되며, 함수의 호출이 완료되며 소멸

      이렇게 스택 영역에 저장되는 함수의 호출 정보를 스택 프레임(Stack Frame)이라고 함

   ④힙(Heap)영역

      사용자가 직접 메모리 관리하는 곳

      사용자에 의해 메모리 공간이 동적으로 할당되고 해제

 

* Register Set

   CPU 내에 존재하는 레지스터들은 현재 실행 중인 프로그램을 위한 데이터로 채워진다

   따라서, 레지스터들의 상태까지도 프로세스의 일부로 포함시켜 말할 수 있다

 

* 프로세스의 스케줄링(Scheduling)

   하나의 CPU가 여러 개의 프로세스를 번갈아 가면서 실행하는데

   이 때, 프로세스의 CPU 할당 순서 및 방법을 결정짓는 일을 가리켜 스케줄링이라고 한다

 

* 프로세스의 상태 변화

   프로세스가 생성되어 소멸되기까지 겪는 상태의 변화

   상태의 종류 : Start, Ready, Running, Blocked, end

   - Start → Ready

      프로세스 생성과 동시에 Ready 상태로 들어간다

      Reay 상태에 있는 프로세스는 CPU에 의해 실행되기를 희망한다

   - Ready → Running

      Ready 상태에 있는 프로세스 중 스케줄러에 의해 선택된 프로세스는 Running 상태가 되어 실행됨

   - Running → Ready

      우선 순위 낮아짐, 자원을 사용할 시간이 초과됨, 운영체제의 스케줄링 정책에 의해

      현재 사용 중인 자원을 반납하고 다시 Ready 상태로 전환

   - Running → Blocked

      데이터 입출력에 관련된 일을 하는 경우 실행 중인 프로세스가 실행을 멈추는 상태로 전환

   - Blocked → Ready

      입출력 작업이 완료된 프로세스는 다시 CPU를 사용하기 위해 Ready 상태로 전환

 

* 컨텍스트 스위칭(Context Switching)

   자원을 반납하는 프로세스 A 관련 레지스터 정보는 메모리에 저장되고, 

   자원을 획득하는 프로세스 B 관련 레지스터 정보는 CPU의 레지스터에 복원시킨다

 

* 실행되는 프로세스의 변경과정에서 발생하는 컨텍스트 스위칭은 시스템에 많은 부담을 준다

 

* 프로세스의 생성

   CreateProcess 함수를 호출하는 프로세스를 가리켜 부모 프로세스(Parent Process),

   CreateProcess 함수 호출에 의해 생성된 프로세스를 가리켜 자식 프로세스(Child Process)라 한다

BOOL CreateProcessA(
  [in, optional]      LPCSTR                lpApplicationName,
  [in, out, optional] LPSTR                 lpCommandLine,
  [in, optional]      LPSECURITY_ATTRIBUTES lpProcessAttributes,
  [in, optional]      LPSECURITY_ATTRIBUTES lpThreadAttributes,
  [in]                BOOL                  bInheritHandles,
  [in]                DWORD                 dwCreationFlags,
  [in, optional]      LPVOID                lpEnvironment,
  [in, optional]      LPCSTR                lpCurrentDirectory,
  [in]                LPSTARTUPINFOA        lpStartupInfo,
  [out]               LPPROCESS_INFORMATION lpProcessInformation
);

 

lpApplicationName

 : 생성할 프로세스의 실행파일 이름을 인자로 전달
lpCommandLine

 : 실행할 명령어줄

   (예) main 함수에 인자 전달
lpProcessAttributes

 : 프로세스의 보안 속성을 지정할 때 사용하는 인자
lpThreadAttributes

 : 스레드의 보안 속성을 지정할 때 사용하는 인자
bInheritHandles

 : 전달인자가 TRUE인 경우,

   생성되는 자식 프로세스는 부모 프로세스가 소유하는 핸들 중 상속 가능한 핸들을 상속한다

   전달인자가 FALSE인 경우, 핸들이 상속되지 않는다
dwCreationFlags

 : 생성하는 프로세스의 특성을 결정지을 때 사용되는 옵션

   특별히 설정할 필요가 없을 경우 0을 전달
lpEnvironment

 : 프로세스마다 Environment Block(환경 블록)이라는 메모리 블록을 관리한다

   이 블록을 통해서 프로세스가 실행에 필요로 하는 문자열을 저장할 수 있다
lpCurrentDirectory

 : 생성하는 프로세스의 현재 디렉터리를 설정하는 인자
lpStartupInfo

 : STARTUPINFO 또는 STARTUPINFOEX 구조체 변수를 초기화한 다음에 이 변수의 인자를 포인터를 인자로 전달
lpProcessInformation

 : 생성하는 프로세스 정보를 얻기 위해 사용되는 인자

   PROCESS_INFORMATION 구조체 변수의 주소값을 인자로 전달

 

   https://learn.microsoft.com/ko-kr/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa

 

CreateProcessA 함수(processthreadsapi.h) - Win32 apps

새 프로세스와 해당 기본 스레드를 만듭니다. 새 프로세스는 호출 프로세스의 보안 컨텍스트에서 실행됩니다. (ANSI)

learn.microsoft.com

 

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

int _tmain(int argc, TCHAR* argv[])
{
	DWORD val1, val2;
	val1 = _ttoi(argv[1]);
	val2 = _ttoi(argv[2]);

	_tprintf(_T("%d + %d = %d\n"), val1, val2, val1 + val2);

	_gettchar();

	return 0;
}

 

1. STARTUPINFO 구조체 변수의 생성 및 초기화

2. 현재 디렉터리의 설정

3. CreateProcess 함수의 호출

   첫 번째. 전달인자를 통해서 실행파일 이름을 전달할 경우,

      현재 디렉터리 기준으로 실행파일을 찾는다

   두 번째. 전달인자를 통해서 실행파일 이름을 전달한 경우,

      표준 검색 경로 순서대로 실행파일을 찾는다

      [표준 검색 경로 순서]

       - 실행 중인 프로세스의 실행파일이 존재하는 디렉터리

       - 실행 중인 프로세스의 현재 디렉터리

       - Windows의 시스템 디렉터리

       - 환경변수 PATH에 의해 지정되어 있는 디렉터리

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

#define DIR_LEN		MAX_PATH + 1

int _tmain(int argc, TCHAR* argv[])
{
	STARTUPINFO si = { 0, };
	PROCESS_INFORMATION pi;

	si.dwFillAttribute = STARTF_USEPOSITION | STARTF_USESIZE;
	si.dwX = 100;
	si.dwY = 200;
	si.dwXSize = 300;
	si.dwYSize = 200;

	LPCWSTR title = _T("I am a boy!");
	si.lpTitle = new TCHAR[_tcslen(title) + 1];
	_tcscpy_s(si.lpTitle, _tcslen(title) + 1, title);

	TCHAR command[] = _T("AdderProcess.exe 10 20");
	TCHAR cDir[DIR_LEN];
	BOOL state;

	GetCurrentDirectory(DIR_LEN, cDir);		// 현재 디렉터리 확인
	_fputts(cDir, stdout);
	_fputts(_T("\n"), stdout);

	SetCurrentDirectory(_T("exe"));			// 상대 경로로 설정함
							// AdderProcess.exe는 만든 exe 폴더 안에 있음

	GetCurrentDirectory(DIR_LEN, cDir);		// 현재 디렉터리 확인
	_fputts(cDir, stdout);
	_fputts(_T("\n"), stdout);
	
	state = CreateProcess(				// 프로세스 생성
		NULL,					// 실행파일의 이름
		command,				// main 함수에 전달될 문자열
		NULL, NULL, TRUE, 
		CREATE_NEW_CONSOLE, 
		NULL, NULL, 
		&si, &pi);

	if (state != 0)
		_fputts(_T("Creation OK!\n"), stdout);
	else
		_fputts(_T("Creation Error!\n"), stdout);

	return 0;
}