Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
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
Archives
Today
Total
관리 메뉴

메모장

TCP/IP 소켓 프로그래밍 복습4 본문

TCP&IP 복습

TCP/IP 소켓 프로그래밍 복습4

Captic 2020. 10. 30. 07:40

참고문헌 - 윤성우 <윤성우의 열혈 TCP/IP 소켓 프로그래밍>

♨ 개인적 해석이 들어간 글임으로, 인지하지 못한 오류가 있을 수 있습니다 ♨

 

 

○ Windows 기반 Thread

 

▣ 커널 오브젝트(Kernel Object)

- 프로세스, 쓰레드, 파일, 세마포어, 뮤텍스 등은 운영체제가 만드는 리소스(Resource)다

-> 운영체제(Windows)가 생성해서 관리하는 리소스

 

- 이런 운영체제의 의해서 생서오디는 리소스들은 관리를 목적으로 정보를 기록하기 위해 내부적으로 데이터 블록을 생성(like 구조체 변수)

-> 리소스 마다 유지해야 하는 정보가 다르니, 데이터 블록의 형태는 리소스마다 차이가 있다

=> 이 데이터 블록이 '커널 오브젝트'

 

예) 윈도우 상에서 mydata.txt파일 생성

- 윈도우는 이 파일을 관리하기 위해 데이터 블록(커널 오브젝트) 생성

 

 

▣ 커널 오브젝트의 소유자는 운영체제

- 커널 오브젝트의 생성, 관리 그리고 소멸지점을 결정하는 것까지 모두 운영체제의 몫이다

 

▣ 프로세스와 쓰레드의 관계

- 프로그램이 시작될 때 main함수를 호출하는 것은 main 쓰레드이다

- 쓰레드를 별도로 생성하지 않는 프로그램 : 단일 쓰레드 모델의 프로그램

예) select 기반의 서버

 

- 쓰레드를 별도로 생성하는 프로그램 : 멀티 쓰레드 모델의 프로그램

 

-> main 함수를 호출하는 것은 쓰레드이고, 그 쓰레드를 담고 있는 것이 프로세스

 

 

▣ 윈도우에서의 쓰레드 생성방법

- CreateThread함수에 의해서 쓰레드가 생성되면 운영체제는 이 쓰레드의 관리를 위한 커널 오브젝트를 생성한다

-> 커널 오브젝트의 구분자(ID) 역할을 하는 정수로 표현되는 '핸들'이 반환된다(∴ CreateThread함수의 반환형은 HANDLE 타입)

 

 

#include <windows.h>

 

HANDLE CreateThread(-> 성공 시 쓰레드 핸들, 실패 시 NULL 반환

LPSECURITY_ATTRIBUTES lpThreadAttributes, ①

SIZE_T dwStackSize,

LPTHREAD_START_ROUTINE lpStartAddress,

LPVOID lpParameter,

DWORD dwCreationFlags,

LPDWORD lpThreadId

);

 

① lpThreadAttributes : 쓰레드의 보안관련 정보전달, 디폴트 보안설정을 위해서 NULL 전달

② dwStackSize : 쓰레드에게 할당할 스택의 크기를 전달, 0 전달하면 디폴트 크기의 스택 생성

③ lpStartAddress : 쓰레드의 main 함수정보 전달

④ lpParameter : 쓰레드의 main 함수호출 시 전달할 인자정보 전달

⑤ dwCreationFlags : 쓰레드 생성 이후의 행동을 결정, 0을 전달하면 생성과 동시에 실행 가능한 상태가 된다

⑥ lpThreadId : 쓰레드의 ID의 저장을 위한 변수의 주소 값 전달

 

 

-> 책에서 신경쓰고 있는 부분은 lpStartAddress와 lpParameter 두 가지 (나머지는 다 0 또는 null을 전달)

※ 윈도우 쓰레드의 소멸 시점

