SlideShare uma empresa Scribd logo
1 de 25
제프리 리처의
WINDOWS VIA C/C++
Chapter06 : 스레드의 기본
2018-01-02
신희민
스레드란 무엇인가?
프로세스와 스레드는 매우 유사한 모양으로 구성되어 있다.
프로세스 : 프로세스 커널 오브젝트와 주소 공간으로 구성
스레드 : 스레드 커널 오브젝트와 스레드 스택으로 구성
프로세스는 스스로 수행 될 수 없다.
단순히 생각한다면 스레드의 저장소
스레드는 항상 프로세스의 컨텍스트 내에 생성되며, 프로세스 안에서 살아 있다.
=스레드는 프로세스의 주소 공간 내에 있는 코드를 수행하고 데이터를 다룬다.
스레드란 무엇인가?
하나의 프로세스 내에 둘 이상의 스레드가 존재하는 경우 스레드들은 단일 주소 공간을 공유하게 된다.
- 스레드들은 동일한 코드를 수행할 수도 있고, 동일 데이터를 조작할 수 있다.
- 커널 오브젝트 핸들 테이블은 프로세스별로 존재하기 때문에 스레드들은 커널 오브젝트 핸들도 공유
프로세스는 자신만의 주소 공간을 가지기 때문에 스레드에 비해 더욱 더 많은 시스템 리소스를 사용
개별 프로세스는 상당량의 정보를 시스템 내부에 저장해 두기 때문에 메모리를 많이 필요로 한다.
반면 스레드는 프로세스에 비해 상당히 적은 시스템 리소스를 필요로 한다.
스레드는 프로세스에 비해 부하가 적기 때문에,
프로세스를 새로 생성하는 대신 추가적인 스레드를 생성하여 문제를 해결하려는 시도가 좋다.
스레드를 생성해야 하는 경우
대부분의 컴퓨터는 CPU라는 매우 강력한 리소스를 가지고 있음.
CPU를 유휴 상태로 놔둬야 할 필요는 없으며, CPU가 작업을 수행할 수 있도록 해주는 것이 좋다.
CPU를 놀게 하기 싫을 때 스레드를 생성!
처음으로 작성하는 스레드 함수
모든 스레드는 수행을 시작할 진입점 함수를 반드시 가져야 한다.
프로세스 내에 두 번째 스레드를 만들려면 새로 생성되는 스레드는 아래와 같은 진입점 함수를 거쳐야함.
- 스레드 함수는 우리가 원하는 작업은 어떤 것이라도수행
- 스레드 함수는 언젠가는 끝날 것이고 반환될 것
- 스레드 함수가 반환되는 시점에 스레드는 수행을 멈추고 스택 반환
- 스레드 커널 오브젝트 사용 카운트 감소
- 스레드 커널 오브젝트 사용 0이 되면 스레드 커널 오브젝트 파괴
스레드를 생성해야 하는 경우
스레드 함수는 반드시 값을 반환해야 한다
- 이 값은 나중에 스레드의 종료 코드가 된다.
스레드 함수는 가능한 함수로 전달된 매개변수와 지역 변수만을 사용하도록 작성되는 것이 좋다.
- 정적 변수나 전역 변수를 사용하게 되면 다수의 스레드가 동시에 변수에 접근 할 수 있게 되며,
변수의 값이 잘못 변경되는 원인이 되기도 한다.
CreateThread 함수
두 번째 스레드를 생성하고 싶다면 이미 수행중인 스레드 내에서 CreateThread를 호출
CreateThread가 호출되면 시스템은 스레드 커널 오브젝트 생성
시스템은 스레듣가 사용할 스택 확보
새로운 스레드는 스레드를 생성한 프로세스와 동일한 컨텍스트 내에서 수행
새로운 스레드는 모든 스레드의 스택에 조차 접근 가능
= 동일 프로세스 내의 스레드들은 손쉽게 상호 통신
CreateThread 함수
PSECURITY_ATTRIBUTES psa
- SECURITY_ATTRIBUTES 구조체를 가리키는 포인터
- 스레드 커널 오브젝트에 대해 기본 보안 특성을 사용할 것이라면 NULL 전달
- Child proces로 해당 스레드 커널 오브젝트 핸들을 상송하도록 하려면,
SECURITY_ATTRIBUTES 구조체의 bInhertHandle멤버를 TRUE로 초기화.
DWORD cbStackSize,
- 스레드가 자신의 스택을 위해 얼마만큼의 주소 공간을 사용할지를 지정
- StackSize 0 이외의 값을 지정하면 함수는 스레드 스택을 확보하기 위해 지정된
- 크기의 메모리를 예약하고 커밋
- 만일 시스템이 스택의 최대 크기를 제한하지 않는다면 재귀호출은 절대로 종료되지 않음
CreateThread 함수
PTHREAD_START_ROUTINE pfnStartAddr
- pfnStartAddr 매개변수는 새로이 생성되는 스레드가 호출할 스레드 함수의 주소를 가르킴
PVOID pvParam
- CreateThread 함수의 pvParam 매개변수로 전달한 값이 그대로 전달
- CreateThread 함수는 스레드가 시작될 때 이 매개변수를 스레드 함수에 전달하는 것 외에
다른 용도로 사용하지 않는다.
CreateThread 함수
DWORD dwCreateFlags
- dwCreateFlags 매개변수는 스레드를 생성할 때 세부적인 제어를 수행하기 위한 추가적인 플래그를 지정하는데 사용
- 값으로 0을 전달하면 스레드는 생성되는 즉시 CPU에 의해 스케줄이 가능하게 됨
- 값으로 CREATE_SUSPENDED를 전달하면 시스템은 스레드를 생성하고 초기화를 완료 한 후 일시 정지 상태 유지
PDWORD pdwThreadID
- pdwThreadID에는 새로운 스레드에 할당되는 스레드 ID값을 저장할 DWORD 변수를
가리키는 주소로 지정하면 된다.
스레드의 종료
스레드는 4가지 방법으로 종료 될 수 있다.
1. 스레드 함수가 반환된다. (이 방법을 강력히 추천)
2. 스레드 함수 내에서 ExitThread 함수를 호출한다. (비추천)
3. 동일한 프로세스나 다른 프로세스에서 TerminateThread 함수를 호출한다. (비추천)
4. 스레드가 포함된 프로세스가 종료된다. (비추천)
1. 스레드 함수 반환
항상 스레드 함수가 반환되도록 설계하는 것이 좋다. 이것은 스레드가 사용한 자원을 적절히
정리할 수 있는 유일한 방법이다.
- 스레드 함수 내에서 생성한 C++ 오브젝트들은 파괴자를 통해 적절히 제거
- 운영체제는 스레드 스택으로 사용하였던 메모리를 반환
- 시스템은 스레드의 종료 코드를 스레드 함수의 반환값으로 설정
- 시스템은 스레드 커널 오브젝트의 사용 카운트를 감소
2. ExitThread 함수
ExitThread 함수를 호출하여 스레드를 강제로 종료 할 수 있다.
- 스레드를 강제로 종료하고 운영 체제가 스레드에서 사용했던 모든 운영체제 리소스를 정리하도록 함
- C/C++ 리소스는 정리되지 않는다.
- ExitThread 함수를 호출하기 보다는 스레드 함수를 반환하도록 하는게 더 좋다.
- ExitThread 함수는 반환되지 않는 함수이기 때문에 이후에 나온은 코드는 수행되지 않는다.
-
3. TerminateThread 함수
TerminateThread 함수를 호출하여 스레드를 종료 할 수 있다.
- ExitThread 함수가 이 함수를 호출하는 스레드를 종료하는 것과는 다르게 어떠한 스레드라도 종료
- 스레드가 종료되면 스레드의 종료 코드는 dwExitCode 매개변수로 전달한 값으로 설정
스레드 커널 오브젝트의 사용 카운트는 감소
- 잘 설계된 애플리케이션이라면 절대로 이 함수를 호출 하지 않는다.
- 이 함수를 호출하면 종료될 스레드는 자신이 곧 종료될 것이라는 사실을 전달받지 못하기 때문
-> 적절한 정리 작업을 수행할 수 없고, 종료 자체를 회피할 수도 없다.
4. 프로세스가 종료되면
- 프로세스가 소유한 모든 스레드 종료
- ExitProcess와 TerminateProcess 함수를 호출하는 경우에도 스레드는 종료
- 차이점은 이러한 함수들을 호출하면 프로세스가 소유하고 있던 모든 스레드가 종료
- 전체 프로세스가 종료되기 때문에 프로세스가 사용하던 리소스들도 모두 정리
- 스레드들이 사용하던 스택들도 정리
- 프로세스를 강제 종료하면 프로세스 내에 남아있는 스레드들에 대해 각각 TerminateThread 함수 호출
- 프로세스를 이와 같이 종료하게 되면 C++ 파괴자가 호출되지도 못하고 자료정리도 수행되지 않음.
5. 스레드가 종료되면
- 스레드가 소유하고 있던 모든 유저 오브젝트 핸들이 삭제
- 스레드의 종료 코드는 STILL_ACTIVE에서 ExitThead나 TerminateThread에서 지정한 종료 코드로 변경
- 스레드 커널 오브젝트의 상태가 시그널 상태로 변경
- 종료되는 스레드가 프로세스 내의 마지막 활성 스레드라면 시스템은 프로세스도 같이 종료되어야 함
- 스레드 커널 오브젝트의 사용 카운트가 1만큼 감소
스레드의 내부
스레드 커널 오브젝트
다른 속성과 통계 정보
사용 카운트 = 2
정지 카운트 = 1
종료 코드 = STILL_ACTIVE
시그널 상태 = FALSE
컨텍스트
SP
IP
다른 CPU 레지스터
스레드 스택
pvParam
pfnStartAddr
.
.
.
NTDLL.dll
VOID RtlUserThreadStart(…)
{
…
}
스레드의 내부
CreateThread 함수가 호출되면 시스템은 스레드 커널 오브젝트 생성
스레드 커널 오브젝트가 생성되면 스레드 스택으로 활용할 메모리 공간을 할당
스레드는 자신만의 가상 메모리 공간을 가지지 않으므로 스택으로 활용할 메모리는
프로세스의 주소 공간으로 할당
이후 시스템은 새로 생성된 스레드 스택의 가장 상위에 두 개의 값을 기록
스레드의 내부
각 스레드는 자신만의 CPU 레지스터 세트를 가지는데, 이를 스레드 컨텍스트라고 부름
이러한 컨텍스트는 스레드가 마지막으로 수행되었을 당시의 스레드 CPU 레지스터값을 가짐
인스터럭션 포인터(IP) 레지스터와 스택 포인터(SP) 레지스터
- 스레드 컨텍스트에 저장되는 값 중 가장 중요한 레지스터
- 스레드는 항상 프로세스의 컨텍스트 내부에서 수행
->프로세스 메모리 공간 상의 특정 위치를 가르킴
- 스레드 커널 오브젝트가 초기화되면 CONTEXT 구조체 내의 스택 포인터 레지스터는
pfnStartAddr를 저장하고 있는 스레드 스택의 주소로 설정
스레드의 내부
- 스레드의 초기화가 완료되면 시스템은 CreateThead 함수 호출 시 CREATE_SUSPENDED 플래그가
전달 되었는지 확인
- 스레드가 CPU 시간을 얻으면 시스템은 스레드 컨텍스트에 마지막으로 저장된 값을 CPU 레지스터로 로드
프로세스 주소 공간 내에 있는 코드를 수정하고 데이터를 변경하는 등의 작업을 한다.
- 새로운 스레드의 IP가 RtlUserThreadStart로 설정 -> 실질적으로 수행하는 최초 위치
- RtlUserThreadStart함수는 시작 코드를 호출하여 각종 초기화를 진행하고 진입점 함수 호출
- RtlUserThreadStart 내에서 스레드가 ExitThread나 ExitProcess를 호출한다는 사실
-> 스레드는 RtlUserThreadStart로 부터 반환되지 못하고 내부적으로 종료
C/C++ 런타임 라이브러리에 대한 고찰
- 멀티 스레드 기반의 C/C++ 프로그램이 정상적으로 동작하려면 C/C++ 런타임 라이브러리 함수들을 사용하는
각 스레드별로 적절한 구조의 데이터 블록을 생성해야 한다.
_beginthreadex
- 운영체제는 새로운 스레드가 생성되었을 때 어떻게 새로운 데이터 블록을 할당해야 할 지 모른다.
- C/C++ 런타임 라이브러리가 제공하는 _beginthreadex를 호출
_beginthreadex 함수의 특징
- 각 스레드는 C/C++ 라이브러리 힙에 _tiddata메모리 블록을 가짐
- _beginthreadex 함수에 전달된 스레드 함수의 주소는 _tiddqata 메모리 블록 내에 저장
- 내부적으로 CreateThread함수를 호출
- 정상적인 경우 CreateThread와 동일하게 스레드 핸들을 반환하며, 문제가 있을 시 0을 반환
C/C++ 런타임 라이브러리에 대한 고찰
_threadstartex
- 새로 생성된 스레드는 RtlUserThreadStart를 호출하고 _threadstartex로 진입
- 새로 생성된 스레드의 _tiddata블록을 가리키는 주소가 매개변수로 전달
- 사용자 정의 스레드 함수가 전달한 매개변수 값으로 호출
- 사용자가 지정한 스레드 함수의 반환값은 스레드의 종료 코드가 됨.
- _callthreadstartex는 단순히 _threadstartex로 반환되고, 계속해서 RtlUserThreadStart로 반환되는 구조가
아님에 주목. 만일 그렇게 되면 스레드는 종료되고, 스레드의 종료 코드는 올바르게 설정될지 모르겠지만,
_tiddata 메모리 블록은 해제되지 않을 것이다. -> 메모리 누수를 발생시킨다.
C/C++ 런타임 라이브러리에 대한 고찰
_endtreadex
- c/c++ 런타임 라이브러리 함수인 _getpid_noexit 함수는 이 함수를 호출하는 스레드의
_tiddata 메모리 블록을 가져오기 위해 내부적으로 운영체제의 TlsGetValue 함수를 호출
- _tiddata 블록이 삭제되고 운영체제의 ExitThread함수가 호출되어 스레드를 파괴
이 과정에서 종료 코드가 전달되고 올바르게 설정
- C/C++ 런타임 라이브러리가 제공하는 _beginthreadex를 호출해주면 됨.
자신의 구분자 얻기
- 스레드가 수행되면 종종 자신의 수행 환경을 변경하기 위해 윈도우 함수를 호출해야 함
- 스레드가 자신의 수행 환경을 변경하는 것은 매우 일반적인 작업이기 때문에 윈도우 운영체제는
스레드가 자신을 소유하는 프로세스의 커널 오브젝트나 자신을 나타내는 스레드 커널 오브젝트를
손 쉽게 얻을 수 있는 함수를 제공
- 이 두개의 함수는 해당 함수를 호출한 스레드를 소유하고 있는 프로세스나 스레드 자신을 나타내는
스레드 커널 오브젝트의 허위 핸들을 반환
허위 핸들을 실제 핸들로 변경하기
- 때때로 허위 핸들 대신 실제 핸들 값을 얻어 와야 할 때도 있다.
- 스레드의 허위 핸들은 항시 현재 스레드의 핸들이다
= 허위 핸들은 함수를 호출한 스레드 자신을 나타낸다.
- 이 문제를 해결하려면 허위 핸들을 실제 핸들로 변경 해야 한다.
DuplicateHandle 함수를 이용하면 이러한 변경 작업을 수행 할 수 있다

