190702-2 -
윈도우의 중요한 예제
교육을 받으면서 노트필기 했던 내용을 날것 그대로 업로드합니다.
-
06커널오브젝트와핸들
핸들테이블을 상속해서 상속한 핸들테이블의 핸들을 가지고 자식이 사용가능하다.
child
#include <stdio.h>
#include <Windows.h>
void main()
{
HANDLE hParentProcess;
DWORD handle;
printf("부모에서 생성한 이벤트의 핸들: ");
scanf("%d", &handle);
HANDLE hEvent = (HANDLE)handle;
printf("이벤트 신호 상태 기다림...\n");
DWORD idx = WaitForSingleObject(hEvent, INFINITE); //● 신호상태 까지 INFINITE기다린다.
if( idx != WAIT_OBJECT_0)
printf("이벤트 핸들이 유효하지 않음! : %d\n", idx);
Sleep(5000);
CloseHandle(hEvent);
}
● 프로세스를 띠워준 프로세스를 부모프로세스 라고한다.
● 윈도우의 모든 프로세스는 자신만의 커널오브젝트 핸들테이블을 가지고 있다. 커널 오브젝트라면 모두 테이블에 기록된다.
● 모든 커널오브젝트는 상속 여부를 결정하는(보안속성) 것이 있다. NULL이면 상속안하겠다는 것.
SECURITY_ATTRIBUTES sa; //● 보안속성 (상속여부를 나타냄)
sa.nLength = sizeof(sa); //● 자신의 사이즈
sa.lpSecurityDescriptor=NULL; //● 문자열(설명)
sa.bInheritHandle = TRUE; //● 상속가능하도록 핸들을 설정할지
● ReferenceCount : 핸들테이블에 핸들이 등록될 때마다 증가한다.
커널오브젝트를 핸들로 관리하고 커널오브젝트는 핸들테이블을 갖는다.
● 프로세스 핸들 사용 2가지
getProcess 수도핸들(레퍼카운트증가X, 등록안되있음)
openProcess 아이디 갖고 프로세스 핸들을 얻어서 씀 (얻은 핸들은 핸들테이블에 등록하고, 참조카운트도 +1증가된다.)
● 핸들번호, 상속여부, 실제커널오브젝트를 가리키는 참조
parent
#include <stdio.h>
#include <windows.h>
void main()
{
STARTUPINFO si = { sizeof(STARTUPINFO), };
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa; //● 보안속성 (상속여부를 나타냄)
sa.nLength = sizeof(sa); //● 자신의 사이즈
sa.lpSecurityDescriptor = NULL; //● 문자열(설명)
sa.bInheritHandle = TRUE; //●ⓑ 상속가능하도록 핸들을 설정할지
//커널 오브젝트 이벤트 상속 가능하도록 설정
HANDLE hEvent = CreateEvent(&sa, FALSE, FALSE, "TestEvent");
//● &sa : 모든 커널 오브젝트는 보안속성을 인수로 갖게되어있음 (여기선 상속가능하게)
//● 첫 인자를 NULL로주면 내 프로세스 내에서만 사용하겠다는 뜻이다.
//핸들 테이블 자식에 상속
CreateProcess(NULL, "Z_ChildProc.exe", NULL, NULL, TRUE/*ⓐ*/, CREATE_NEW_CONSOLE/*동일콘솔을 쓸건지*/, NULL, NULL, &si, &pi);
//●ⓐ 핸들 테이블을 자식한테 상속할지 결정한다.
//CreateProcess(NULL, "Z_ChildProc.exe",NULL,NULL,FALSE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi);
printf("이벤트의 핸들: %d\n", hEvent); //● 핸들의 번호 출력
printf("엔터를 누르면 이벤트를 신호 상태로 설정함");
getchar();
SetEvent(hEvent); //● 신호상태로 만든다.
CloseHandle(hEvent);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
● CreateProcess 함수 호출시
-
자식 프로세스에게 핸들 테이블을 상속할 것인가 결정
-
또 다른 자식 프로세스 생성지 현재 등록하게된 핸들의 상속 여부를 결정
10. 쓰레드생성
● 프로세스와 쓰레드는 종료될 때 신호상태가된다.
#include <stdio.h>
#include <windows.h>
DWORD __stdcall ThreadFunc(LPVOID pParam)
{
printf("ThreadFunc Param : %s\n", pParam);
for(int i = 0 ; i < 10 ; ++i)
{
printf("ThreadFunc : %d\n", i);
Sleep(500);
}
return 0; // 자신이 실행하는 함수가 종료하면 쓰레드도 종료(정상종료) (우아한 종료)
}
void main()
{
DWORD threadID;
HANDLE hThread;
hThread = CreateThread( //● 서브 쓰레드
0, //보안 속성
0, //stack 크기(0: default)
ThreadFunc, //쓰레드가 수행할 함수
"Hello!", // 쓰레드 인자
0, // 플래그(CREATE_SUSPENDED : 쓰레드 시작과 동시에 블록 상태로)
&threadID // 쓰레드 ID out param
);
/*ThreadFunc("Hello!");*/ //● 프라이머리 쓰레드가 실행
//쓰레드 종료 대기
WaitForSingleObject(hThread, INFINITE);//● 서브스레드가 신호상태 될때까지 기다린다.
//쓰레드 커널 오브젝트 닫기
CloseHandle(hThread);
}
#include <stdio.h>
#include <windows.h>
DWORD __stdcall ThreadFunc(LPVOID pParam)
{
printf("ThreadFunc Param : %s\n", pParam);
for (int i = 0; i < 10; ++i)
{
printf("ThreadFunc[%d] : %d\n",GetCurrentThreadId(), i);
Sleep(500);
}
return 0;
}
void main()
{
DWORD threadID;
HANDLE hThread;
hThread = CreateThread( //● 서브 쓰레드 // 여기부터 명령 체계가 2개가된다.
0, //보안 속성
0, //stack 크기(0: default)
ThreadFunc, //쓰레드가 수행한 함수
"Hello!", // 쓰레드 인자
0, // 플래그(CREATE_SUSPENDED : 쓰레드 시작과 동시에 블록 상태로)
&threadID // 쓰레드 ID out param
);
for (int i = 0; i < 200; i++)
{
printf("main()[%d] : %d\n", GetCurrentThreadId(), i);
Sleep(300);
}
//쓰레드 종료 대기
WaitForSingleObject(hThread, INFINITE);//● 서브스레드가 신호상태 될때까지 기다린다.
//쓰레드 커널 오브젝트 닫기
CloseHandle(hThread);
}
● 쓰레드는 다중작업에 사용된다.
● 프라이머리 쓰레드가 죽으면(프로세스가 죽는다) 나머지 서브 쓰레드가 다 죽는다.
#include <stdio.h>
#include <windows.h>
DWORD __stdcall ThreadFunc(LPVOID pParam)
{
printf("ThreadFunc Param : %s\n", pParam);
for (int i = 0; i < 10; ++i)
{
printf("ThreadFunc[%d] : %d\n", GetCurrentThreadId(), i);
Sleep(500);
}
return 0;
}
void main()
{
DWORD threadID;
//HANDLE hThread;
CreateThread( //● 서브 쓰레드
0, //보안 속성
0, //stack 크기(0: default)
ThreadFunc, //쓰레드가 수행한 함수
"Hello!", // 쓰레드 인자
0, // 플래그(CREATE_SUSPENDED : 쓰레드 시작과 동시에 블록 상태로)
&threadID // 쓰레드 ID out param
);
CreateThread( //● 서브 쓰레드
0, //보안 속성
0, //stack 크기(0: default)
ThreadFunc, //쓰레드가 수행한 함수
"Hello!", // 쓰레드 인자
0, // 플래그(CREATE_SUSPENDED : 쓰레드 시작과 동시에 블록 상태로)
&threadID // 쓰레드 ID out param
);
Sleep(1);
//프라이머리 쓰레드 종료시 모든 서브 프로세스가 종료된다.
}
● 스택 메모리는 사실 스레드가 소유하는 것이다.
● static으로 지정한 변수는 GD영역에 있으므로 서로 다른 쓰레드에서 같이 사용할 수 있다.
#include <stdio.h>
#include <windows.h>
DWORD __stdcall ThreadFunc(LPVOID pParam)// API에서는 쓰레드에서 사용할 함수를 이렇게 만들어야한다.
{
static int i;
printf("ThreadFunc Param : %s\n", pParam);
for (i = 0; i < 10; ++i)
{
printf("ThreadFunc[%d] : %d\n", GetCurrentThreadId(), i);
Sleep(500);
}
return 0;
}
void main()
{
DWORD threadID;
//HANDLE hThread;
CreateThread( //● 서브 쓰레드
0, //보안 속성 모든 커널오브젝트가 갖는 보안속성
0, //stack 크기(0: default(약1MB)) ●스택사이즈를 명시.
ThreadFunc, //쓰레드가 수행할 함수(의 주소)
"Hello!", // 쓰레드 인자 (전달하고자 하는 인수)
0, // 플래그(CREATE_SUSPENDED : 쓰레드 시작과 동시에 블록 상태로)
&threadID // 쓰레드 ID out param
);
CreateThread( //● 서브 쓰레드
0, //보안 속성
0, //stack 크기(0: default)
ThreadFunc, //쓰레드가 수행한 함수
"Hello!", // 쓰레드 인자
0, // 플래그(CREATE_SUSPENDED : 쓰레드 시작과 동시에 블록 상태로) 0:어떤 플러그도 설정하지 않는다.
&threadID // 쓰레드 ID out param
);
for (int i = 0; i < 200; i++)
{
printf("main()[%d] : %d\n", GetCurrentThreadId(), i);
Sleep(300);
}
}
↑ i변수를 for문에서 0으로 초기화 하기는 하지만 static 변수라 같이 카운트 하므로 위와 같이 출력된다.
● 쓰레드나 프로세스를 종료하는 방법
-
함수를 리턴시키는 방법(가장 정상적인 방법)
-
Exit로 시작하는 함수 (정상 종료)
-
Terminate 로 종료 (비정상 종료)(강제종료)
#include <stdio.h>
#include <windows.h>
DWORD __stdcall ThreadFunc(LPVOID pParam)
{
printf("ThreadFunc Param : %s\n", pParam);
for(int i = 0 ; i < 10 ; ++i)
{
printf("ThreadFunc : %d\n", i);
Sleep(500);
ExitThread(0); //?? 0만 전달하는데 왜 종료 -> 자기 내부에서만 호출가능
//함수를 호출한 쓰레드를 종료한다.
}
//2. ExitThread(0) 함수 사용
ExitThread(0);
return 0; //1. 쓰레드 함수 반환 종료
}
void main()
{
DWORD threadID;
HANDLE hThread;
hThread = CreateThread(
0, //보안 속성
0, //stack 크기(0: default)
ThreadFunc, //쓰레드가 수행한 함수
"Hello!", // 쓰레드 인자
0, // 플래그(CREATE_SUSPENDED : 쓰레드 시작과 동시에 블록 상태로)
&threadID // 쓰레드 ID out param
);
printf("main Thread: 종료하라면 엔터키를 누르세요.\n");
getchar();
//3. 외부 강제 종료(사용하지 말 것!) 쓰레드 자신은 종료를 알지 못한다.(메모리와 리소르 해제 없이 종료!)
TerminateThread(hThread, 0);
//쓰레드 종료 대기
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
13. 다중쓰레드와메모리(static,heap)
#include <stdio.h>
#include <windows.h>
void __cdecl Sleep() //● __cdecl : 기본호출규약 (지금은 별로 안중요) (64에서는 둘다 동일하게 처리된다)
{
for(int i = 0 ; i < 100000000 ; ++i)
;
}
DWORD __stdcall ThreadFunc(LPVOID pParam)
{
printf("first : %d 생성\n", (int)pParam);
static int num = (int)pParam; //정적 변수에 쓰레드 번호 저장
printf("ThreadFunc Param : %d 생성\n", num);
for(int i = 0 ; i < 10 ; ++i)
{
printf("ThreadFunc[%d] : %d\n",num, i);
Sleep();
}
return 0;
}
void main()
{
DWORD threadID;
HANDLE hThread[2];
hThread[0] = CreateThread(0,0,ThreadFunc, (LPVOID)1, 0, &threadID);
hThread[1] = CreateThread(0,0,ThreadFunc, (LPVOID)2, 0, &threadID);
//쓰레드 종료 대기(모든 쓰레드의 신호상태를 기다림)
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
}
● static 변수 초기화는 단 한 번
'System Programming' 카테고리의 다른 글
[system programming] 핸들, 커널 오브젝트, 프로세스 등 (0) | 2019.07.04 |
---|---|
[system programming] WS_CHILD, SuspendThread, ResumeThread 등 (0) | 2019.07.04 |
[system programming] TLS, ResumeThread, nterlockedIncrement 등 (0) | 2019.07.04 |
[system programming] 핸들, 동기, 신호/비신호 (0) | 2019.07.03 |
[system programming] 커널오브젝트, 아스키코드와 유니코드 (0) | 2019.07.03 |