- 스레드에 의해서 처음 호출된, 쓰레드의 main 함수가 반환하는(종료되는) 시점

 

 

▣ 멀티 쓰레드 기반의 프로그램 작성을 위한 환경설정

- VC++상에서는 'C/C++ Runtime Library(이하 CRT)'라는 것을 지정해줘야 한다 -> C/C++ 표준함수의 호출에 필요한 라이브러리

[프로젝트] > [속성] > [C/C++] > [코드 생성(Code Generation)] > [런타임 라이브러리(Runtime Library)] > '다중 스레드 디버그 DLL(/MDd)' 설정

 

 

▣ 쓰레드에 안전한 C 표준함수의 호출을 위한 쓰레드 생성

- 앞서 나온 CreateThread를 통해 만들어진 쓰레드를 통해서 C/C++ 표준함수를 호출하면 안정적으로 동작하지 않는다

이를 대체하는(C/C++ 표준함수에 안정적으로 동작시킬 수 있는 쓰레드를 생성하려면) ' _beginthreadex '함수를 호출해야 한다

 

#include process.h

 

uintptr_t _beginthreadex(-> 성공 시 쓰레드 핸들, 실패 시 0 반환

void* security,

unsigned stack_size,

unsigned ( *start_address )( void* ),

void* arglist,

unsigned initflag,

unsigned* thrdaddr

);

 

 

-> CreateThread함수와 비교하면 각 매개변수가 지닌 의미와 순서가 동일하다

(이름과 자료형에 조금 차이난다)

 

※ _beginthreadex함수 이전에 정의된 _beginthread함수

- _beginthread함수는 쓰레드 생성시 반환되는 핸들을 무효화시켜 커널 오브젝트에 접근할 수 있는 방법을 막아버란다

-> 그 대신에 나온게 _beginthreadex함수

∴ ex 붙은 거로 쓰자

 

※※ _beginthreadex의 반환형인 uintptr_t는 64비트로 표현되는 unsigned 정수 자료형이다

 

 

예) thread_kun.c

 

#include <stdio.h>

#include <windows.h>

#include <process.h>// _beginthreadex, _endthreadex를 위해 추가

 

unsigned WINAPI ThreadFunc(void* arg);

 

int main(int argc, char* argv[])

{

HANDLE hThread;

unsigned threadID;

int param = 5;

 

hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)&param, 0, &threadID);

/* 쓰레드의 main함수로 ThreadFunc를, ThreadFunc의 인자로 param의 주소값을 전달하면서 쓰레드의 생성을 요구 */

 

if (hThread == 0)

{

puts("_beginthreadex() error");

return -1;

}

 

Sleep(3000);// 1/1000초(나노초) 단위로 블로킹 상태를 만든다 -> 3초 대기시간

puts("end of main");

return 0;

}

 

unsigned WINAPI ThreadFunc(void* arg)// WINAPI라는 윈도우 고유의 키워드 -> 매개변수의 전달바향, 할당된 스택의 반환방법 등을 포함하는 함수의 호출규약을 명시해 놓은 것

{// _beginthreadex 함수가 요구하는 호출규약을 지키기 위해 삽입한 것

int cnt = *((int*)arg);

for (int i = 0; i < cnt; i++)

{

Sleep(1000);

puts("running thread");

}

 

return 0;

}

 

 

▣ 커널 오브젝트의 상태, 그리고 상태의 확인

- 커널 오브젝트에 담겨진 해당 리소스에 대한 리로스 중 중요한 것 중 하나가 '상태(state)'

예) 쓰레드의 종료여부 : 종료된 상태 = 'signaled 상태' / 종료되지 않은 상태 = 'non-signaled 상태'

 

- 운영체제는 프로세스나 쓰레드가 종료되면 해당 커널 오브젝트를 'signaled 상태'로 변경한다 (∵ 프로세스와 쓰레드의 초기 상태는 'non-signaled 상태')

-> signaled / non-signaled는 boolean형 변수로 표현된다