Mais conteúdo relacionado

Mais procurados

모어이펙티브 C++ 3,4장 예외, 효율 스터디
모어이펙티브 C++ 3,4장 예외, 효율 스터디모어이펙티브 C++ 3,4장 예외, 효율 스터디
모어이펙티브 C++ 3,4장 예외, 효율 스터디quxn6
 
Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약Nam Hyeonuk
 
이펙티브 C++ 공부
이펙티브 C++ 공부이펙티브 C++ 공부
이펙티브 C++ 공부quxn6
 
파이썬 크롤링 모듈
파이썬 크롤링 모듈파이썬 크롤링 모듈
파이썬 크롤링 모듈Yong Joon Moon
 
동기화, 스케줄링
동기화, 스케줄링동기화, 스케줄링
동기화, 스케줄링xxbdxx
 
Python+numpy pandas 3편
Python+numpy pandas 3편Python+numpy pandas 3편
Python+numpy pandas 3편Yong Joon Moon
 
Python+numpy pandas 1편
Python+numpy pandas 1편Python+numpy pandas 1편
Python+numpy pandas 1편Yong Joon Moon
 
Python_numpy_pandas_matplotlib 이해하기_20160815
Python_numpy_pandas_matplotlib 이해하기_20160815Python_numpy_pandas_matplotlib 이해하기_20160815
Python_numpy_pandas_matplotlib 이해하기_20160815Yong Joon Moon
 
