https://www.hanbit.co.kr/store/books/look.php?p_code=B2974835990
제프리 리처의 Windows via C/C++(복간판)
이 책은 윈도우 XP, 윈도우 비스타, 윈도우 서버 2008까지 내용을 포괄한다. 이미 윈도우 10이 출시된 지 오래지만 윈도우의 기본 구조는 변하지 않아 아직까지도 이 책은 윈도우 시스템 프로그래
www.hanbit.co.kr
해당 책을 읽고 학습 목적으로 간단하게 정리한 글입니다.
Chapter 04: 프로세스
개요
이번 챕터에서는 시스템이 수행 중인 애플리케이션(프로세스)을 관리하는 방법에 대해 알아본다.
프로세스는 두 개의 컴포넌트로 구성된다.
1. 프로세스 컨텍스트(실행 상태): 프로세스를 관리하기 위한 목적으로 OS가 사용하는 커널 오브젝트
2. 실행 모듈, DLL의 코드와 데이터를 수용하는 공간
프로세스가 수행하기 위해서는 프로세스 컨텍스트 내의 스레드가 있어야 한다.
하나의 프로세스는 여러개의 스레드를 가질 수 있으며 프로세스 주소 공간 내에서 동시에 코드를 수행한다.
이를 위해 스레드는 독립적인 Register Set과 Stack을 가진다.
프로세스가 생성되며 자동으로 먼저 생성되는 스레드를 Primary(Main) Thread라고 한다.
OS는 라운드로빈 방식으로 여러 Thread를 실행한다.
스케줄링 알고리즘이 존재하므로 CPU에 따라 코드를 작성할 필요는 없지만, 알고리즘을 멀티코어 환경에 맞게 설계하면 성능 향상을 기대해볼 수 있다.
윈도우 애플리케이션
윈도우는 GUI, CLI 기반의 애플리케이션을 지원한다.
일반적으로 GUI는 그래픽, CLI는 콘솔 기반 이지만 GUI에서도 콘솔 사용이 가능하다.
GUI 프로그램인 WinMain, CLI 프로그램은 main함수를 진입점으로 사용한다.
서브(하위) 시스템 링커 스위치와 진입점 함수의 적절한 매핑이 필요하다.

C/C++런타임 시작 함수가 수행하는 작업
1. 명령행 인자 포인터 획득 : 이를 파싱해 argv[], argc를 만든다.
2. 환경 변수 포인터 획득: PATH, TEMP등을 획득하며 getenv()등으로 확인 가능하다.
3. 전역 변수 초기화
4. 힙 초기화
5. 전역, static c++클래스 객체 생성자 호출
이 과정 후 main을 실행한다.
Process Instance Handle
모든 실행, DLL파일(모듈)은 로드될 때 고유한 인스턴스 핸들을 할당받는다.
인스턴스 핸들은 로드된 모듈의 시작 주소와 같다.
로드되는 주소는 링커에 의해 결정된다.
따라서 GetMoudleHandle은 해당 모듈의 시작주소를 반환하는 것과 같다.
https://learn.microsoft.com/ko-kr/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlea
GetModuleHandleA 함수(libloaderapi.h) - Win32 apps
지정된 모듈에 대한 모듈 핸들을 검색합니다. 호출 프로세스에서 모듈을 로드해야 합니다. (ANSI)
learn.microsoft.com
해당 함수는 자신을 호출한 프로세스의 주소 공간 만을 확인한다.
해당 함수의 인자에 NULL을 넣는다면 현재 실행중인 실행 파일의 핸들을 return한다. 따라서 DLL내에서 GetModuleHandle(NULL)을 호출한다면 DLL 파일이 아닌 실행 파일의 시작 주소(메인 실행 파일)를 return한다.
프로세스의 명령행
GetCommandLine 함수를 사용하면 명령행 시작점에 대한 포인터를 얻을 수 있다.
https://learn.microsoft.com/ko-kr/windows/win32/api/processenv/nf-processenv-getcommandlinew
GetCommandLineW 함수(processenv.h) - Win32 apps
현재 프로세스의 명령줄 문자열을 검색합니다. (유니코드)
learn.microsoft.com
해당 함수는 항상 동일한 지점을 반환하기 때문에 해당 명령행 버퍼를 수정하는 것은 바람직하지 않다.
또는 명령행을 얻기 위해서 진입점 함수의 argv를 사용할 수 있다.
argv를 사용하면, 명령행의 인자를 토큰 단위로 접근할 수 있다.
프로세스의 환경변수
모든 프로세스는 환경블록을 가진다.
환경블록은 환경변수(environment variable)의 집합이다.
이를 얻기 위해서는
1. GetEnvironmentStrings 함수 호출
2. TCHAR* env[] 사용 (CUI 한정)
을 사용할 수 있다.
다만 GetEnvironmentStrings 사용 시에는 내부적으로 할당한 메모리 해제를 위해 FreeEnvironmentStrings 함수 호출이 필수적이다.