signaled : true

non-signaled : false

 

- 커널 오브젝트의 상태값을 물어 main 쓰레드가 생성된 쓰레드의 main함수가 종료되기 전까지 기다리게 할 수 있다

-> WaitForSingleObject & WaitForMultileObjects

 

 

▣ WaitForSingleObject & WaitForMultileObjects

- 하나의 커널 오브젝트에 대해서 signaled 상태인지를 확인하기 위해서 호출하는 함수

 

#include <windows.h>

 

DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);-> 성공 시 이벤트 정보, 실패 시 WAIT_FAILED 반환

 

① hHandle : 상태확인의 대상이 되는 커널 오브젝트의 핸들을 전달

② dwMilliseconds : 1/1000초 단위로 타임아웃을 지정,

인자로 INFINITE 전달 시, 커널 오브젝트가 signaled 상태가 되기 전에는 반환되지 않는다

③ DWORD(반환값) : signaled 상태로 인한 반환 시, WAIT_OBJECT_0 반환, 타임아웃으로 인한 반환 시 WAIT_TIMEOUT 반환

 

 

-> 이벤트 발생에 의해서(signaled 상태가 되어서) 반환되면, 해당 커널 오브젝트를 다시 non-signaled 상태로 되돌리기도 한다

=> 다시 non-signaled 상태가 되는 커널 오브젝트를 가르켜 'auto-reset 모드' 커널 오브젝트라 하고

자동으로 non-signaled 상태가 되지 않는 커널 오브젝트를 가르켜 'manual-reset 모드' 커널 오브젝트라 한다

 

 

- 둘 이상의 커널 오브젝트를 대상으로 상태를 확인하는 경우에 호출하는 함수

 

#include <windows.h>

 

DWORD WaitForMultipleObjects(

DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliseconds);

 

① nCount : 상태확인의 대상이 되는 커널 오브젝트의 핸들을 전달

② lpHandles : 핸들정보를 담고 있는 배열의 주소 값 전달

③ bWaitAll : TRUE 전달 시, 모든 검사대상이 signaled 상태가 되어야 반환,

FALSE 전달 시, 검사대상 중 하나라도 signaled 상태가 되면 반환

④ dwMilliseconds : 1/1000초 단위로 타임아웃을 지정,

인자로 INFINITE 전달 시, 커널 오브젝트가 signaled상태가 되기 전까지 반환하지 않는다

 

 

예) WaitForSingleObject을 적용한 thread_kun.c

 

#include <stdio.h>

#include <windows.h>

#include <process.h>

 

unsigned WINAPI ThreadFunc(void* arg);

 

int main(int argc, char* argv[])

{

HANDLE hThread;

DWORD wr;

unsigned threadID;

int param = 5;

 

hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)&param, 0, &threadID);

 

if (hThread == 0)

{

puts("_beginthreadex() error");

return -1;

}

 

if ((wr = WaitForSingleObject(hThread, INFINITE)) == WAIT_FAILED)// WaitForSingleObject 함수호출을 통해서 쓰레드의 종료를 대기하고 있다

{

puts("thread wait error");

return -1;

}

 

 

printf("wait result : %s \n", (wr == WAIT_OBJECT_0) ? "signaled" : "time-out");// WaitForSingleObject 함수의 반환 값을 통해서 반환의 원일을 학인하고 있다

puts("end of main");

return 0;

}

 

unsigned WINAPI ThreadFunc(void* arg)

{

int cnt = *((int*)arg);

for (int i = 0; i < cnt; i++)

{

Sleep(1000);

puts("running thread");

}

 

return 0;

}

'TCP&IP 복습' 카테고리의 다른 글

TCP/IP 소켓 프로그래밍 복습3  (0) 2020.10.29
TCP/IP 소켓 프로그래밍 복습2  (0) 2020.10.28
TCP/IP 소켓 프로그래밍 복습 1  (0) 2020.10.26