05_STL컨테이너정리
05_STL컨테이너정리05_STL컨테이너정리
05_STL컨테이너정리noerror
 
Windows via c/c++ 스터디9장
Windows via c/c++ 스터디9장Windows via c/c++ 스터디9장
Windows via c/c++ 스터디9장HolyTak
 
이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디quxn6
 
이펙티브 C++ 스터디
이펙티브 C++ 스터디이펙티브 C++ 스터디
이펙티브 C++ 스터디quxn6
 
More effective c++ chapter1,2
More effective c++ chapter1,2More effective c++ chapter1,2
More effective c++ chapter1,2문익 장
 
[C++adv] STL 사용법과 주의 사항
[C++adv] STL 사용법과 주의 사항[C++adv] STL 사용법과 주의 사항
[C++adv] STL 사용법과 주의 사항MinGeun Park
 
PINTOS Operating system homework
PINTOS Operating system homeworkPINTOS Operating system homework
PINTOS Operating system homeworkGichan Lee
 
Jupyter notebook 이해하기
Jupyter notebook 이해하기 Jupyter notebook 이해하기
Jupyter notebook 이해하기 Yong Joon Moon
 
네트워크 프로그래밍 입출력 다중화 & 논블록소켓
네트워크 프로그래밍 입출력 다중화 & 논블록소켓네트워크 프로그래밍 입출력 다중화 & 논블록소켓
네트워크 프로그래밍 입출력 다중화 & 논블록소켓Eutark Park
 
