일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- ColorGradingLutPass
- Windows Build
- Rim Light
- Specular
- AppSW
- 프로그래밍 기초
- Three(Two) Tone Shading
- 벡터
- 메모리 누수
- URP로 변경
- 게임 수학
- OculusMotionVectorPass
- Virtual Byte
- Cartoon Rendering
- 개인 바이트
- URP
- ASW(Application SpaceWarp)
- 3d
- Cell Look
- 작업 집합
- C언어
- Private Bytes
- 가상 바이트
- Toon Shader
- VR
- Cell Shader
- working set
- Today
- Total
WinCNT
커널 오브젝트와 프로세스 본문
커널 오브젝트
커널 오브젝트란 커널에 의해 할당된 간단한 메모리 블록!!
커널이 소유하며, 커널에 의해서만 접근 가능(사용 카운트로 관리함)
API(여기서는 Windows가 제공)로 커널 오브젝트를 생성하면 커널 오브젝트의 핸들 값을 반환
핸들 값은 프로세스별로 독립적으로 유지
운영 프로세스는 하나이며, 하드웨어에 접근할 수 있는 것은 오직 운영 프로세스!!
사용자 프로세스는 하드웨어에 접근할 수 없다
프로세스란 실행 코드, 데이터의 조합, 즉 메모리가 필요하다
커널도 프로세스이므로 메모리가 필요하다
프로세스와 커널 오브젝트
프로세스의 커널 오브젝트: 프로세스가 소유한 커널 오브젝트(의 핸들 값)
프로세스 간의 커널 오브젝트: 프로세스 간에 공통적으로 접근이 가능한 커널 오브젝트(의 핸들 값)
프로세스 커널 오브젝트: 프로세스 자체의 커널 오브젝트(의 핸들 값)
프로세스의 커널 오브젝트
프로세스가 초기화되면 운영체제는 프로세스를 위해
커널 오브젝트 핸들 테이블을 생성한다
이는 커널 오브젝트의 메모리 블록을 가리키는 포인터(주소)를 갖고 있으며
프로세스에서 커널 오브젝트를 생성하면 테이블이 채워진다(CreateMutext 등)
만약 CloseHandle() 등의 함수를 호출하여
명시적으로 커널 오브젝트를 더 이상 사용 하지 않는다고 알려주면
Usage count를 감소시키고 커널 테이블의 값도 다시 비워준다
프로세스간의 커널 오브젝트
프로세스 간에 커널 오브젝트를 공유하는 방법으론 크게 3가지가 존재한다
- 부모 프로세스에서 자식 프로세스에 상속
- 명명된 커널 오브젝트 사용
- 핸들 복사 DuplicateHandle 함수
#include <windows.h>
DWORD CALLBACK ThreadProc(PVOID pvParam);
int main()
{
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
HANDLE hMutexDup, hThread;
DWORD dwThreadId;
DuplicateHandle(GetCurrentProcess(),
hMutex,
GetCurrentProcess(),
&hMutexDup,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
hThread = CreateThread(NULL, 0, ThreadProc,
(LPVOID) hMutexDup, 0, &dwThreadId);
// Perform work here, closing the handle when finished with the
// mutex. If the reference count is zero, the object is destroyed.
CloseHandle(hMutex);
// Wait for the worker thread to terminate and clean up.
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
return 0;
}
DWORD CALLBACK ThreadProc(PVOID pvParam)
{
HANDLE hMutex = (HANDLE)pvParam;
// Perform work here, closing the handle when finished with the
// mutex. If the reference count is zero, the object is destroyed.
CloseHandle(hMutex);
return 0;
}
핸들 값들은 프로세스 별로 독립적으로 유지
즉, 같은 커널 오브젝트에 대해서라도 커널이 커널 오브젝트를 관리하는 핸들 값과
프로세스가 커널 오브젝트에 접근하기 위한 핸들 값은 다르다!!
프로세스 커널 오브젝트
프로세스는 일반적으로 수행 중인 프로그램의 인스턴스라고 정의하며, 두 개의 컴포넌트로 구성된다
- 프로세스를 관리하기 위한 목적으로 운영 체제가 사용하는 프로세스 커널 오브젝트
- 실행 모듈이나 DLL의 코드와 데이터를 수용하는 주소 공간(스레드 스택, 힙 할당 포함)
프로세스의 컨텍스트 내에서 수행되는 스레드가 반드시 하나 이상 존재한다
프로세스가 생성되면 시스템은 자동적으로 첫 번째 스레드 생성된다
프로세스는 스레드의 집합이며, 스레드를 하나의 컨텍스트로 묶은 것이 프로세스라고 할 수 있다
스레드는 프로세스의 같은 주소 공간을 공유하므로 스레드 간의 통신은 용이하다
스레드들은 프로세스 주소 공간 내에서 동시에(!) 코드를 수행한다
그러기 위해서 스레드들은 자신만의 CPU 레지스터 집합과 스택을 가져야 한다
만일 프로세스의 주소 공간에 코드를 수행할 스레드가 없다면
운영체제는 자동적으로 프로세스와 프로세스 주소 공간을 파괴한다
(프로세스는 자력으로 수행될 수 없기 때문!)
스레드에 대한 스케줄링은 운영체제가 알아서 해주지만
멀티 CPU 머신의 장점을 최대한으로 살리기 위해 알고리즘을 변경할 수도 있다
윈도우 어플리케이션의 두 형태
/SUBSYSTEM:CONSOLE
/SUBSYSTEM:WINDOWS
선택한 서브 시스템에 따라 링커가 선택하는 진입점 기호 또는 진입점 함수(main, WinMain 등)가 결정된다
프로세스를 구동하는 것 자체는 커널 영역의 코드인 *Startup에서 해준다
프로세스의 생성과 소멸
프로세스의 생성(p.137)
CreateProcess함수 사용
스레드가 CreateProcess함수를 호출하면 OS는 사용 카운트가 1인 프로세스 커널 오브젝트를 생성한다
참고로 프로세스 커널 오브젝트 ≠ 프로세스 자체
프로세스 커널 오브젝트는 OS가 프로세스를 관리하기 위한 목적으로 생성한 데이터 구조체이다
커널 오브젝트가 생성되면, OS는 새로운 프로세스를 위한 가상 주소 공간을 생성하고,
실행 파일의 코드와 데이터, 수행에 필요한 DLL파일을 프로세스의 주소 공간 상에 로드한다(중요!!!)
프로세스 커널 오브젝트와 가상 주소 공간을 나누는 이유는
커널 메모리 공간이 중요하기 때문이라 생각된다
(가상 메모리 부족하면 단순히 에러가 발생할 뿐이지만 커널 메모리가 부족하면 블루 스크린이 발생한다!!)
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <locale.h>
int _tmain(int argc, TCHAR* argv[])
{
#pragma region CreateProcess 예제 코드
// 시작 정보
STARTUPINFO si; // 한 번에 초기화 하기: STARTUPINFO si = { sizeof(si) };
// 프로세스 정보
PROCESS_INFORMATION pi;
// 시작 정보 초기화
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si); // STARTUPINFO의 구조체 바이트 크기
// 프로세스 정보 초기화
ZeroMemory(&pi, sizeof(pi));
// cmd 입력 값 오류 시
// 속성 --> 디버깅 --> 명령 인수에 작성할 수 있다
if (argc != 2)
{
_tprintf(_T("Usage %s\n"), argv[0]);
}
else
{
if (
// ==================== CreateProcess의 디폴트 실행 예제 ====================
CreateProcess(
NULL, // 모듈 이름
argv[1], // 실행하려는 프로그램명(명령행의 첫 원소), 상수 문자열 불가!
NULL, // 프로세스 핸들 상속 여부
NULL, // 스레드 핸들 상속 여부
FALSE, // 호출 프로세스의 상속 가능한 핸들을 새 프로세스에 상속할지 여부
0, // 생성 플래그 --> MSDN에서 옵션 확인 가능
NULL, // 새 프로세스의 환경 블록에 대한 포인터
NULL, // 프로세스의 현재 디렉토리에 대한 전체 경로
&si, // 시작 정보
&pi) // 프로세스 정보
)
{
_tprintf(_T("CreateProcess"));
// %08x : 16진수 8자리 출력(공백은 0으로 패딩)
_tprintf(_T("\n Parent ID: 0x%08X"), GetCurrentProcessId());
_tprintf(_T("\n Child ID: 0x%08X"), pi.dwProcessId); // dwProcessId: 프로세스를 식별 값
// 지정된 프로세스의 우선 순위 클래스를 검색
DWORD priority = GetPriorityClass(pi.hProcess); // hProcess: 새로 생성된 프로세스에 대한 핸들
if (priority)
{
_tprintf(_T("\n Priority Class: %d"), priority);
}
// 자식 프로세스의 종료를 대기
WaitForSingleObject(pi.hProcess, INFINITE);
// 핸들 닫기(안 하면 누수가 발생할 수 있음)
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
{
// 오류 발생 시, 에러 확인
_tprintf(_T("CreateProcess Failed %d\n"), GetLastError());
}
}
#pragma endregion
// 일시 정지
_tsystem(_T("pause"));
return 0;
}
프로세스의 종료(p.155)
프로세스는 다음과 같이 4가지 방법으로 종료될 수 있다
- 메인 쓰레드 반환(가장 추천되는 방법)
- 다른 쓰레드들을 종료시키고 메인 쓰레드도 안전하게 종료된다
- 프로세스 내의 스레드에서 ExitProcess() 호출(이 방법은 피하는 것이 좋음)
- C/C++ 런타임이 정상적으로 정리 작업할 시간이 없음
- 다른 프로세스의 스레드에서 TerminateProcess() 호출(이 방법은 피하는 것이 좋음)
- 외부에서 프로세스를 종료할 때 TerminateProcess()가 호출됨
- 프로세스 내의 모든 스레드가 각자 종료(가끔 필요하다)
메인 쓰레드 진입점 함수의 반환
프로세스가 종료되어야 할 때에는
항상 메인 쓰레드의 진입점 함수가 반환하도록 어플리케이션을 설계하는 것이 좋다
이 방법만이 유일하게 메인 쓰레드의 리소스들이 적절히 해제되는 것이 보장된다
- 메인 쓰레드에 의해 생성된 C++ 오브젝트들이 소멸자를 이용해서 적절하게 소멸된다
- 운영 체제는 쓰레드 스택의 용도로 할당한 메모리 공간을 적절히 해제한다
- 시스템은 진입점 함수의 반환 값으로 프로세스의 종료 코드를 설정한다
- 운영 체제는 프로세스 커널 오브젝트의 사용 카운트를 감소시킨다
프로세스가 종료되면
- 프로세스 내에 남아 있는 쓰레드가 종료된다
- 프로세스에 의해 할당됐던 모든 사용자 오브젝트, GUI 오브젝트가 삭제되며,
모든 커널 오브젝트가 소멸된다 - 프로세스의 종료 코드가 STILL_ACTIVE에서
ExitProcess나 TerminateProcess 호출 시 설정한 종료 코드로 변경된다 - (중요!) 프로세스 커널 오브젝트의 상태가 시그널 상태로 변경된다
(커널 오브젝트를 이용한 쓰레드 동기화와 관련된 내용) - 프로세스 커널 오브젝트의 사용 카운트가 1만큼 감소한다
Taking a Snapshot and Viewing Processes
참고 사이트)
https://docs.microsoft.com/en-us/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes
작업 관리자 등이 프로세스의 상태를 가져오는 예제
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
// Forward declarations:
BOOL GetProcessList();
BOOL ListProcessModules(DWORD dwPID);
BOOL ListProcessThreads(DWORD dwOwnerPID);
void printError(const TCHAR* msg);
int main(void)
{
GetProcessList();
return 0;
}
BOOL GetProcessList()
{
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32;
DWORD dwPriorityClass;
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
printError(TEXT("CreateToolhelp32Snapshot (of processes)"));
return(FALSE);
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process,
// and exit if unsuccessful
if (!Process32First(hProcessSnap, &pe32))
{
printError(TEXT("Process32First")); // show cause of failure
CloseHandle(hProcessSnap); // clean the snapshot object
return(FALSE);
}
// Now walk the snapshot of processes, and
// display information about each process in turn
do
{
_tprintf(TEXT("\n\n====================================================="));
_tprintf(TEXT("\nPROCESS NAME: %s"), pe32.szExeFile);
_tprintf(TEXT("\n-------------------------------------------------------"));
// Retrieve the priority class.
dwPriorityClass = 0;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
if (hProcess == NULL)
printError(TEXT("OpenProcess"));
else
{
dwPriorityClass = GetPriorityClass(hProcess);
if (!dwPriorityClass)
printError(TEXT("GetPriorityClass"));
CloseHandle(hProcess);
}
_tprintf(TEXT("\n Process ID = 0x%08X"), pe32.th32ProcessID);
_tprintf(TEXT("\n Thread count = %d"), pe32.cntThreads);
_tprintf(TEXT("\n Parent process ID = 0x%08X"), pe32.th32ParentProcessID);
_tprintf(TEXT("\n Priority base = %d"), pe32.pcPriClassBase);
if (dwPriorityClass)
_tprintf(TEXT("\n Priority class = %d"), dwPriorityClass);
// List the modules and threads associated with this process
ListProcessModules(pe32.th32ProcessID);
ListProcessThreads(pe32.th32ProcessID);
} while (Process32Next(hProcessSnap, &pe32)); // 다음 프로세스 리스트를 획득
CloseHandle(hProcessSnap);
return(TRUE);
}
BOOL ListProcessModules(DWORD dwPID)
{
HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
MODULEENTRY32 me32;
// Take a snapshot of all modules in the specified process.
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
if (hModuleSnap == INVALID_HANDLE_VALUE)
{
printError(TEXT("CreateToolhelp32Snapshot (of modules)"));
return(FALSE);
}
// Set the size of the structure before using it.
me32.dwSize = sizeof(MODULEENTRY32);
// Retrieve information about the first module,
// and exit if unsuccessful
if (!Module32First(hModuleSnap, &me32))
{
printError(TEXT("Module32First")); // show cause of failure
CloseHandle(hModuleSnap); // clean the snapshot object
return(FALSE);
}
// Now walk the module list of the process,
// and display information about each module
do
{
_tprintf(TEXT("\n\n MODULE NAME: %s"), me32.szModule);
_tprintf(TEXT("\n Executable = %s"), me32.szExePath);
_tprintf(TEXT("\n Process ID = 0x%08X"), me32.th32ProcessID);
_tprintf(TEXT("\n Ref count (g) = 0x%04X"), me32.GlblcntUsage);
_tprintf(TEXT("\n Ref count (p) = 0x%04X"), me32.ProccntUsage);
_tprintf(TEXT("\n Base address = 0x%08X"), (DWORD)me32.modBaseAddr);
_tprintf(TEXT("\n Base size = %d"), me32.modBaseSize);
} while (Module32Next(hModuleSnap, &me32));
CloseHandle(hModuleSnap);
return(TRUE);
}
BOOL ListProcessThreads(DWORD dwOwnerPID)
{
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
// Take a snapshot of all running threads
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hThreadSnap == INVALID_HANDLE_VALUE)
return(FALSE);
// Fill in the size of the structure before using it.
te32.dwSize = sizeof(THREADENTRY32);
// Retrieve information about the first thread,
// and exit if unsuccessful
if (!Thread32First(hThreadSnap, &te32))
{
printError(TEXT("Thread32First")); // show cause of failure
CloseHandle(hThreadSnap); // clean the snapshot object
return(FALSE);
}
// Now walk the thread list of the system,
// and display information about each thread
// associated with the specified process
do
{
if (te32.th32OwnerProcessID == dwOwnerPID)
{
_tprintf(TEXT("\n\n THREAD ID = 0x%08X"), te32.th32ThreadID);
_tprintf(TEXT("\n Base priority = %d"), te32.tpBasePri);
_tprintf(TEXT("\n Delta priority = %d"), te32.tpDeltaPri);
_tprintf(TEXT("\n"));
}
} while (Thread32Next(hThreadSnap, &te32));
CloseHandle(hThreadSnap);
return(TRUE);
}
void printError(const TCHAR* msg)
{
DWORD eNum;
TCHAR sysMsg[256];
TCHAR* p;
eNum = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, eNum,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
sysMsg, 256, NULL);
// Trim the end of the line and terminate it with a null
p = sysMsg;
while ((*p > 31) || (*p == 9))
++p;
do { *p-- = 0; } while ((p >= sysMsg) &&
((*p == '.') || (*p < 33)));
// Display the message
_tprintf(TEXT("\n WARNING: %s failed with error %d (%s)"), msg, eNum, sysMsg);
}
aaa
'게임 프로그래밍(학습 내용 정리) > 시스템 프로그래밍' 카테고리의 다른 글
Exception Filter (0) | 2022.03.22 |
---|---|
실제 핸들과 허위 핸들(Pseudo Handle) (0) | 2022.03.22 |
프로세스와 스레드 (0) | 2022.03.21 |
커널 오브젝트 (0) | 2022.03.08 |
시스템 프로그래밍이란 무엇인가? (0) | 2022.03.07 |