280 likes | 508 Views
14. 프로세스. 1. 프로세스와 스레드. 프로세스는 실행중인 프로그램의 한 인스턴스이다 . 운영체제는 실행된 프로그램을 프로세스 단위로 관리한다 . 프로세스는 각각 4GB 의 주소 공간과 파일 , 메모리 , 스레드 등의 객체들을 소유하며 프로세스가 종료될 때 프로세스가 소유한 자원은 운영체제에 의해 파괴된다 . 프로세스는 실행과 동시에 스레드를 하나 만들고 스레드를 호출함으로써 스레드에게 모든 작업을 맡긴다 . 프로세스는 최소한 한 개 이상의 스레드를 가진다 .
E N D
14. 프로세스
1. 프로세스와 스레드 • 프로세스는 실행중인 프로그램의 한 인스턴스이다. • 운영체제는 실행된 프로그램을 프로세스 단위로 관리한다. • 프로세스는 각각 4GB의 주소 공간과 파일, 메모리, 스레드 등의 객체들을 소유하며 프로세스가 종료될 때 프로세스가 소유한 자원은 운영체제에 의해 파괴된다. • 프로세스는 실행과 동시에 스레드를 하나 만들고 스레드를 호출함으로써 스레드에게 모든 작업을 맡긴다. • 프로세스는 최소한 한 개 이상의 스레드를 가진다. • 프로세스와 동시에 만들어지는 스레드를 주 스레드(Primary Thread)라고 한다. • 하나의 프로세스가 여러 개의 스레드를 만들 수 있다.
1. 프로세스와 스레드 BOOLCreateProcess( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); • lpApplicationName : • 실행하고자 프로그램의 이름을 준다. • 완전 경로를 주거나 파일명만 지정한 경우는 현재 디렉토리에서 파일을 찾으며 검색 경로는 사용하지 않는다. • 이 인수를 NULL로 주고 두 번째 인수에 실행 파일명을 줄 수도 있다.
1. 프로세스와 스레드 • lpCommandLine • 명령행 인수를 지정한다. • 첫 번째 인수가 NULL일 경우 실행 파일명을 가질 수도 있으며 실행 파일명과 명령행 인수를 동시에 지정하는 것도 가능하다. • lpStartupInfo • 새로 만든 프로세스의 메인 윈도우가 어떻게 초기화될지를 지정하는 구조체이다. • 이 구조체의 cb멤버에는 구조체의 크기가 반드시 대입되어야 한다. • lpProcessInformation • 생성된 프로세스의 정보를 대입 받기 위한 구조체이며 생략할 수 없다. case WM_LBUTTONDOWN: { STARTUPINFO si; memset(&si,0,sizeof(STARTUPINFO)); PROCESS_INFORMATION pi; CreateProcess(NULL,"Notepad.exe",NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi); } return 0;
1. 프로세스와 스레드 typedef struct _STARTUPINFO { DWORD cb; LPTSTR lpReserved; LPTSTR lpDesktop; LPTSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFO,*LPSTARTUPINFO;
1. 프로세스와 스레드 • cb : sizeof(STARTUPINFO)값을 대입한다. • dwFlags : 어떤 속성을 지정할 것인가에 따라 플래그를 설정한다. typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION,*LPPROCESS_INFORMATION;
1. 프로세스와 스레드 • lpProcessAttributes • lpThreadAttributes • 프로세스와 주 스레드의 보안 속성을 지정한다. • bInheritHandles • 새로 생성되는 프로세스가 페이런트로부터 핸들을 상속받을 수 있는지를 지정한다. • dwCreationFlags • 새로 생성되는 프로세스의 우선 순위 클래스와 프로세스 생성 옵션을 지정한다. • REALTIME_PRIORITY_CLASS : 최상위 우선권 • HIGH_PRIORITY_CLASS : 상위 우선권 • ABOVE_PRIORITY_CLASS : 상위 우선권 • NORMAL_PRIORITY_CLASS : 보통 우선권 • BELOW_PRIORITY_CLASS : 하위 우선권 • IDLE_PRIORITY_CLASS : 최하위 우선권 • lpEnvironment • 새 프로세스의 환경 블록을 지정하는 포인터. 이 값이 NULL이면 페이런트의 환경 블록을 사용하며 보통 NULL이다. • lpCurrentDirectory, • lpStartupInfo, • lpProcessInformation
1. 프로세스와 스레드 • lpCurrentDirectory • 새 프로세스의 작업 디렉토리를 지정한다. • NULL일 경우 페이런트의 현재 디렉토리가 새 프로세스의 작업디렉토리가 된다. • DWORD WaitForInputIdle( HANDLE hProcess, DWORD dwMilliseconds); • 이 함수는 hProcess가 지정하는 프로세스가 사용자의 입력을 대기할 수 있을 때까지, 즉 초기화가 완료될 때까지 기다려준다. • LPTSTR GetCommandLine(VOID); • 이 함수는 현재 프로세스의 명령행 인수를 조사해 리턴해 준다. • CommandLineToArgvW() • 함수를 사용하여 토큰별로 분리 할 수도 있다.
1. 프로세스와 스레드 • VOID ExitProcess( UINT uExitCode ); • 이 함수가 호출되면 프로세스는 정리작업에 들어가 즉각 종료된다. • 1. 프로세스와 연결된 모든 DLL을 종료시키기 위해 각 DLL의 DllMain함수가 호출되며 DLL들은 스스로 정리 작업을 한다. • 2. 모든 열려진 핸들을 닫는다. • 3. 실행중인 모든 스레드는 종료한다. • 4. 프로세스 커널 객체와 스레드 객체는 신호상태가 되며 이 객체를 기다리는 다른 프로세스는 대기상태를 해제할 수 있다. • 5. 프로세스의 종료코드는 STILL_ACTIVE와 ExitProcess가 지정한 종료값이 된다. • BOOL TerminateProcess( HANDLE hProcess, UINT uExitCode); • 이 함수는 ExitProcess에 비해 종료 대상이 되는 프로세스의 핸들을 인수로 가지므로 다른 프로세스를 강제로 종료시킬 수도 있다. • 이 함수는 ExitProcess보다 훨씬 더 위험하다. • TerminateProcess함수가 호출될 때 ExitProcess와 동일한 정리작업이 수행되나 단 연결된 DLL에게 종료사실이 통지되지 않는다. • 어쩔 수 없이 강제로 종료해야 할 경우에만 사용한다.
2. 프로세스 핸들 • 커널 객체는 프로세스 한정적이다. • 커널 객체를 만드는 프로세스만이 자신의 핸들로 해당 객체를 액세스할 수 있다는 뜻이다. • 핸들은 프로세스 내에서 해당 객체를 액세스할 때 사용하는 한정적인 값이며 이 핸들을 사용하여 객체를 마음대로 조작할 수 있다. • ID는 시스템 전역적인 값이며 다른 프로세스 ID와 절대 중복되지 않는다. • 프로세스끼리 ID를 전달해 줌으로써 목적이 되는 프로세스 핸들을 다시 오픈할 수 있다. • HANDLE GetCurrentProcess(VOID); • HANDLE GetCurrentProcessId(VOID);
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps ; static HINSTANCE hInst; static STARTUPINFO si; static PROCESS_INFORMATION pi; switch (message) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lParam)->hInstance; return 0; case WM_LBUTTONDOWN: { memset(&si,0,sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USEPOSITION | STARTF_USESIZE; si.dwX = 200; si.dwY = 200; si.dwXSize = 100; si.dwYSize = 100; CreateProcess(NULL,"Notepad.exe",NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi); } return 0; case WM_RBUTTONDOWN: { HWND hProcessWnd = FindWindow(NULL,"Process"); MessageBox(NULL,"윈도우를 찾았습니다.","",MB_OK); PostMessage(hProcessWnd,WM_MYMSG,(WPARAM)pi.dwProcessId,NULL); } return 0; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps ; static HINSTANCE hInst; static DWORD pID; static HANDLE hProc; switch (message) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lParam)->hInstance; return 0; case WM_MYMSG: { pID = (DWORD)wParam; hProc = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pID); char temp[256]; wsprintf(temp,"Process ID : %x, Process Handle : %x",pID,hProc); MessageBox(NULL,temp,"",MB_OK); } return 0; case WM_LBUTTONDOWN: { DWORD ExitCode; GetExitCodeProcess(hProc,&ExitCode); if (ExitCode != STILL_ACTIVE) { MessageBox(NULL,"프로세스 핸들이 유효하지 않습니다.","",MB_OK); } else { TerminateProcess(hProc,NULL); pID = NULL; } } return 0; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
3. Thread • Project/Settings c/c++탭의 Code Generation에서 Use run-time Library옵션을 선택
3. Thread uintptr_t _beginthreadex( void * security, unsigned stack_size, unsigned ( __stdcall *start_address )( void * ), void * arglist, unsigned initflag, unsigned * thrdaddr ); • security : SECURITY_ATTRIBUTES구조체의 주소, 대부분 NULL • stack_size : 0을 입력하면 기본 스텍사이즈를 사용한다. • unsigned ( __stdcall *start_address )( void * ) • arglist : 스레드 함수로 넘어가는 변수 • initflag : CREATE_SUSPENDED면 스레드만 만들고 실행은 하지 않는다. • thrdaddr : 스레드 ID
typedef struct threadParam { HWND hwnd; BOOL bCont; }THREAD_PARAM; unsigned int WINAPI MyThreadFunc(LPVOID lpParameter) { THREAD_PARAM * pTp = (THREAD_PARAM *)lpParameter; HWND hwnd = pTp->hwnd; while(pTp->bCont) { HBRUSH hBrush = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256)); RECT ClietRect,rect; GetClientRect(hwnd,&ClietRect); SetRect(&rect,rand()%ClietRect.right,rand()%ClietRect.bottom,rand()%ClietRect.right,rand()%ClietRect.bottom); HDC hdc = GetDC(hwnd); HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc,hBrush); Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom); SelectObject(hdc,hOldBrush); ReleaseDC(hwnd,hdc); Sleep(100); } return 0; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps ; static unsigned int ThreadID; static HANDLE hThread; static THREAD_PARAM tp;
switch (message) { case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_THREAD_START: if (hThread == NULL) { tp.hwnd = hwnd; tp.bCont = TRUE; hThread = (HANDLE)_beginthreadex(NULL,NULL,MyThreadFunc,(void *)&tp,NULL,&ThreadID); } else { DWORD ExitCode; GetExitCodeThread(hThread,&ExitCode); if (ExitCode == STILL_ACTIVE ) MessageBox(NULL,"현재 스레드는 살아있습니다.","",MB_OK); else MessageBox(NULL,"스레드는 죽었습니다.","",MB_OK); } break; case IDM_THREAD_QUIT: tp.bCont = FALSE; break; case IDM_THERAD_SUSPEND: SuspendThread(hThread); break; case IDM_THREAD_RESUME: ResumeThread(hThread); break; } return 0; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
CRITICAL_SECTION cs; int XPos; unsigned int WINAPI MyThreadFunc1(LPVOID lpParameter) { THREAD_PARAM * pTp = (THREAD_PARAM *)lpParameter; HWND hwnd = pTp->hwnd; while(pTp->bCont) { HBRUSH hBrush = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256)); RECT ClietRect,rect; GetClientRect(hwnd,&ClietRect); SetRect(&rect,rand()%ClietRect.right,rand()%ClietRect.bottom,rand()%ClietRect.right,rand()%ClietRect.bottom); HDC hdc = GetDC(hwnd); EnterCriticalSection(&cs); XPos = 100; Sleep(10); TextOut(hdc,XPos,0,"1번 스레드 펑션",15); LeaveCriticalSection(&cs); HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc,hBrush); Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom); SelectObject(hdc,hOldBrush); ReleaseDC(hwnd,hdc); Sleep(120); } return 0; }
unsigned int WINAPI MyThreadFunc2(LPVOID lpParameter) { THREAD_PARAM * pTp = (THREAD_PARAM *)lpParameter; HWND hwnd = pTp->hwnd; while(pTp->bCont) { HBRUSH hBrush = CreateSolidBrush(RGB(rand()%256,rand()%256,rand()%256)); RECT ClietRect,rect; GetClientRect(hwnd,&ClietRect); SetRect(&rect,rand()%ClietRect.right,rand()%ClietRect.bottom,rand()%ClietRect.right,rand()%ClietRect.bottom); HDC hdc = GetDC(hwnd); EnterCriticalSection(&cs); XPos = 400; Sleep(15); TextOut(hdc,XPos,0,"2번 스레드 펑션",15); LeaveCriticalSection(&cs); HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc,hBrush); Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom); SelectObject(hdc,hOldBrush); ReleaseDC(hwnd,hdc); Sleep(130); } return 0; }
3. 동기화 • 크리티컬 섹션 • voidInitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection ); • voidDeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection ); • 둘 다 CRITICAL_SECTON형의 포인터를 인수로 요구한다. • voidEnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection ); • voidLeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection ); • 보호될 코드를 다음과 같이 두 함수로 감싸준다. EnterCriticalSection(&cs); //이 사이에서 공유 자원을 안전하게 액세스한다. LeaveCriticalSection(&cs);
3. 동기화 case WM_CREATE: InitializeCriticalSection(&cs); return 0; case WM_DESTROY: DeleteCriticalSection(&cs); PostQuitMessage (0) ; return 0 ; • 교착 상태 EnterCriticalSection(&cs1); EnterCriticalSection(&cs2); //이 사이에서 공유 자원을 안전하게 액세스한다. LeaveCriticalSection(&cs2); LeaveCriticalSection(&cs1); EnterCriticalSection(&cs2); EnterCriticalSection(&cs1); //이 사이에서 공유 자원을 안전하게 액세스한다. LeaveCriticalSection(&cs1); LeaveCriticalSection(&cs2);
3. 동기화 • 동기화 객체 • 동기화에 사용되는 객체이다. • 프로세스,스레드처럼 커널 객체이며 프로세스 한정적인 핸들을 가진다. • 동기화 객체는 크리티컬 섹션보다 느리기는 하지만 여러 프로그램에서 동시에 동기화가 가능하다. • 신호 상태 : 스레드의 실행을 허가하는 상태. 신호상태의 동기화 객체를 가진 스레드는 계속 실행할 수 있다. • 비 신호 상태 : 스레드의 실행을 허가하지 않는 상태이며 신호 상태가 될 때까지 스레드는 블록 된다. • DWORDWaitForSingleObject(HANDLEhHandle, DWORDdwMilliseconds ); • dwMilliseconds : 1/1000초 단위로 지정한다. INFINITE로 지정하면 무한정 기다린다.
3. 동기화 • WAIT_OBJECT_0 : hHandle 객체가 신호 상태가 되었다. • WAIT_TIMEOUT : 타임 아웃 시간이 경과하였다. • WAIT_ABANDONED : 포기된 뮤텍스 • 뮤텍스 • 크리티컬 섹션과 유사하다. • 이름을 가질 수 있고 프로세스간에서도 사용이 가능하다. • HANDLECreateMutex(LPSECURITY_ATTRIBUTESlpMutexAttributes, BOOLbInitialOwner, LPCTSTRlpName ); • lpMutexAttributes : 보통 NULL • bInitialOwner :뮤텍스를 생성함과 동시에 소유할 것인지를 지정한다. TRUE이면 소유하며, 뮤텍스가 비 신호상태로 생성됨으로써 다른 스레드는 이 뮤텍스를 소요할 수 없게 된다. • 생성한 뮤텍스를 파괴할 때는 CloseHandle함수를 이용한다. • BOOLReleaseMutex(HANDLE hMutex ); • 뮤텍스 소유를 해제한다.
#include <windows.h> #include "resource.h" BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ; return TRUE; } BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hEdit1,hEdit2; static HANDLE hFileMapping; static char * pMapView; static HANDLE hMutex; switch (message) { case WM_INITDIALOG : hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,1042,"MappingMutexSample"); pMapView = (char *)MapViewOfFile(hFileMapping,FILE_MAP_ALL_ACCESS,0,0,1024); hEdit1 = GetDlgItem(hDlg,IDC_EDIT1); hEdit2 = GetDlgItem(hDlg,IDC_EDIT2); hMutex = CreateMutex(NULL,FALSE,"MyMutexSample"); return TRUE ;
case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BUTTON1: { WaitForSingleObject(hMutex,INFINITE); char temp[256]; GetWindowText(hEdit1,temp,256); SetWindowText(hEdit2,temp); strcpy(pMapView,temp); ReleaseMutex(hMutex); } return TRUE; case IDC_BUTTON2: { WaitForSingleObject(hMutex,INFINITE); SetWindowText(hEdit2,pMapView); ReleaseMutex(hMutex); } return TRUE; case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; }
3. 동기화 • 이벤트 • 어떤 사건이 일어났음을 알려주는 동기화 객체이다. • 크리티컬 섹션, 뮤텍스, 세마포어는 주로 공유 자원을 보호하기 위해 사용되는 데 비해 이벤트는 그보다는 스레드간의 작업 순서나 시기를 조정하기 위해 사용한다. • 자동 리셋 • 대기 상태가 종료되면 자동으로 비신호상태가 된다. • 수동 리셋 • 스레드가 비신호상태로 만들어 줄 때까지 신호상태를 유지한다. HANDLECreateEvent( LPSECURITY_ATTRIBUTESlpEventAttributes, BOOLbManualReset, BOOLbInitialState, LPCTSTRlpName );
3. 동기화 • bManualReset : 이 이벤트가 수동 리셋 이벤트인지 자동 리셋 이벤트인지를 지정한다. • TRUE : 수종 리셋 이벤트, FALSE : 자동 리셋 이벤트 • bInitialState : 이벤트 생성과 동시에 신호상태로 만들어 이벤트를 기다리는 스레드가 곧바로 실행하도록 한다. • BOOL SetEvent( HANDLE hEvent ); • BOOL ResetEvent( HANDLE hEvent ); • BOOL PulseEvent(HANDLE hEvent );
#include <windows.h> #include "resource.h" #include <process.h> BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc) ; return TRUE; } typedef struct param { HWND hEdit2; BOOL bCont; HANDLE hEvent; char * pView; }PARAM; unsigned __stdcall MyThreadProc( void * pArguments ) { PARAM * pParam = (PARAM *)pArguments; while(pParam->bCont) { WaitForSingleObject(pParam->hEvent,INFINITE); SetWindowText(pParam->hEdit2,pParam->pView); } _endthreadex( 0 ); return 0; } BOOL CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static HWND hEdit1,hEdit2; static HANDLE hFileMapping; static char * pMapView; static HANDLE hMutex; static HANDLE hThread;
static PARAM param; static HANDLE hEvent; switch (message) { case WM_INITDIALOG : hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,1042,"MappingMutexSample"); pMapView = (char *)MapViewOfFile(hFileMapping,FILE_MAP_ALL_ACCESS,0,0,1024); hEdit1 = GetDlgItem(hDlg,IDC_EDIT1); hEdit2 = GetDlgItem(hDlg,IDC_EDIT2); hMutex = CreateMutex(NULL,FALSE,"MyMutexSample"); hEvent = CreateEvent(NULL,TRUE,FALSE,"MyMutexSampleEvent"); param.bCont = TRUE; param.hEdit2 = hEdit2; param.hEvent = hEvent; param.pView = pMapView; hThread = (HANDLE)_beginthreadex(NULL,NULL,MyThreadProc,¶m,NULL,NULL); return TRUE ; case WM_COMMAND : switch (LOWORD (wParam)) { case IDC_BUTTON1: { WaitForSingleObject(hMutex,INFINITE); char temp[256]; GetWindowText(hEdit1,temp,256); strcpy(pMapView,temp); PulseEvent(hEvent); ReleaseMutex(hMutex); } return TRUE; case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; }