티스토리 뷰

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 함수 호출시
  1. 자식 프로세스에게 핸들 테이블을 상속할 것인가 결정
  2. 또 다른 자식 프로세스 생성지 현재 등록하게된 핸들의 상속 여부를 결정
 

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 변수라 같이 카운트 하므로 위와 같이 출력된다.
 
● 쓰레드나 프로세스를 종료하는 방법
  1. 함수를 리턴시키는 방법(가장 정상적인 방법)
  2. Exit로 시작하는 함수 (정상 종료)
  3. 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 변수 초기화는 단 한 번
댓글
댓글쓰기 폼
네이버 이웃추가
«   2019/11   »
          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
글 보관함