프로세스는 기본적으로 위의 설정창에서 제어 가능한 시스템 환경변수, 사용자 환경변수를 가진다.
자식 프로세스는 부모 프로세스의 환경변수들을 기본적으로 상속받지만 부모 프로세스에서 종류를 정할 수도 있다.
- 개별 환경변수를 얻기 위한 방법으로 GetEnvironmentVariable 함수를 사용할 수도 있다.
- 일반적으로 환경변수에는 대체 가능한 문자열을 많이 사용하는데 개발자가 이를 활용하기 위해 ExpandEnvironmentStrings 함수를 사용할 수 있다.
- SetEnvironmentVariable을 통해 환경변수를 설정할 수 있다.
프로세스의 선호도: 프로세스의 스레드가 특정 CPU에서만 수행되게 하기 위해 선호도를 지정할 수 있다.
프로세스의 에러 모드: 심각한 에러 처리 방법 = 에러 모드를 SetErrorMode 함수를 통해서 설정할 수 있다.
프로세스의 현재 드라이브, 디렉터리: 전체 경로가 전달되지 않으면 기본 디렉터리를 통한 상대경로로 파일을 찾는다.
시스템 버전: GetVersionEx을 통해 Window의 버전을 확인할 수 있다. GetVersionEx는 OSVERSIONINFOEX 구조체를 사용한다.
CreateProcess 함수
CreateProcess에 대해 자세히 알아보자.
CreateProcessA 함수(processthreadsapi.h) - Win32 apps
새 프로세스와 해당 기본 스레드를 만듭니다. 새 프로세스는 호출 프로세스의 보안 컨텍스트에서 실행됩니다. (ANSI)
learn.microsoft.com
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
);
CreateProcess 호출 시 UsageCount가 1인 프로세스 커널 오브젝트가 생성된다.
그리고 주 스레드 커널 오브젝트도 생성된다.
pszApplicationName, pszCommandLine
실행 파일명, 명령행 문자열
pszCommandLine에 넘어가는 문자열 인자는 내부적으로 수정을 가한 뒤 복원하는 형태이기 때문에 해당 인자는 수정가능한 형태여야 한다. Access Violation이 일어나지 않도록 주의가 필요하다.
pszCommandLine을 사용하면, 프로세스 생성에 대한 추가 정보 전달이 가능하다.
일반적으로는 pszApplilcationName을 NULL로 지정한 뒤 pszCommandLine을 활용해 파일명을 지정한다.
psaProcess(lpProcessAttributes), psaThread, bInheritHandles
프로세스에 sa 구조체를 통해 보안을 설정할 수 있다.
psaProcess, psaThread는 생성되는 프로세스, 주 스레드에 대한 특성을 지정하는 것이며, bInheritHandles는 부모 프로세스의 상속가능한 핸들을 상속할지 여부를 결정하는 인자이다.
fdwCreate(dwCreationFlags): 새로운 프로세스 생성 방법을 지정한다. 여러개의 flag를 |을 통해 결합 가능하다.
자세한 것은 문서 참조
pvEnvironment(lpEnvironment): 환경블록 포인터를 지정한다. NULL로 한다면 부모 프로세스의 환경블록을 상속한다.
pszCurDir(lpCurrentDirectory): 디렉토리 설정방법, NULL이라면 부모 프로세스 애플리케이션의 디렉토리와 같다.
psiStartInfo(lpStartupInfo): STARTUPINFO 혹은 EX 구조체를 사용한다. 초기화를 하지 않는 경우 쓰레기 값이 넘어가기 때문에 0으로 초기화하는 것은 필수적이다. dwCreationFlags인자를 통해 어떤 특성들을 사용할지 지정할 수 있다.
프로세스 종료
1. main thread의 return
2. process 내의 thread가 ExitProcess 호출
3. 다른 process가 TerminateProcess 호출
4. process 내의 thread가 각자 종료됨
위와 같은 4가지 방법으로 종료될 수 있다.
기본적으로는 1번 방법 외에는 사용하지 않는다.
C++오브젝트, 할당된 메모리 공간이 적절히 해제되고, Process 커널 오브젝트 Usage Count가 적절히 감소되기 때문이다.
ExitProcess를 사용하면 그 즉시 프로세스가 멈춘다. OS관련 리소스는 정리해주지만 런타임 리소스는 정리되지 않는다.
TerminateProcess는 외부에서 호출이 가능하지 때문에 종료되는 프로세스는 실행 흐름에 관계 없이 그 즉시 정리된다.
차일드 프로세스
프로세스가 자신의 서브루틴을 위해서는 보통은 스레드를 만들기는 하지만 자식 프로세스를 만들어 해결할 수도 있을 것이다.
파이프 메일슬롯등 IPC가 가능하지만 대부분은 자식 프로세스를 만들더라도 아예 독립적인 역할을 하는 프로세를 만들 것이다.
부모로부터 자식을 완전히 독립시키기 위해 이러한 경우에는 자식 프로세스를 생성하는 동시에 해당 프로세스에대한 핸들 + 주 스레드 핸들에 대한 closeHandle을 호출한다.
관리자가 표준 사용자로 수행되는 경우
윈도우는 UAC(User Account Control)을 통해 유저의 권한을 다룬다.
일반적으로 사용자는 관리자 정도 높은 권한 사용자로 로그온을 한다.
이 때 기본적으로 표준 사용자 권한 토큰을 지급한 뒤 관리자 권한이 필요한 경우 관리자 권한으로 실행 기능을 이용하면 권한 상승된 토큰을 발급한다.
따라서 프로세스 실행 뒤에는 권한을 바꿀 수 없으며 필요한 경우 프로세스를 새로 시작하며 발급받아야 한다.
'C, C++ > WINDOWS VIA C,C++' 카테고리의 다른 글
| [WINDOWS VIA C/C++] 07. 스레드 스케줄링, 우선순위, 선호도 (3) | 2025.08.27 |
|---|---|
| [WINDOWS VIA C/C++] 05.잡 (0) | 2025.08.21 |
| [WINDOWS VIA C/C++] 03.커널 오브젝트 (2) | 2025.08.14 |
| [WINDOWS VIA C/C++] 02.문자와 문자열로 작업하기 (2) | 2025.08.14 |
| [WINDOWS VIA C/C++] 01.에러 핸들링 (1) | 2025.08.14 |