Effective c++chapter8
Effective c++chapter8Effective c++chapter8
Effective c++chapter8성연 김
 

Mais procurados (20)

모어이펙티브 C++ 3,4장 예외, 효율 스터디
모어이펙티브 C++ 3,4장 예외, 효율 스터디모어이펙티브 C++ 3,4장 예외, 효율 스터디
모어이펙티브 C++ 3,4장 예외, 효율 스터디
 
Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약
 
이펙티브 C++ 공부
이펙티브 C++ 공부이펙티브 C++ 공부
이펙티브 C++ 공부
 
파이썬 크롤링 모듈
파이썬 크롤링 모듈파이썬 크롤링 모듈
파이썬 크롤링 모듈
 
동기화, 스케줄링
동기화, 스케줄링동기화, 스케줄링
동기화, 스케줄링
 
Python+numpy pandas 3편
Python+numpy pandas 3편Python+numpy pandas 3편
Python+numpy pandas 3편
 
Python+numpy pandas 1편
Python+numpy pandas 1편Python+numpy pandas 1편
Python+numpy pandas 1편
 
120114 windows viacpp_03
120114 windows viacpp_03120114 windows viacpp_03
120114 windows viacpp_03
 
Gpg1
Gpg1Gpg1
Gpg1
 
Python_numpy_pandas_matplotlib 이해하기_20160815
Python_numpy_pandas_matplotlib 이해하기_20160815Python_numpy_pandas_matplotlib 이해하기_20160815
Python_numpy_pandas_matplotlib 이해하기_20160815
 
05_STL컨테이너정리
05_STL컨테이너정리05_STL컨테이너정리
05_STL컨테이너정리
 
Windows via c/c++ 스터디9장
Windows via c/c++ 스터디9장Windows via c/c++ 스터디9장
Windows via c/c++ 스터디9장
 
이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디
 
이펙티브 C++ 스터디
이펙티브 C++ 스터디이펙티브 C++ 스터디
이펙티브 C++ 스터디
 
More effective c++ chapter1,2
More effective c++ chapter1,2More effective c++ chapter1,2
More effective c++ chapter1,2
 
[C++adv] STL 사용법과 주의 사항
[C++adv] STL 사용법과 주의 사항[C++adv] STL 사용법과 주의 사항
[C++adv] STL 사용법과 주의 사항
 
PINTOS Operating system homework
PINTOS Operating system homeworkPINTOS Operating system homework
PINTOS Operating system homework
 
Jupyter notebook 이해하기
Jupyter notebook 이해하기 Jupyter notebook 이해하기
Jupyter notebook 이해하기
 
네트워크 프로그래밍 입출력 다중화 & 논블록소켓
네트워크 프로그래밍 입출력 다중화 & 논블록소켓네트워크 프로그래밍 입출력 다중화 & 논블록소켓
네트워크 프로그래밍 입출력 다중화 & 논블록소켓
 
Effective c++chapter8
Effective c++chapter8Effective c++chapter8
Effective c++chapter8
 

Semelhante a Windows via c++ chapter6

Windows via C/C++ 06 스레드의 기본
Windows via C/C++ 06 스레드의 기본Windows via C/C++ 06 스레드의 기본
Windows via C/C++ 06 스레드의 기본ssuser0c2478
 
뇌자T etc.windows multi threading programming
뇌자T   etc.windows multi threading programming뇌자T   etc.windows multi threading programming
뇌자T etc.windows multi threading programmingcancan21st
 
쓰레드.pdf
쓰레드.pdf쓰레드.pdf
쓰레드.pdfSeokju Hong
 
07 스레드스케줄링,우선순위,그리고선호도
07 스레드스케줄링,우선순위,그리고선호도07 스레드스케줄링,우선순위,그리고선호도
07 스레드스케줄링,우선순위,그리고선호도ssuser3fb17c
 
android_thread
android_threadandroid_thread
android_threadhandfoot
 
스레드
스레드스레드
스레드xxbdxx
 
Linux programming study
Linux programming studyLinux programming study
Linux programming studyYunseok Lee
 
하둡 타입과 포맷
하둡 타입과 포맷하둡 타입과 포맷
하둡 타입과 포맷진호 박
 
파이썬 병렬프로그래밍
파이썬 병렬프로그래밍파이썬 병렬프로그래밍
파이썬 병렬프로그래밍Yong Joon Moon
 
세션1. block chain as a platform
세션1. block chain as a platform세션1. block chain as a platform
세션1. block chain as a platformJay JH Park
 
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요NAVER D2
 
Java mentoring of samsung scsc 2
Java mentoring of samsung scsc   2Java mentoring of samsung scsc   2
Java mentoring of samsung scsc 2도현 김
 
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화sung ki choi
 
OS Process, Thread, CPU Scheduling에 대해 알아봅시다.pdf
OS Process, Thread, CPU Scheduling에 대해 알아봅시다.pdfOS Process, Thread, CPU Scheduling에 대해 알아봅시다.pdf
OS Process, Thread, CPU Scheduling에 대해 알아봅시다.pdfHo Jeong Im
 
Vectorized processing in_a_nutshell_DeView2014
Vectorized processing in_a_nutshell_DeView2014Vectorized processing in_a_nutshell_DeView2014
Vectorized processing in_a_nutshell_DeView2014Gruter
 

Semelhante a Windows via c++ chapter6 (20)

Windows via C/C++ 06 스레드의 기본
Windows via C/C++ 06 스레드의 기본Windows via C/C++ 06 스레드의 기본
Windows via C/C++ 06 스레드의 기본
 
뇌자T etc.windows multi threading programming
뇌자T   etc.windows multi threading programming뇌자T   etc.windows multi threading programming
뇌자T etc.windows multi threading programming
 
쓰레드.pdf
쓰레드.pdf쓰레드.pdf
쓰레드.pdf
 
07 스레드스케줄링,우선순위,그리고선호도
07 스레드스케줄링,우선순위,그리고선호도07 스레드스케줄링,우선순위,그리고선호도
07 스레드스케줄링,우선순위,그리고선호도
 
android_thread
android_threadandroid_thread
android_thread
 
스레드
스레드스레드
스레드
 
Linux programming study
Linux programming studyLinux programming study
Linux programming study
 
04 프로세스
04 프로세스04 프로세스
04 프로세스
 
하둡 타입과 포맷
하둡 타입과 포맷하둡 타입과 포맷
하둡 타입과 포맷
 
파이썬 병렬프로그래밍
파이썬 병렬프로그래밍파이썬 병렬프로그래밍
파이썬 병렬프로그래밍
 
세션1. block chain as a platform
세션1. block chain as a platform세션1. block chain as a platform
세션1. block chain as a platform
 
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
 
ice_grad
ice_gradice_grad
ice_grad
 
Java mentoring of samsung scsc 2
Java mentoring of samsung scsc   2Java mentoring of samsung scsc   2
Java mentoring of samsung scsc 2
 
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화
제프리 리처의 Windows via C/C++ : 8장 유저 모드에서의 스레드 동기화
 
OS Process, Thread, CPU Scheduling에 대해 알아봅시다.pdf
OS Process, Thread, CPU Scheduling에 대해 알아봅시다.pdfOS Process, Thread, CPU Scheduling에 대해 알아봅시다.pdf
OS Process, Thread, CPU Scheduling에 대해 알아봅시다.pdf
 
Java stream v0.1
Java stream v0.1Java stream v0.1
Java stream v0.1
 
Java stream v0.1
Java stream v0.1Java stream v0.1
Java stream v0.1
 
Gcd ppt
Gcd pptGcd ppt
Gcd ppt
 
Vectorized processing in_a_nutshell_DeView2014
Vectorized processing in_a_nutshell_DeView2014Vectorized processing in_a_nutshell_DeView2014
Vectorized processing in_a_nutshell_DeView2014
 

Windows via c++ chapter6

  • 1. 제프리 리처의 WINDOWS VIA C/C++ Chapter06 : 스레드의 기본 2018-01-02 신희민
  • 2. 스레드란 무엇인가? 프로세스와 스레드는 매우 유사한 모양으로 구성되어 있다. 프로세스 : 프로세스 커널 오브젝트와 주소 공간으로 구성 스레드 : 스레드 커널 오브젝트와 스레드 스택으로 구성 프로세스는 스스로 수행 될 수 없다. 단순히 생각한다면 스레드의 저장소 스레드는 항상 프로세스의 컨텍스트 내에 생성되며, 프로세스 안에서 살아 있다. =스레드는 프로세스의 주소 공간 내에 있는 코드를 수행하고 데이터를 다룬다.
  • 3. 스레드란 무엇인가? 하나의 프로세스 내에 둘 이상의 스레드가 존재하는 경우 스레드들은 단일 주소 공간을 공유하게 된다. - 스레드들은 동일한 코드를 수행할 수도 있고, 동일 데이터를 조작할 수 있다. - 커널 오브젝트 핸들 테이블은 프로세스별로 존재하기 때문에 스레드들은 커널 오브젝트 핸들도 공유 프로세스는 자신만의 주소 공간을 가지기 때문에 스레드에 비해 더욱 더 많은 시스템 리소스를 사용 개별 프로세스는 상당량의 정보를 시스템 내부에 저장해 두기 때문에 메모리를 많이 필요로 한다. 반면 스레드는 프로세스에 비해 상당히 적은 시스템 리소스를 필요로 한다. 스레드는 프로세스에 비해 부하가 적기 때문에, 프로세스를 새로 생성하는 대신 추가적인 스레드를 생성하여 문제를 해결하려는 시도가 좋다.
  • 4. 스레드를 생성해야 하는 경우 대부분의 컴퓨터는 CPU라는 매우 강력한 리소스를 가지고 있음. CPU를 유휴 상태로 놔둬야 할 필요는 없으며, CPU가 작업을 수행할 수 있도록 해주는 것이 좋다. CPU를 놀게 하기 싫을 때 스레드를 생성!
  • 5. 처음으로 작성하는 스레드 함수 모든 스레드는 수행을 시작할 진입점 함수를 반드시 가져야 한다. 프로세스 내에 두 번째 스레드를 만들려면 새로 생성되는 스레드는 아래와 같은 진입점 함수를 거쳐야함. - 스레드 함수는 우리가 원하는 작업은 어떤 것이라도수행 - 스레드 함수는 언젠가는 끝날 것이고 반환될 것 - 스레드 함수가 반환되는 시점에 스레드는 수행을 멈추고 스택 반환 - 스레드 커널 오브젝트 사용 카운트 감소 - 스레드 커널 오브젝트 사용 0이 되면 스레드 커널 오브젝트 파괴
  • 6. 스레드를 생성해야 하는 경우 스레드 함수는 반드시 값을 반환해야 한다 - 이 값은 나중에 스레드의 종료 코드가 된다. 스레드 함수는 가능한 함수로 전달된 매개변수와 지역 변수만을 사용하도록 작성되는 것이 좋다. - 정적 변수나 전역 변수를 사용하게 되면 다수의 스레드가 동시에 변수에 접근 할 수 있게 되며, 변수의 값이 잘못 변경되는 원인이 되기도 한다.
  • 7. CreateThread 함수 두 번째 스레드를 생성하고 싶다면 이미 수행중인 스레드 내에서 CreateThread를 호출 CreateThread가 호출되면 시스템은 스레드 커널 오브젝트 생성 시스템은 스레듣가 사용할 스택 확보 새로운 스레드는 스레드를 생성한 프로세스와 동일한 컨텍스트 내에서 수행 새로운 스레드는 모든 스레드의 스택에 조차 접근 가능 = 동일 프로세스 내의 스레드들은 손쉽게 상호 통신
  • 8. CreateThread 함수 PSECURITY_ATTRIBUTES psa - SECURITY_ATTRIBUTES 구조체를 가리키는 포인터 - 스레드 커널 오브젝트에 대해 기본 보안 특성을 사용할 것이라면 NULL 전달 - Child proces로 해당 스레드 커널 오브젝트 핸들을 상송하도록 하려면, SECURITY_ATTRIBUTES 구조체의 bInhertHandle멤버를 TRUE로 초기화. DWORD cbStackSize, - 스레드가 자신의 스택을 위해 얼마만큼의 주소 공간을 사용할지를 지정 - StackSize 0 이외의 값을 지정하면 함수는 스레드 스택을 확보하기 위해 지정된 - 크기의 메모리를 예약하고 커밋 - 만일 시스템이 스택의 최대 크기를 제한하지 않는다면 재귀호출은 절대로 종료되지 않음
  • 9. CreateThread 함수 PTHREAD_START_ROUTINE pfnStartAddr - pfnStartAddr 매개변수는 새로이 생성되는 스레드가 호출할 스레드 함수의 주소를 가르킴 PVOID pvParam - CreateThread 함수의 pvParam 매개변수로 전달한 값이 그대로 전달 - CreateThread 함수는 스레드가 시작될 때 이 매개변수를 스레드 함수에 전달하는 것 외에 다른 용도로 사용하지 않는다.
  • 10. CreateThread 함수 DWORD dwCreateFlags - dwCreateFlags 매개변수는 스레드를 생성할 때 세부적인 제어를 수행하기 위한 추가적인 플래그를 지정하는데 사용 - 값으로 0을 전달하면 스레드는 생성되는 즉시 CPU에 의해 스케줄이 가능하게 됨 - 값으로 CREATE_SUSPENDED를 전달하면 시스템은 스레드를 생성하고 초기화를 완료 한 후 일시 정지 상태 유지 PDWORD pdwThreadID - pdwThreadID에는 새로운 스레드에 할당되는 스레드 ID값을 저장할 DWORD 변수를 가리키는 주소로 지정하면 된다.
  • 11. 스레드의 종료 스레드는 4가지 방법으로 종료 될 수 있다. 1. 스레드 함수가 반환된다. (이 방법을 강력히 추천) 2. 스레드 함수 내에서 ExitThread 함수를 호출한다. (비추천) 3. 동일한 프로세스나 다른 프로세스에서 TerminateThread 함수를 호출한다. (비추천) 4. 스레드가 포함된 프로세스가 종료된다. (비추천)
  • 12. 1. 스레드 함수 반환 항상 스레드 함수가 반환되도록 설계하는 것이 좋다. 이것은 스레드가 사용한 자원을 적절히 정리할 수 있는 유일한 방법이다. - 스레드 함수 내에서 생성한 C++ 오브젝트들은 파괴자를 통해 적절히 제거 - 운영체제는 스레드 스택으로 사용하였던 메모리를 반환 - 시스템은 스레드의 종료 코드를 스레드 함수의 반환값으로 설정 - 시스템은 스레드 커널 오브젝트의 사용 카운트를 감소
  • 13. 2. ExitThread 함수 ExitThread 함수를 호출하여 스레드를 강제로 종료 할 수 있다. - 스레드를 강제로 종료하고 운영 체제가 스레드에서 사용했던 모든 운영체제 리소스를 정리하도록 함 - C/C++ 리소스는 정리되지 않는다. - ExitThread 함수를 호출하기 보다는 스레드 함수를 반환하도록 하는게 더 좋다. - ExitThread 함수는 반환되지 않는 함수이기 때문에 이후에 나온은 코드는 수행되지 않는다. -
  • 14. 3. TerminateThread 함수 TerminateThread 함수를 호출하여 스레드를 종료 할 수 있다. - ExitThread 함수가 이 함수를 호출하는 스레드를 종료하는 것과는 다르게 어떠한 스레드라도 종료 - 스레드가 종료되면 스레드의 종료 코드는 dwExitCode 매개변수로 전달한 값으로 설정 스레드 커널 오브젝트의 사용 카운트는 감소 - 잘 설계된 애플리케이션이라면 절대로 이 함수를 호출 하지 않는다. - 이 함수를 호출하면 종료될 스레드는 자신이 곧 종료될 것이라는 사실을 전달받지 못하기 때문 -> 적절한 정리 작업을 수행할 수 없고, 종료 자체를 회피할 수도 없다.
  • 15. 4. 프로세스가 종료되면 - 프로세스가 소유한 모든 스레드 종료 - ExitProcess와 TerminateProcess 함수를 호출하는 경우에도 스레드는 종료 - 차이점은 이러한 함수들을 호출하면 프로세스가 소유하고 있던 모든 스레드가 종료 - 전체 프로세스가 종료되기 때문에 프로세스가 사용하던 리소스들도 모두 정리 - 스레드들이 사용하던 스택들도 정리 - 프로세스를 강제 종료하면 프로세스 내에 남아있는 스레드들에 대해 각각 TerminateThread 함수 호출 - 프로세스를 이와 같이 종료하게 되면 C++ 파괴자가 호출되지도 못하고 자료정리도 수행되지 않음.
  • 16. 5. 스레드가 종료되면 - 스레드가 소유하고 있던 모든 유저 오브젝트 핸들이 삭제 - 스레드의 종료 코드는 STILL_ACTIVE에서 ExitThead나 TerminateThread에서 지정한 종료 코드로 변경 - 스레드 커널 오브젝트의 상태가 시그널 상태로 변경 - 종료되는 스레드가 프로세스 내의 마지막 활성 스레드라면 시스템은 프로세스도 같이 종료되어야 함 - 스레드 커널 오브젝트의 사용 카운트가 1만큼 감소
  • 17. 스레드의 내부 스레드 커널 오브젝트 다른 속성과 통계 정보 사용 카운트 = 2 정지 카운트 = 1 종료 코드 = STILL_ACTIVE 시그널 상태 = FALSE 컨텍스트 SP IP 다른 CPU 레지스터 스레드 스택 pvParam pfnStartAddr . . . NTDLL.dll VOID RtlUserThreadStart(…) { … }
  • 18. 스레드의 내부 CreateThread 함수가 호출되면 시스템은 스레드 커널 오브젝트 생성 스레드 커널 오브젝트가 생성되면 스레드 스택으로 활용할 메모리 공간을 할당 스레드는 자신만의 가상 메모리 공간을 가지지 않으므로 스택으로 활용할 메모리는 프로세스의 주소 공간으로 할당 이후 시스템은 새로 생성된 스레드 스택의 가장 상위에 두 개의 값을 기록
  • 19. 스레드의 내부 각 스레드는 자신만의 CPU 레지스터 세트를 가지는데, 이를 스레드 컨텍스트라고 부름 이러한 컨텍스트는 스레드가 마지막으로 수행되었을 당시의 스레드 CPU 레지스터값을 가짐 인스터럭션 포인터(IP) 레지스터와 스택 포인터(SP) 레지스터 - 스레드 컨텍스트에 저장되는 값 중 가장 중요한 레지스터 - 스레드는 항상 프로세스의 컨텍스트 내부에서 수행 ->프로세스 메모리 공간 상의 특정 위치를 가르킴 - 스레드 커널 오브젝트가 초기화되면 CONTEXT 구조체 내의 스택 포인터 레지스터는 pfnStartAddr를 저장하고 있는 스레드 스택의 주소로 설정
  • 20. 스레드의 내부 - 스레드의 초기화가 완료되면 시스템은 CreateThead 함수 호출 시 CREATE_SUSPENDED 플래그가 전달 되었는지 확인 - 스레드가 CPU 시간을 얻으면 시스템은 스레드 컨텍스트에 마지막으로 저장된 값을 CPU 레지스터로 로드 프로세스 주소 공간 내에 있는 코드를 수정하고 데이터를 변경하는 등의 작업을 한다. - 새로운 스레드의 IP가 RtlUserThreadStart로 설정 -> 실질적으로 수행하는 최초 위치 - RtlUserThreadStart함수는 시작 코드를 호출하여 각종 초기화를 진행하고 진입점 함수 호출 - RtlUserThreadStart 내에서 스레드가 ExitThread나 ExitProcess를 호출한다는 사실 -> 스레드는 RtlUserThreadStart로 부터 반환되지 못하고 내부적으로 종료
  • 21. C/C++ 런타임 라이브러리에 대한 고찰 - 멀티 스레드 기반의 C/C++ 프로그램이 정상적으로 동작하려면 C/C++ 런타임 라이브러리 함수들을 사용하는 각 스레드별로 적절한 구조의 데이터 블록을 생성해야 한다. _beginthreadex - 운영체제는 새로운 스레드가 생성되었을 때 어떻게 새로운 데이터 블록을 할당해야 할 지 모른다. - C/C++ 런타임 라이브러리가 제공하는 _beginthreadex를 호출 _beginthreadex 함수의 특징 - 각 스레드는 C/C++ 라이브러리 힙에 _tiddata메모리 블록을 가짐 - _beginthreadex 함수에 전달된 스레드 함수의 주소는 _tiddqata 메모리 블록 내에 저장 - 내부적으로 CreateThread함수를 호출 - 정상적인 경우 CreateThread와 동일하게 스레드 핸들을 반환하며, 문제가 있을 시 0을 반환
  • 22. C/C++ 런타임 라이브러리에 대한 고찰 _threadstartex - 새로 생성된 스레드는 RtlUserThreadStart를 호출하고 _threadstartex로 진입 - 새로 생성된 스레드의 _tiddata블록을 가리키는 주소가 매개변수로 전달 - 사용자 정의 스레드 함수가 전달한 매개변수 값으로 호출 - 사용자가 지정한 스레드 함수의 반환값은 스레드의 종료 코드가 됨. - _callthreadstartex는 단순히 _threadstartex로 반환되고, 계속해서 RtlUserThreadStart로 반환되는 구조가 아님에 주목. 만일 그렇게 되면 스레드는 종료되고, 스레드의 종료 코드는 올바르게 설정될지 모르겠지만, _tiddata 메모리 블록은 해제되지 않을 것이다. -> 메모리 누수를 발생시킨다.
  • 23. C/C++ 런타임 라이브러리에 대한 고찰 _endtreadex - c/c++ 런타임 라이브러리 함수인 _getpid_noexit 함수는 이 함수를 호출하는 스레드의 _tiddata 메모리 블록을 가져오기 위해 내부적으로 운영체제의 TlsGetValue 함수를 호출 - _tiddata 블록이 삭제되고 운영체제의 ExitThread함수가 호출되어 스레드를 파괴 이 과정에서 종료 코드가 전달되고 올바르게 설정 - C/C++ 런타임 라이브러리가 제공하는 _beginthreadex를 호출해주면 됨.
  • 24. 자신의 구분자 얻기 - 스레드가 수행되면 종종 자신의 수행 환경을 변경하기 위해 윈도우 함수를 호출해야 함 - 스레드가 자신의 수행 환경을 변경하는 것은 매우 일반적인 작업이기 때문에 윈도우 운영체제는 스레드가 자신을 소유하는 프로세스의 커널 오브젝트나 자신을 나타내는 스레드 커널 오브젝트를 손 쉽게 얻을 수 있는 함수를 제공 - 이 두개의 함수는 해당 함수를 호출한 스레드를 소유하고 있는 프로세스나 스레드 자신을 나타내는 스레드 커널 오브젝트의 허위 핸들을 반환
  • 25. 허위 핸들을 실제 핸들로 변경하기 - 때때로 허위 핸들 대신 실제 핸들 값을 얻어 와야 할 때도 있다. - 스레드의 허위 핸들은 항시 현재 스레드의 핸들이다 = 허위 핸들은 함수를 호출한 스레드 자신을 나타낸다. - 이 문제를 해결하려면 허위 핸들을 실제 핸들로 변경 해야 한다. DuplicateHandle 함수를 이용하면 이러한 변경 작업을 수행 할 수 있다