SlideShare a Scribd company logo
1 of 34
Windows Via C/C++
  스레드의 기본


           아꿈사 오전반 윤석윤
       ( seedyoon@gmail.com )
개요
• 스레드란?
• 스레드를 생성해야 하는 경우
• 스레드를 생성하지 말아야 하는 경우
• 처음으로 작성하는 스레드 함수
• Create Thread 함수
• 스레드의 종류
• 스레드의 내부
• C/C++ 런타임 라이브러리에 대한 고찰
• 자신의 구분자 얻기
스레드란?
                 프로세스

                 코드
                전역 변수들
                프로세스 힙
           프로세스 자원들
            열린 파일들
             프로세스
              힙
              ….
                환경 블록
        스레드 1            스레드 2

         TLS              TLS

         스택               스택
스레드란?
• 프로세스와 스레드는 매우 유사한 모양으로 구성
  – 프로세스는 프로세스 커널 오브젝트와 주소공간으로 구성
  – 스레드는 스레드 커널 오브젝트와 스레드 스택( 16장 )으로 구성 되어 있
    다.


• 프로세스는 스스로 수행 될 수 없다( 4장 )
  – 껍데기일 뿐, 단순히 생각한다면 스레드의 저장소로 볼 수도 있다.
  – 스레드는 항상 프로세스의 컨텍스트 내에 생성되며, 프로세스 안에서
    살아 있을 수 있다.
  – 프로세스의 주소 공간 내에 있는 코드를 수행하고 데이터를 다룬다.
스레드란?
•   주소 공간의 공유
    – 스레드들은 동일한 코드를 수행할 수도 있고, 동일 데이터를 조작할 수도 있다.
    – 커널 오브젝트 핸들 테이블은 스레드 별로 존재하는 것이 아니라 프로세스별로 존재하
      기 때문에 커널 오브젝트 핸들도 역시 공유.



•   프로세스에 비해 가벼움
    – 프로세스는 자신만의 주소 공간을 가지기 때문에 스레드에 비해 더욱 많은 시스템
      리소스를 사옹
    – exe, dll파일이 주소 공간으로 로드되어야 하므로 파일 리소스 또한 필요
    – 반면 스레드는 하나의 커널 오브젝트와 스레드 스택 정도만을 필요로 할 뿐
    – 컨텍스트의 양이 적기 때문에 상대적으로 프로세스에 비해 전환이 빠르다.
스레드를 생성해야 하는 경우
• CPU를 놀게 하기 싫을 때!!
  – 모든 프로세스는 적어도 하나 이상의 스레드를
   가질 수 있고, 아주 특별한 작업을 수행하는 경우가 아닌 이상 운
   영체제가 다수의 스레드를 동시에 수행 할 수 있다는 것은 매우 많
   은 이점을 제공.
  – Visual Studio IDE, Vista의 인덱싱, 스프레드 시트의 백그라운드
   재계산 작업, 워드 프로세스의 철자 계산 등등
  – I/O 성능에 의해 결정되는 프로그램들의 성능을 향상 시킬 때
   (순차적 파일들을 처리 할 때에는 읽기 및 쓰기 연산을 한꺼번에
   최대한 많이 수행하는 것이 더 효율적)
스레드를 생성하지 말아야 하는 경
우
• 위험성과 문제점
 – 한 프로세스의 스레드들은 저장소와 자원들을 공유하므로,
  한 프로세스가 다른 스레드의 자료를 잘못 수정할 수 있다
  (경쟁조건이나 교착 같은 결함이 발생 할 수 있다)
 – 특정한 상황에서는 동시성이 성능을 향상시키는 게 아니라
  크게 떨어뜨리기도 한다.
 – 기존의 단일 스레드 프로그램을 스레드들을 활용하도록 변
  환하는 것이 어려울 수 있다. 위의 사항들이 이유일 수도 있
  고, 프로그램 이해 부족이 이유 일 수도 있다.(ㅜ_ㅜ)
처음으로 작성하는 스레드 함수
• 스레드의 진입점 함수 원형
  – 스레드 함수는 우리가 원하는 작업은 어떤 것이라도 수행할 수 있다.
  – 스레드 함수는 언젠가는 끝날 것이고 반환될 것이다.
  – 스레드 함수가 WINAPI ThreadFunc(VOID 수행을 멈추고
       DWORD 반환되는 시점에 스레드는 pvParam)
    스레드가 사용하던 스택도 반환된다.
       {
             DWORD dwResult = 0;
  – 스레드 커널 오브젝트의 사용 카운트도 감소한다.
             …
  – 이 값이 0이 되면 스레드 커널 오브젝트는 파괴된다.
             Return(dwResult);
       }
  – 프로세스 커널 오브젝트와 마찬가지로 스레드 커널 오브젝트 또한
   이를 통해 관리되는 스레드 만큼은 살아 있으며 스레드 종료 이후에도 여
   전히 살아 있을 수 있다.
처음으로 작성하는 스레드 함수
• 모든 스레드는 수행을 시작할 진입점 함수(entry point)를 반드시
 가져야 한다.
  – 스레드 함수는 우리가 원하는 작업은 어떤 것이라도 수행할 수 있다.
  – 스레드 함수는 언젠가는 끝날 것이고 반환될 것이다.
  – 스레드 함수가 반환되는 시점에 스레드는 수행을 멈추고
   스레드가 사용하던 스택도 반환된다.
  – 스레드 커널 오브젝트의 사용 카운트도 감소한다.
  – 이 값이 0이 되면 스레드 커널 오브젝트는 파괴된다.
  – 프로세스 커널 오브젝트와 마찬가지로 스레드 커널 오브젝트 또한
   이를 통해 관리되는 스레드 만큼은 살아 있으며 스레드 종료 이후에도 여
   전히 살아 있을 수 있다.
처음으로 작성하는 스레드 함수
• 스레드 함수는 반드시 값을 반환해야 한다.
    – 이 값은 나중에 스레드의 종료 코드가 되며, C/C++ 런타임 라이브
      러리가 주 스레드의 종료 코드를 프로세스의 종료 코드로 사용하
      는 것과 유사하다.


•   스레드 함수는 가능한 함수로 전달된 매개변수와 지역 변수만을
    사용하도록 작성되는 것이 좋다.
    – 정적(static)변수나 전역(global)변수를 사용하게 되면 다수의 스레드가 동시에 변
      수에 접근할 수 있게 되며, 이는 변수의 값이 잘못 변경되는 원인이 되기도 한다.
    – 하지만 함수의 매개변수와 지역변수는 스레드의 스택에 유지되기 때문에 다른 스
      레드에 의해 내용이 변경될 가능성이 거의 없다.
CreateThread 함수

• 결론
 – 쓰지말자. 그래도 알아보자.
CreateThread 함수



    HANDLE CreateThread(
    1.  LPSECURITY_ATTRIBUTES lpThreadAttributes,
    2.  SIZE_T dwStackSize,
    3.  LPTHREAD_START_ROUTINE lpStartAddress,
    4.  LPVOID lpParameter,
    5.  DWORD dwCreationFlags,
    6.  LPDWORD lpThreadId     );
CreateThread 함수
•   LPSECURITY_ATTRIBUTES
     – 스레드 커널 오브젝트에 대해 기본 보안 특성을 사용할 것이라면 이 매개변수로 NULL
        을 전달하면 된다.
     – 나머지는 취향것 MSDN 참조

•   StackSize
     – 스레드가 자신의 스택을 위해 얼마만큼의 주소 공간을 사용할지를 지정할 수 있다.
     – StackSize 0 이외의 값을 지정하면 함수는 스레드 스택을 확보하기 위해 지정된
        크기의 메모리를 예약하고 커밋까지 수행한다.
     – 만일 시스템이 스택의 최대 크기를 제한하지 않는다면 이러한 재귀호출은 절대로
        종료되지 않을 것이다.
     – /STACK:[reserve][,commit]
         •   reserve 인자는 시스템이 스레드스택을 위해 지정된 크기 만큼의 주소 공간을 예약(기본 1M)
         •   Commit 인자는 스택으로 예약된 주소 공간에 커밋된 물리적 저장소의 초기크기를 나타낸다.
스레드의 종료

• 스레드의 종료
 – 스레드 함수가 반환된다( Excellent!! )

 – 스레드 함수 내에서 ExitThread 함수를 호출한다

 – 동일한 프로세스나 다른 프로세스에서
  TerminateThread함수를 호출한다.

 – 스레드가 포함된 프로세스가 종료된다.
스레드의 종료

• 스레드 함수가 반환된다.
 – 항상 스레드 함수가 반환되도록 설계하는 것이
  좋다. 이것은 스레드가 사용한 자원을 적절하
  게 정리 할 수 있는 유일한 방법이다.
스레드의 종료

• 스레드 함수가 반환된다.


스레드 함수 내에서 생성한                   시스템은 스레드의 종료 코드를
                 운영체제는 스레드 스택으
모든 C++ 오브젝트들은                    스레드 함수의 반환 값으로 설정
                 로 사용했던 메모리 반환
파괴자를 통해 적절히 제거                   (스레드 커널 오브젝트에 저장)




                                   시스템은 스레드 커널 오브
                                   젝트의 사용 카운터를 감소
스레드의 종료

• ExitThread 함수
  – 스레드를 강제 종료(반환되지 않는 함수).

스레드를 강제 종료,                         시스템은 스레드의 종료 코드를
운영체제가 스레드에          운영체제는 스레드 스택으
                                    스레드 함수의 반환 값으로 설정
사용했던 모든 운영체제        로 사용했던 메모리 반환
                                    (스레드 커널 오브젝트에 저장)
리소스를 종료




         스레드 함수 내에서 생성한
         모든 C++ 오브젝트들은
         파괴자를 통해 적절히 제거              시스템은 스레드 커널 오브
                                     젝트의 사용 카운터를 감소
스레드의 종료

• TerminatedThread 함수
  – 어떠한 스레드라도 종료 가능(비동기 함수)

스레드를 강제 종료,
운영체제가 스레드에           시스템은 스레드의 종료 코드를
                                            시스템은 스레드 커널 오브
사용했던 모든 운영체제         스레드 함수의 반환 값으로 설정
                                            젝트의 사용 카운터를 감소
리소스를 종료              (스레드 커널 오브젝트에 저장)




          스레드 함수 내에서 생성한
                                운영체제는 스레드 스택으
          모든 C++ 오브젝트들은
                                로 사용했던 메모리 반환
          파괴자를 통해 적절히 제거
스레드의 종료

• 프로세스 종료시
 – 프로세스가 소유한 모든 스레드 종료
  • 프로세스가 사용중 이던 리소스들도 모두 정리

  • 프로세스 내에 남아 있던 스레드들에 대해 각각
   TerminatedThread 함수가 호출!!!!
스레드의 종료
•   스레드가 종료되면
    – 모든 유저 오브젝트 핸들이 삭제
       •   윈도우와 윈도우 훅 두 개의 사용자 오브젝트는 스레드에 의해 소유, 스레드가 종료되면 시스템
           은 자동적으로 해당 스레드가 생성한 윈도우를 파괴하고, 설치된 윈도우 훅을 제거한다.
       •   다른 형태의 오브젝트들은 모두 소유하고 있는 프로세스가 종료되는 시점에 파괴된다.

    – 스레드의 종료 코드는 STILL_ACTIVATE에서 exitThread나 TerminatedThread에서 지
      정한 종료코드로 변경
    – 스레드 커널 오브젝트의 상태가 시그널 상태로 변경
    – 종료되는 스레드가 프로세스 내의 마지막 활성 스레드라면 시스템은 프로세스도 같이
      종료되어야 하는 것으로 간주
    – 스레드 커널 오브젝트의 사용 카운트가 1만큼 감소

    스레드의 종료 여부를 확인하기 위해 어떻게 스레드 핸들을 사용할 지에
    대해서는 9장에서~
스레드의 내부
스레드 커널 오브젝트



                           스레드 스택
  컨텍스트

       SP                          pvParam


         IP                      pfnStartAddr


  다른 CPU 레지스터                            .
                                         .
                                         .




  다른속성과 통계정보

       사용 카운트 = 2
                             NTDLL.dll
       정지카운트 = 1
                             VOID RtlUserThreadStart(…){}
  종료 코드 = STILL_ACTIVATE

     시그널 상태 = FALSE
스레드의 내부
스레드 커널 오브젝트

                                                            사용카운트
                                                            CreateThread 함수가 반환한 핸들이
                           스레드 스택
                                                             제거될 때까지 파괴되지 않는다.)
  컨텍스트

       SP                          pvParam


         IP                      pfnStartAddr


  다른 CPU 레지스터                            .
                                         .
                                         .




  다른속성과 통계정보

       사용 카운트 = 2
                             NTDLL.dll
       정지카운트 = 1
                             VOID RtlUserThreadStart(…){}
  종료 코드 = STILL_ACTIVATE

     시그널 상태 = FALSE
스레드의 내부
스레드 커널 오브젝트

                                                            사용카운트
                                                            CreateThread 함수가 반환한 핸들이
                           스레드 스택
                                                             제거될 때까지 파괴되지 않는다.)
  컨텍스트

       SP                          pvParam


         IP                      pfnStartAddr

                                                              스레드 커널 오브젝트의 초기화
  다른 CPU 레지스터                            .
                                         .
                                         .




  다른속성과 통계정보

       사용 카운트 = 2
                             NTDLL.dll
       정지카운트 = 1
                             VOID RtlUserThreadStart(…){}
  종료 코드 = STILL_ACTIVATE

     시그널 상태 = FALSE
스레드의 내부
스레드 커널 오브젝트

                                                            사용카운트
                                                            CreateThread 함수가 반환한 핸들이
                           스레드 스택
                                                             제거될 때까지 파괴되지 않는다.)
  컨텍스트

       SP                          pvParam


         IP                      pfnStartAddr

                                                              스레드 커널 오브젝트의 초기화
  다른 CPU 레지스터                            .
                                         .
                                         .



                                                                스레드 스택으로 활용할
                                                                  메모리 공간 할당
  다른속성과 통계정보                                                    (프로세스 주소 내 공간)

       사용 카운트 = 2
                             NTDLL.dll
       정지카운트 = 1
                             VOID RtlUserThreadStart(…){}
  종료 코드 = STILL_ACTIVATE

     시그널 상태 = FALSE
스레드의 내부
스레드 커널 오브젝트

                                                            사용카운트
                                                            CreateThread 함수가 반환한 핸들이
                           스레드 스택
                                                             제거될 때까지 파괴되지 않는다.)
  컨텍스트

       SP                          pvParam


         IP                      pfnStartAddr

                                                              스레드 커널 오브젝트의 초기화
  다른 CPU 레지스터                            .
                                         .
                                         .



                                                                스레드 스택으로 활용할
                                                                  메모리 공간 할당
  다른속성과 통계정보                                                    (프로세스 주소 내 공간)

       사용 카운트 = 2
                             NTDLL.dll
       정지카운트 = 1                                              새로 생성된 스레드 스택의 가장
                             VOID RtlUserThreadStart(…){}        상위에 두 개의 값을 기록
  종료 코드 = STILL_ACTIVATE                                    1. pvParam
                                                            2. pfnStartAddr
     시그널 상태 = FALSE
스레드의 내부
스레드 커널 오브젝트

                                                            스레드 컨텍스트
                                                            스레드가 마지막으로 수행되었을 때
                           스레드 스택
                                                            당시의 CPU 레지스터 값을 가지고 있다.
  컨텍스트

       SP                          pvParam


         IP                      pfnStartAddr               인스트럭션 포인터(IP)와 스택 포인터(SP)
                                                            프로세스 컨텍스트 내부에서 수행되기 때문에,
                                                            프로세스 메모리 공간 상의 특정 위치를
  다른 CPU 레지스터                            .
                                         .                  가리키고 있다.
                                         .



                                                            스레드 커널 오브젝트가 초기화되면 Context구조
                                                            체 내의 스택 포인터 레지스터는 pfnStartAddr를
  다른속성과 통계정보                                                저장하고 있는 스레드 스택의 주소로 설정

       사용 카운트 = 2
                             NTDLL.dll
       정지카운트 = 1                                            IP는 NTDLL.dll 모듈이 익스포트 하고
                             VOID RtlUserThreadStart(…){}   있는 RtlUserThreadStart라는
  종료 코드 = STILL_ACTIVATE                                    문서화 되지 않은 함수의 주소를
                                                            가리키도록 설정
     시그널 상태 = FALSE
스레드의 내부
스레드 커널 오브젝트
                                                          스레드의 초기화가 완료되면
                                                          CREATE_SUSPENDED플래그가 전달되
                           스레드 스택                         었는지 확인, 전달되지 않았다면 정지 카
  컨텍스트                                                    운트를 0으로 설정 스레드가 프로세서에
                                   pvParam
                                                          스케줄될 수 있도록 한다.
       SP

         IP                      pfnStartAddr             스레드가 CPU 시간을 얻으면 시스템은 스레드
                                                          컨텍스트에 마지막으로 저장된 값을 CPU 레지
  다른 CPU 레지스터                            .                스터로 로드, 프로세스 주소 공간 내에 있는
                                         .                코드를 수정하고 데이터를 변경하는 등의
                                         .                작업을 수행한다.



                                                          새로운 스레드의 IP가 RtlUserThreadStart로 설정,
  다른속성과 통계정보                                              실질적으로 수행하는 최초 위치가 된다.

       사용 카운트 = 2
                             NTDLL.dll
       정지카운트 = 1
                               VOID RtlUserThreadStart    RtlUserThreadStart 함수는 C/C++ 런타임 라이브
  종료 코드 = STILL_ACTIVATE       ( pfnStartAddr, pvParam)   러리의 시작 코드를 호출하여 각종 초기화를 진행
                                                          하고, _tmain이나 _tWinMain과 같은 진입점 함수
                                                          를 호출한다.
     시그널 상태 = FALSE

                                                          진입점 함수가 반횐되면 C/C++ 애플리케이션의
                                                          주 스레드는 RtlUserThreadStart 함수로 절대 반
                                                          환되지 않는다.
C/C++런타임 라이브러리에 대한 고찰
•   Visual Studio의 C/C++런타임 라이브러리
     –   4가지의 네이티브 런타임 라이브러리
     –   2가지 형태의 매니지드 런타임을 포함
     –   모든 라이브러리들이 멀티스레드 개발을 지원하므로 더 이상
         싱글스레드 전용의 c/c++ 라이브러리는 제공되지 않는다.


•   표준 C 런타임 라이브러리
     –   운영체제에 스레드라는 개념이 도입되기 한참 전인 1970년대에 처음 개발
     –   멀티스레드 애플리케이션에서 C 런타임 라이브러리를 사용했을 때 발생하는 문제에 대해서 전혀 고려하지 않음
     –   수행중인 스레드가 system함수를 호출하고 if 문장이 수행되기 직전에 인터럽트 될 시, 동일 프로세스 내에 수행 중인
         두 번째 스레드가 수행, 이 스레드가 다른 C 런타임 함수를 호출하여 errorno의 전역 변수 값을 바꾸어 버릴 수 있다.
          •   Errno, _doserror, strtok, _wcstok, strerror, _strerror, tmpnam, tmpfile, asctime, _wasctime, gmtime, _ecvt, _fcvt등등


     –   멀티스레드 기반의 C/C++ 프로그램이 정상적으로 동작하려면 C/C++ 런타임 라이브러리 함수들을 사용하는
         각 스레드별로 적절한 구조의 데이터 블록을 생성해야 한다.
C/C++런타임 라이브러리에 대한 고찰
•   _beginthreadex
     –   운영체제는 새로운 스레드가 생성되었을 때 어떻게 새로운 데이터 블록을 할당해야 할 지 알 수없다.
     –   개발자는 이 모든 것이 정상적으로 수행될 수 있도록 해 주어야 할 막중한 책임이 있다!!.
     –   C/C++런타임 라이브러리가 제공하는 _beginthreadex를 호출해 주면 된다.


•   _begintreadex 함수의 특징
     –   각 스레드는 c/c++ 런타임 라이브러리 힙에 _tiddata메모리 블록을 가진다.
     –   _beginthredex 함수에 전달된 스레드 함수의 주소는 _tiddata메모리 블록 내에 저장된다.(Mtdll.h 에
         서 확인 가능)
     –   내부적으로 CreateThread함수를 호출, pfnStartAddr 매개변수로 전달한 스레드 함수가 아니라
         _threadstartex 라는 함수가 수행하게 된다. 또한 스레드 함수로 전달할 매개변수도 pvParam이 아니
         라 _tiddata 구조체의 주소다.
     –   정상적인 경우 CreateThread와 동일하게 스레드 핸들을 반환하고, 문제가 있으면 0을 반환한다.
C/C++런타임 라이브러리에 대한 고찰
•   _threadstartex
     –   새로 생성된 스레드는 RtlUserThreadStart(NTDLL.dll)를 호출하고 곧 _threadstartex로 진입
     –   새로 생성된 스레드의 _tiddata 블록을 가리키는 주소가 매개변수로 전달
     –   TlsSetValue는 이 함수를 호츨하는 스레드와 매개변수로 전달되는 값을 연계시키는 운영체제 함수다.
         이러한 값이 저장 되는 공간을 스레드 지역 저장소 (TLS)라고 한다.
     –   사용자 정의 스레드 함수가 전달한 매개변수 값으로 호출된다. 스레드 함수의 주소와 매개 변수 값은
         _begintreadex 함수 내에서 TLS에 저장하였던 _tiddata 블록을 _callthreadstartex 함수 내에서 가져
         와서 사용한다.
     –   사용자가 지정한 스레드 함수의 반환 값은 스레드의 종료 코드가 된다.
     –   Callthreadstaartex는 단순히 _threadstartex로 반환되고, 계속해서 RtlUserThreadStart로 반환되는
         구조가 아님에 주목, 만일 그렇다면 스레드는 종료되고, 스레드의 종료 코드는 올바르게 설정될지 모
         르겠지만 _tiddata 메모리 블록은 해제되지 않을 것며 이것은 메모리 누수를 일으키지 않기 위해
         _endtreadex라는 c/c++ 런타임 라이브러리 함수가 제공되며, 이 함수는 매개변수로 스레드 종료 코
         드로 전달 받는다.
C/C++런타임 라이브러리에 대한 고찰
•   _endtreadex
     –   c/c++런타임 라이브러리 함수인 _getpid_noexit함수는 이 함수를 호출하는 스레드의 _tiddata 메모리 블록을 가져오기
         위해 내부적으로 운영체제의 TlsGetValue 함수를 호출
     –   _tiddata 블록이 삭제되고 운영체제의 ExitThread함수가 호출되어 스레드를 실제로 파괴한다. 물론 이 과정에서 종료 코
         드가 전달되고 올바르게 설정된다.
     –   C/C++런타임 라이브러리가 제공하는 _beginthreadex를 호출해 주면 된다.


•   그외
     –   c/c++런타임 라이브러리는 두개의 스레드가 힙으로부터 동시에 메모리를 할당하는 것을 금지하고 있다. 호출한 malloc
         함수로 부터 첫 번째 스레드가 반환될 때까지 두 번째 스레드는 malloc 함수를 수행하지 못하고 대기 상태가 되며, 첫 번
         째 스레드가 반환된 이후라야 비로소 두 번째 스레드는 malloc 함수 내부로 진입 할 수 있다.
     –   c/c++ 런타임 라이브러리의 dll 버전은 다른 애플리케이션이나 dll과 공유될 수 있도록 작성되어 있다. 따라서
         멀티스레드 버전의 dll 라이브러리는 단 한번만 로드되면 된다. c/c++ 런타임 라이브러리가 dll 형태로 제공되기 때문에
         애플리케이션과 dll은 c/c++ 런타임 라이브러리 함수의 코드를 각각 가질 필요가 없고, 그 결과로 생성파일은
         좀 더 작아지고 ms가 c/c++ 런타임 라이브러리 dll 파일 내의 버그를 수정하게 되면 애플리케이션은 자동적으로
         이러한 수정사항을 반영하게 된다.
C/C++런타임 라이브러리에 대한 고찰

• 실수로 _beginthreadex 대신 createThread
 를 호출하였다면
  – 그냥 _beginThreadex 씁시다(힘들어요)

• 절대로 쓰지 말아야 하는 c/c++ 런타임 라
 이브러리 함수
  – 마찬가지입니다. _beginThreadex, ex ex!!!
자신의 구분자 얻기

• 자신의 구분자 얻기(핸들 얻기)
 – 236~ 240page( 저도 하고 싶었어요;;)

 – 대부분은 허위 핸들에서 작업 가능하지만 다른
  프로세스 쪽에 전달해서 쓰거나 제거하게 되면
  Hell…
끝

More Related Content

What's hot

동기화, 스케줄링
동기화, 스케줄링동기화, 스케줄링
동기화, 스케줄링xxbdxx
 
Windows via c++ chapter6
Windows via c++   chapter6Windows via c++   chapter6
Windows via c++ chapter6Shin heemin
 
Windows via c++ part 1
Windows via c++ part 1Windows via c++ part 1
Windows via c++ part 1Shin heemin
 
Effective STL 1~4장 정리
Effective STL 1~4장 정리Effective STL 1~4장 정리
Effective STL 1~4장 정리Shin heemin
 
Jupyter notebok tensorboard 실행하기_20160706
Jupyter notebok tensorboard 실행하기_20160706Jupyter notebok tensorboard 실행하기_20160706
Jupyter notebok tensorboard 실행하기_20160706Yong Joon Moon
 
비동기 파일 로딩
비동기 파일 로딩비동기 파일 로딩
비동기 파일 로딩Bongseok Cho
 
20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은
20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은
20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은jieun kim
 
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - OkHttp
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - OkHttp[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - OkHttp
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - OkHttpNAVER D2
 
[NDC 2016] 유니티, iOS에서 LINQ 사용하기
[NDC 2016] 유니티, iOS에서 LINQ 사용하기[NDC 2016] 유니티, iOS에서 LINQ 사용하기
[NDC 2016] 유니티, iOS에서 LINQ 사용하기Daehee Kim
 
Startup JavaScript 8 - NPM, Express.JS
Startup JavaScript 8 - NPM, Express.JSStartup JavaScript 8 - NPM, Express.JS
Startup JavaScript 8 - NPM, Express.JSCirculus
 
Apache ZooKeeper 소개
Apache ZooKeeper 소개Apache ZooKeeper 소개
Apache ZooKeeper 소개중선 곽
 
[143] Modern C++ 무조건 써야 해?
[143] Modern C++ 무조건 써야 해?[143] Modern C++ 무조건 써야 해?
[143] Modern C++ 무조건 써야 해?NAVER D2
 
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http RequestNAVER D2
 
20150502 unix v6로 배우는 커널의 원리와 구조 1 김지은
20150502 unix v6로 배우는 커널의 원리와 구조 1 김지은20150502 unix v6로 배우는 커널의 원리와 구조 1 김지은
20150502 unix v6로 배우는 커널의 원리와 구조 1 김지은jieun kim
 
Javascript 조금 더 잘 알기
Javascript 조금 더 잘 알기Javascript 조금 더 잘 알기
Javascript 조금 더 잘 알기jongho jeong
 

What's hot (20)

동기화, 스케줄링
동기화, 스케줄링동기화, 스케줄링
동기화, 스케줄링
 
Windows via c++ chapter6
Windows via c++   chapter6Windows via c++   chapter6
Windows via c++ chapter6
 
Windows via c++ part 1
Windows via c++ part 1Windows via c++ part 1
Windows via c++ part 1
 
Effective STL 1~4장 정리
Effective STL 1~4장 정리Effective STL 1~4장 정리
Effective STL 1~4장 정리
 
120114 windows viacpp_03
120114 windows viacpp_03120114 windows viacpp_03
120114 windows viacpp_03
 
04 프로세스
04 프로세스04 프로세스
04 프로세스
 
Jupyter notebok tensorboard 실행하기_20160706
Jupyter notebok tensorboard 실행하기_20160706Jupyter notebok tensorboard 실행하기_20160706
Jupyter notebok tensorboard 실행하기_20160706
 
Lock free queue
Lock free queueLock free queue
Lock free queue
 
비동기 파일 로딩
비동기 파일 로딩비동기 파일 로딩
비동기 파일 로딩
 
20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은
20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은
20150509 unix v6로 배우는 커널의 원리와 구조 3 김지은
 
Gcd ppt
Gcd pptGcd ppt
Gcd ppt
 
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - OkHttp
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - OkHttp[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - OkHttp
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - OkHttp
 
[NDC 2016] 유니티, iOS에서 LINQ 사용하기
[NDC 2016] 유니티, iOS에서 LINQ 사용하기[NDC 2016] 유니티, iOS에서 LINQ 사용하기
[NDC 2016] 유니티, iOS에서 LINQ 사용하기
 
Startup JavaScript 8 - NPM, Express.JS
Startup JavaScript 8 - NPM, Express.JSStartup JavaScript 8 - NPM, Express.JS
Startup JavaScript 8 - NPM, Express.JS
 
Apache ZooKeeper 소개
Apache ZooKeeper 소개Apache ZooKeeper 소개
Apache ZooKeeper 소개
 
[143] Modern C++ 무조건 써야 해?
[143] Modern C++ 무조건 써야 해?[143] Modern C++ 무조건 써야 해?
[143] Modern C++ 무조건 써야 해?
 
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request
[D2 CAMPUS] 안드로이드 오픈소스 스터디자료 - Http Request
 
20150502 unix v6로 배우는 커널의 원리와 구조 1 김지은
20150502 unix v6로 배우는 커널의 원리와 구조 1 김지은20150502 unix v6로 배우는 커널의 원리와 구조 1 김지은
20150502 unix v6로 배우는 커널의 원리와 구조 1 김지은
 
Anatomy of Realm
Anatomy of RealmAnatomy of Realm
Anatomy of Realm
 
Javascript 조금 더 잘 알기
Javascript 조금 더 잘 알기Javascript 조금 더 잘 알기
Javascript 조금 더 잘 알기
 

Similar to Windws via c/c++ chapter 6

뇌자T etc.windows multi threading programming
뇌자T   etc.windows multi threading programming뇌자T   etc.windows multi threading programming
뇌자T etc.windows multi threading programmingcancan21st
 
Java mentoring of samsung scsc 2
Java mentoring of samsung scsc   2Java mentoring of samsung scsc   2
Java mentoring of samsung scsc 2도현 김
 
android_thread
android_threadandroid_thread
android_threadhandfoot
 
쓰레드.pdf
쓰레드.pdf쓰레드.pdf
쓰레드.pdfSeokju Hong
 
파이썬 병렬프로그래밍
파이썬 병렬프로그래밍파이썬 병렬프로그래밍
파이썬 병렬프로그래밍Yong Joon Moon
 
NDC12_Lockless게임서버설계와구현
NDC12_Lockless게임서버설계와구현NDC12_Lockless게임서버설계와구현
NDC12_Lockless게임서버설계와구현noerror
 
세션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
 
Linux programming study
Linux programming studyLinux programming study
Linux programming studyYunseok Lee
 
Nodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjsNodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjs기동 이
 
나만의 엔진 개발하기
나만의 엔진 개발하기나만의 엔진 개발하기
나만의 엔진 개발하기YEONG-CHEON YOU
 
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요NAVER D2
 
[2B2]기계 친화성을 중심으로 접근한 최적화 기법
[2B2]기계 친화성을 중심으로 접근한 최적화 기법[2B2]기계 친화성을 중심으로 접근한 최적화 기법
[2B2]기계 친화성을 중심으로 접근한 최적화 기법NAVER D2
 
5장 객체와클래스
5장 객체와클래스5장 객체와클래스
5장 객체와클래스SeoYeong
 
테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템QooJuice
 
Startup JavaScript 4 - 객체
Startup JavaScript 4 - 객체Startup JavaScript 4 - 객체
Startup JavaScript 4 - 객체Circulus
 
C# Game Server
C# Game ServerC# Game Server
C# Game Serverlactrious
 
TenforFlow Internals
TenforFlow InternalsTenforFlow Internals
TenforFlow InternalsKiho Hong
 
Chapter3 - learning spark
Chapter3 - learning sparkChapter3 - learning spark
Chapter3 - learning sparkMungyu Choi
 

Similar to Windws via c/c++ chapter 6 (20)

뇌자T etc.windows multi threading programming
뇌자T   etc.windows multi threading programming뇌자T   etc.windows multi threading programming
뇌자T etc.windows multi threading programming
 
Java mentoring of samsung scsc 2
Java mentoring of samsung scsc   2Java mentoring of samsung scsc   2
Java mentoring of samsung scsc 2
 
android_thread
android_threadandroid_thread
android_thread
 
쓰레드.pdf
쓰레드.pdf쓰레드.pdf
쓰레드.pdf
 
파이썬 병렬프로그래밍
파이썬 병렬프로그래밍파이썬 병렬프로그래밍
파이썬 병렬프로그래밍
 
NDC12_Lockless게임서버설계와구현
NDC12_Lockless게임서버설계와구현NDC12_Lockless게임서버설계와구현
NDC12_Lockless게임서버설계와구현
 
Rx java essentials
Rx java essentialsRx java essentials
Rx java essentials
 
세션1. block chain as a platform
세션1. block chain as a platform세션1. block chain as a platform
세션1. block chain as a platform
 
Linux programming study
Linux programming studyLinux programming study
Linux programming study
 
Nodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjsNodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjs
 
나만의 엔진 개발하기
나만의 엔진 개발하기나만의 엔진 개발하기
나만의 엔진 개발하기
 
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
[2B7]시즌2 멀티쓰레드프로그래밍이 왜 이리 힘드나요
 
[2B2]기계 친화성을 중심으로 접근한 최적화 기법
[2B2]기계 친화성을 중심으로 접근한 최적화 기법[2B2]기계 친화성을 중심으로 접근한 최적화 기법
[2B2]기계 친화성을 중심으로 접근한 최적화 기법
 
5장 객체와클래스
5장 객체와클래스5장 객체와클래스
5장 객체와클래스
 
테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템테라로 살펴본 MMORPG의 논타겟팅 시스템
테라로 살펴본 MMORPG의 논타겟팅 시스템
 
Startup JavaScript 4 - 객체
Startup JavaScript 4 - 객체Startup JavaScript 4 - 객체
Startup JavaScript 4 - 객체
 
ES6 for Node.js Study 2주차
ES6 for Node.js Study 2주차ES6 for Node.js Study 2주차
ES6 for Node.js Study 2주차
 
C# Game Server
C# Game ServerC# Game Server
C# Game Server
 
TenforFlow Internals
TenforFlow InternalsTenforFlow Internals
TenforFlow Internals
 
Chapter3 - learning spark
Chapter3 - learning sparkChapter3 - learning spark
Chapter3 - learning spark
 

Recently uploaded

Console API (Kitworks Team Study 백혜인 발표자료)
Console API (Kitworks Team Study 백혜인 발표자료)Console API (Kitworks Team Study 백혜인 발표자료)
Console API (Kitworks Team Study 백혜인 발표자료)Wonjun Hwang
 
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...Kim Daeun
 
캐드앤그래픽스 2024년 5월호 목차
캐드앤그래픽스 2024년 5월호 목차캐드앤그래픽스 2024년 5월호 목차
캐드앤그래픽스 2024년 5월호 목차캐드앤그래픽스
 
A future that integrates LLMs and LAMs (Symposium)
A future that integrates LLMs and LAMs (Symposium)A future that integrates LLMs and LAMs (Symposium)
A future that integrates LLMs and LAMs (Symposium)Tae Young Lee
 
Merge (Kitworks Team Study 이성수 발표자료 240426)
Merge (Kitworks Team Study 이성수 발표자료 240426)Merge (Kitworks Team Study 이성수 발표자료 240426)
Merge (Kitworks Team Study 이성수 발표자료 240426)Wonjun Hwang
 
MOODv2 : Masked Image Modeling for Out-of-Distribution Detection
MOODv2 : Masked Image Modeling for Out-of-Distribution DetectionMOODv2 : Masked Image Modeling for Out-of-Distribution Detection
MOODv2 : Masked Image Modeling for Out-of-Distribution DetectionKim Daeun
 

Recently uploaded (6)

Console API (Kitworks Team Study 백혜인 발표자료)
Console API (Kitworks Team Study 백혜인 발표자료)Console API (Kitworks Team Study 백혜인 발표자료)
Console API (Kitworks Team Study 백혜인 발표자료)
 
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
Continual Active Learning for Efficient Adaptation of Machine LearningModels ...
 
캐드앤그래픽스 2024년 5월호 목차
캐드앤그래픽스 2024년 5월호 목차캐드앤그래픽스 2024년 5월호 목차
캐드앤그래픽스 2024년 5월호 목차
 
A future that integrates LLMs and LAMs (Symposium)
A future that integrates LLMs and LAMs (Symposium)A future that integrates LLMs and LAMs (Symposium)
A future that integrates LLMs and LAMs (Symposium)
 
Merge (Kitworks Team Study 이성수 발표자료 240426)
Merge (Kitworks Team Study 이성수 발표자료 240426)Merge (Kitworks Team Study 이성수 발표자료 240426)
Merge (Kitworks Team Study 이성수 발표자료 240426)
 
MOODv2 : Masked Image Modeling for Out-of-Distribution Detection
MOODv2 : Masked Image Modeling for Out-of-Distribution DetectionMOODv2 : Masked Image Modeling for Out-of-Distribution Detection
MOODv2 : Masked Image Modeling for Out-of-Distribution Detection
 

Windws via c/c++ chapter 6

  • 1. Windows Via C/C++ 스레드의 기본 아꿈사 오전반 윤석윤 ( seedyoon@gmail.com )
  • 2. 개요 • 스레드란? • 스레드를 생성해야 하는 경우 • 스레드를 생성하지 말아야 하는 경우 • 처음으로 작성하는 스레드 함수 • Create Thread 함수 • 스레드의 종류 • 스레드의 내부 • C/C++ 런타임 라이브러리에 대한 고찰 • 자신의 구분자 얻기
  • 3. 스레드란? 프로세스 코드 전역 변수들 프로세스 힙 프로세스 자원들 열린 파일들 프로세스 힙 …. 환경 블록 스레드 1 스레드 2 TLS TLS 스택 스택
  • 4. 스레드란? • 프로세스와 스레드는 매우 유사한 모양으로 구성 – 프로세스는 프로세스 커널 오브젝트와 주소공간으로 구성 – 스레드는 스레드 커널 오브젝트와 스레드 스택( 16장 )으로 구성 되어 있 다. • 프로세스는 스스로 수행 될 수 없다( 4장 ) – 껍데기일 뿐, 단순히 생각한다면 스레드의 저장소로 볼 수도 있다. – 스레드는 항상 프로세스의 컨텍스트 내에 생성되며, 프로세스 안에서 살아 있을 수 있다. – 프로세스의 주소 공간 내에 있는 코드를 수행하고 데이터를 다룬다.
  • 5. 스레드란? • 주소 공간의 공유 – 스레드들은 동일한 코드를 수행할 수도 있고, 동일 데이터를 조작할 수도 있다. – 커널 오브젝트 핸들 테이블은 스레드 별로 존재하는 것이 아니라 프로세스별로 존재하 기 때문에 커널 오브젝트 핸들도 역시 공유. • 프로세스에 비해 가벼움 – 프로세스는 자신만의 주소 공간을 가지기 때문에 스레드에 비해 더욱 많은 시스템 리소스를 사옹 – exe, dll파일이 주소 공간으로 로드되어야 하므로 파일 리소스 또한 필요 – 반면 스레드는 하나의 커널 오브젝트와 스레드 스택 정도만을 필요로 할 뿐 – 컨텍스트의 양이 적기 때문에 상대적으로 프로세스에 비해 전환이 빠르다.
  • 6. 스레드를 생성해야 하는 경우 • CPU를 놀게 하기 싫을 때!! – 모든 프로세스는 적어도 하나 이상의 스레드를 가질 수 있고, 아주 특별한 작업을 수행하는 경우가 아닌 이상 운 영체제가 다수의 스레드를 동시에 수행 할 수 있다는 것은 매우 많 은 이점을 제공. – Visual Studio IDE, Vista의 인덱싱, 스프레드 시트의 백그라운드 재계산 작업, 워드 프로세스의 철자 계산 등등 – I/O 성능에 의해 결정되는 프로그램들의 성능을 향상 시킬 때 (순차적 파일들을 처리 할 때에는 읽기 및 쓰기 연산을 한꺼번에 최대한 많이 수행하는 것이 더 효율적)
  • 7. 스레드를 생성하지 말아야 하는 경 우 • 위험성과 문제점 – 한 프로세스의 스레드들은 저장소와 자원들을 공유하므로, 한 프로세스가 다른 스레드의 자료를 잘못 수정할 수 있다 (경쟁조건이나 교착 같은 결함이 발생 할 수 있다) – 특정한 상황에서는 동시성이 성능을 향상시키는 게 아니라 크게 떨어뜨리기도 한다. – 기존의 단일 스레드 프로그램을 스레드들을 활용하도록 변 환하는 것이 어려울 수 있다. 위의 사항들이 이유일 수도 있 고, 프로그램 이해 부족이 이유 일 수도 있다.(ㅜ_ㅜ)
  • 8. 처음으로 작성하는 스레드 함수 • 스레드의 진입점 함수 원형 – 스레드 함수는 우리가 원하는 작업은 어떤 것이라도 수행할 수 있다. – 스레드 함수는 언젠가는 끝날 것이고 반환될 것이다. – 스레드 함수가 WINAPI ThreadFunc(VOID 수행을 멈추고 DWORD 반환되는 시점에 스레드는 pvParam) 스레드가 사용하던 스택도 반환된다. { DWORD dwResult = 0; – 스레드 커널 오브젝트의 사용 카운트도 감소한다. … – 이 값이 0이 되면 스레드 커널 오브젝트는 파괴된다. Return(dwResult); } – 프로세스 커널 오브젝트와 마찬가지로 스레드 커널 오브젝트 또한 이를 통해 관리되는 스레드 만큼은 살아 있으며 스레드 종료 이후에도 여 전히 살아 있을 수 있다.
  • 9. 처음으로 작성하는 스레드 함수 • 모든 스레드는 수행을 시작할 진입점 함수(entry point)를 반드시 가져야 한다. – 스레드 함수는 우리가 원하는 작업은 어떤 것이라도 수행할 수 있다. – 스레드 함수는 언젠가는 끝날 것이고 반환될 것이다. – 스레드 함수가 반환되는 시점에 스레드는 수행을 멈추고 스레드가 사용하던 스택도 반환된다. – 스레드 커널 오브젝트의 사용 카운트도 감소한다. – 이 값이 0이 되면 스레드 커널 오브젝트는 파괴된다. – 프로세스 커널 오브젝트와 마찬가지로 스레드 커널 오브젝트 또한 이를 통해 관리되는 스레드 만큼은 살아 있으며 스레드 종료 이후에도 여 전히 살아 있을 수 있다.
  • 10. 처음으로 작성하는 스레드 함수 • 스레드 함수는 반드시 값을 반환해야 한다. – 이 값은 나중에 스레드의 종료 코드가 되며, C/C++ 런타임 라이브 러리가 주 스레드의 종료 코드를 프로세스의 종료 코드로 사용하 는 것과 유사하다. • 스레드 함수는 가능한 함수로 전달된 매개변수와 지역 변수만을 사용하도록 작성되는 것이 좋다. – 정적(static)변수나 전역(global)변수를 사용하게 되면 다수의 스레드가 동시에 변 수에 접근할 수 있게 되며, 이는 변수의 값이 잘못 변경되는 원인이 되기도 한다. – 하지만 함수의 매개변수와 지역변수는 스레드의 스택에 유지되기 때문에 다른 스 레드에 의해 내용이 변경될 가능성이 거의 없다.
  • 11. CreateThread 함수 • 결론 – 쓰지말자. 그래도 알아보자.
  • 12. CreateThread 함수 HANDLE CreateThread( 1. LPSECURITY_ATTRIBUTES lpThreadAttributes, 2. SIZE_T dwStackSize, 3. LPTHREAD_START_ROUTINE lpStartAddress, 4. LPVOID lpParameter, 5. DWORD dwCreationFlags, 6. LPDWORD lpThreadId );
  • 13. CreateThread 함수 • LPSECURITY_ATTRIBUTES – 스레드 커널 오브젝트에 대해 기본 보안 특성을 사용할 것이라면 이 매개변수로 NULL 을 전달하면 된다. – 나머지는 취향것 MSDN 참조 • StackSize – 스레드가 자신의 스택을 위해 얼마만큼의 주소 공간을 사용할지를 지정할 수 있다. – StackSize 0 이외의 값을 지정하면 함수는 스레드 스택을 확보하기 위해 지정된 크기의 메모리를 예약하고 커밋까지 수행한다. – 만일 시스템이 스택의 최대 크기를 제한하지 않는다면 이러한 재귀호출은 절대로 종료되지 않을 것이다. – /STACK:[reserve][,commit] • reserve 인자는 시스템이 스레드스택을 위해 지정된 크기 만큼의 주소 공간을 예약(기본 1M) • Commit 인자는 스택으로 예약된 주소 공간에 커밋된 물리적 저장소의 초기크기를 나타낸다.
  • 14. 스레드의 종료 • 스레드의 종료 – 스레드 함수가 반환된다( Excellent!! ) – 스레드 함수 내에서 ExitThread 함수를 호출한다 – 동일한 프로세스나 다른 프로세스에서 TerminateThread함수를 호출한다. – 스레드가 포함된 프로세스가 종료된다.
  • 15. 스레드의 종료 • 스레드 함수가 반환된다. – 항상 스레드 함수가 반환되도록 설계하는 것이 좋다. 이것은 스레드가 사용한 자원을 적절하 게 정리 할 수 있는 유일한 방법이다.
  • 16. 스레드의 종료 • 스레드 함수가 반환된다. 스레드 함수 내에서 생성한 시스템은 스레드의 종료 코드를 운영체제는 스레드 스택으 모든 C++ 오브젝트들은 스레드 함수의 반환 값으로 설정 로 사용했던 메모리 반환 파괴자를 통해 적절히 제거 (스레드 커널 오브젝트에 저장) 시스템은 스레드 커널 오브 젝트의 사용 카운터를 감소
  • 17. 스레드의 종료 • ExitThread 함수 – 스레드를 강제 종료(반환되지 않는 함수). 스레드를 강제 종료, 시스템은 스레드의 종료 코드를 운영체제가 스레드에 운영체제는 스레드 스택으 스레드 함수의 반환 값으로 설정 사용했던 모든 운영체제 로 사용했던 메모리 반환 (스레드 커널 오브젝트에 저장) 리소스를 종료 스레드 함수 내에서 생성한 모든 C++ 오브젝트들은 파괴자를 통해 적절히 제거 시스템은 스레드 커널 오브 젝트의 사용 카운터를 감소
  • 18. 스레드의 종료 • TerminatedThread 함수 – 어떠한 스레드라도 종료 가능(비동기 함수) 스레드를 강제 종료, 운영체제가 스레드에 시스템은 스레드의 종료 코드를 시스템은 스레드 커널 오브 사용했던 모든 운영체제 스레드 함수의 반환 값으로 설정 젝트의 사용 카운터를 감소 리소스를 종료 (스레드 커널 오브젝트에 저장) 스레드 함수 내에서 생성한 운영체제는 스레드 스택으 모든 C++ 오브젝트들은 로 사용했던 메모리 반환 파괴자를 통해 적절히 제거
  • 19. 스레드의 종료 • 프로세스 종료시 – 프로세스가 소유한 모든 스레드 종료 • 프로세스가 사용중 이던 리소스들도 모두 정리 • 프로세스 내에 남아 있던 스레드들에 대해 각각 TerminatedThread 함수가 호출!!!!
  • 20. 스레드의 종료 • 스레드가 종료되면 – 모든 유저 오브젝트 핸들이 삭제 • 윈도우와 윈도우 훅 두 개의 사용자 오브젝트는 스레드에 의해 소유, 스레드가 종료되면 시스템 은 자동적으로 해당 스레드가 생성한 윈도우를 파괴하고, 설치된 윈도우 훅을 제거한다. • 다른 형태의 오브젝트들은 모두 소유하고 있는 프로세스가 종료되는 시점에 파괴된다. – 스레드의 종료 코드는 STILL_ACTIVATE에서 exitThread나 TerminatedThread에서 지 정한 종료코드로 변경 – 스레드 커널 오브젝트의 상태가 시그널 상태로 변경 – 종료되는 스레드가 프로세스 내의 마지막 활성 스레드라면 시스템은 프로세스도 같이 종료되어야 하는 것으로 간주 – 스레드 커널 오브젝트의 사용 카운트가 1만큼 감소 스레드의 종료 여부를 확인하기 위해 어떻게 스레드 핸들을 사용할 지에 대해서는 9장에서~
  • 21. 스레드의 내부 스레드 커널 오브젝트 스레드 스택 컨텍스트 SP pvParam IP pfnStartAddr 다른 CPU 레지스터 . . . 다른속성과 통계정보 사용 카운트 = 2 NTDLL.dll 정지카운트 = 1 VOID RtlUserThreadStart(…){} 종료 코드 = STILL_ACTIVATE 시그널 상태 = FALSE
  • 22. 스레드의 내부 스레드 커널 오브젝트 사용카운트 CreateThread 함수가 반환한 핸들이 스레드 스택 제거될 때까지 파괴되지 않는다.) 컨텍스트 SP pvParam IP pfnStartAddr 다른 CPU 레지스터 . . . 다른속성과 통계정보 사용 카운트 = 2 NTDLL.dll 정지카운트 = 1 VOID RtlUserThreadStart(…){} 종료 코드 = STILL_ACTIVATE 시그널 상태 = FALSE
  • 23. 스레드의 내부 스레드 커널 오브젝트 사용카운트 CreateThread 함수가 반환한 핸들이 스레드 스택 제거될 때까지 파괴되지 않는다.) 컨텍스트 SP pvParam IP pfnStartAddr 스레드 커널 오브젝트의 초기화 다른 CPU 레지스터 . . . 다른속성과 통계정보 사용 카운트 = 2 NTDLL.dll 정지카운트 = 1 VOID RtlUserThreadStart(…){} 종료 코드 = STILL_ACTIVATE 시그널 상태 = FALSE
  • 24. 스레드의 내부 스레드 커널 오브젝트 사용카운트 CreateThread 함수가 반환한 핸들이 스레드 스택 제거될 때까지 파괴되지 않는다.) 컨텍스트 SP pvParam IP pfnStartAddr 스레드 커널 오브젝트의 초기화 다른 CPU 레지스터 . . . 스레드 스택으로 활용할 메모리 공간 할당 다른속성과 통계정보 (프로세스 주소 내 공간) 사용 카운트 = 2 NTDLL.dll 정지카운트 = 1 VOID RtlUserThreadStart(…){} 종료 코드 = STILL_ACTIVATE 시그널 상태 = FALSE
  • 25. 스레드의 내부 스레드 커널 오브젝트 사용카운트 CreateThread 함수가 반환한 핸들이 스레드 스택 제거될 때까지 파괴되지 않는다.) 컨텍스트 SP pvParam IP pfnStartAddr 스레드 커널 오브젝트의 초기화 다른 CPU 레지스터 . . . 스레드 스택으로 활용할 메모리 공간 할당 다른속성과 통계정보 (프로세스 주소 내 공간) 사용 카운트 = 2 NTDLL.dll 정지카운트 = 1 새로 생성된 스레드 스택의 가장 VOID RtlUserThreadStart(…){} 상위에 두 개의 값을 기록 종료 코드 = STILL_ACTIVATE 1. pvParam 2. pfnStartAddr 시그널 상태 = FALSE
  • 26. 스레드의 내부 스레드 커널 오브젝트 스레드 컨텍스트 스레드가 마지막으로 수행되었을 때 스레드 스택 당시의 CPU 레지스터 값을 가지고 있다. 컨텍스트 SP pvParam IP pfnStartAddr 인스트럭션 포인터(IP)와 스택 포인터(SP) 프로세스 컨텍스트 내부에서 수행되기 때문에, 프로세스 메모리 공간 상의 특정 위치를 다른 CPU 레지스터 . . 가리키고 있다. . 스레드 커널 오브젝트가 초기화되면 Context구조 체 내의 스택 포인터 레지스터는 pfnStartAddr를 다른속성과 통계정보 저장하고 있는 스레드 스택의 주소로 설정 사용 카운트 = 2 NTDLL.dll 정지카운트 = 1 IP는 NTDLL.dll 모듈이 익스포트 하고 VOID RtlUserThreadStart(…){} 있는 RtlUserThreadStart라는 종료 코드 = STILL_ACTIVATE 문서화 되지 않은 함수의 주소를 가리키도록 설정 시그널 상태 = FALSE
  • 27. 스레드의 내부 스레드 커널 오브젝트 스레드의 초기화가 완료되면 CREATE_SUSPENDED플래그가 전달되 스레드 스택 었는지 확인, 전달되지 않았다면 정지 카 컨텍스트 운트를 0으로 설정 스레드가 프로세서에 pvParam 스케줄될 수 있도록 한다. SP IP pfnStartAddr 스레드가 CPU 시간을 얻으면 시스템은 스레드 컨텍스트에 마지막으로 저장된 값을 CPU 레지 다른 CPU 레지스터 . 스터로 로드, 프로세스 주소 공간 내에 있는 . 코드를 수정하고 데이터를 변경하는 등의 . 작업을 수행한다. 새로운 스레드의 IP가 RtlUserThreadStart로 설정, 다른속성과 통계정보 실질적으로 수행하는 최초 위치가 된다. 사용 카운트 = 2 NTDLL.dll 정지카운트 = 1 VOID RtlUserThreadStart RtlUserThreadStart 함수는 C/C++ 런타임 라이브 종료 코드 = STILL_ACTIVATE ( pfnStartAddr, pvParam) 러리의 시작 코드를 호출하여 각종 초기화를 진행 하고, _tmain이나 _tWinMain과 같은 진입점 함수 를 호출한다. 시그널 상태 = FALSE 진입점 함수가 반횐되면 C/C++ 애플리케이션의 주 스레드는 RtlUserThreadStart 함수로 절대 반 환되지 않는다.
  • 28. C/C++런타임 라이브러리에 대한 고찰 • Visual Studio의 C/C++런타임 라이브러리 – 4가지의 네이티브 런타임 라이브러리 – 2가지 형태의 매니지드 런타임을 포함 – 모든 라이브러리들이 멀티스레드 개발을 지원하므로 더 이상 싱글스레드 전용의 c/c++ 라이브러리는 제공되지 않는다. • 표준 C 런타임 라이브러리 – 운영체제에 스레드라는 개념이 도입되기 한참 전인 1970년대에 처음 개발 – 멀티스레드 애플리케이션에서 C 런타임 라이브러리를 사용했을 때 발생하는 문제에 대해서 전혀 고려하지 않음 – 수행중인 스레드가 system함수를 호출하고 if 문장이 수행되기 직전에 인터럽트 될 시, 동일 프로세스 내에 수행 중인 두 번째 스레드가 수행, 이 스레드가 다른 C 런타임 함수를 호출하여 errorno의 전역 변수 값을 바꾸어 버릴 수 있다. • Errno, _doserror, strtok, _wcstok, strerror, _strerror, tmpnam, tmpfile, asctime, _wasctime, gmtime, _ecvt, _fcvt등등 – 멀티스레드 기반의 C/C++ 프로그램이 정상적으로 동작하려면 C/C++ 런타임 라이브러리 함수들을 사용하는 각 스레드별로 적절한 구조의 데이터 블록을 생성해야 한다.
  • 29. C/C++런타임 라이브러리에 대한 고찰 • _beginthreadex – 운영체제는 새로운 스레드가 생성되었을 때 어떻게 새로운 데이터 블록을 할당해야 할 지 알 수없다. – 개발자는 이 모든 것이 정상적으로 수행될 수 있도록 해 주어야 할 막중한 책임이 있다!!. – C/C++런타임 라이브러리가 제공하는 _beginthreadex를 호출해 주면 된다. • _begintreadex 함수의 특징 – 각 스레드는 c/c++ 런타임 라이브러리 힙에 _tiddata메모리 블록을 가진다. – _beginthredex 함수에 전달된 스레드 함수의 주소는 _tiddata메모리 블록 내에 저장된다.(Mtdll.h 에 서 확인 가능) – 내부적으로 CreateThread함수를 호출, pfnStartAddr 매개변수로 전달한 스레드 함수가 아니라 _threadstartex 라는 함수가 수행하게 된다. 또한 스레드 함수로 전달할 매개변수도 pvParam이 아니 라 _tiddata 구조체의 주소다. – 정상적인 경우 CreateThread와 동일하게 스레드 핸들을 반환하고, 문제가 있으면 0을 반환한다.
  • 30. C/C++런타임 라이브러리에 대한 고찰 • _threadstartex – 새로 생성된 스레드는 RtlUserThreadStart(NTDLL.dll)를 호출하고 곧 _threadstartex로 진입 – 새로 생성된 스레드의 _tiddata 블록을 가리키는 주소가 매개변수로 전달 – TlsSetValue는 이 함수를 호츨하는 스레드와 매개변수로 전달되는 값을 연계시키는 운영체제 함수다. 이러한 값이 저장 되는 공간을 스레드 지역 저장소 (TLS)라고 한다. – 사용자 정의 스레드 함수가 전달한 매개변수 값으로 호출된다. 스레드 함수의 주소와 매개 변수 값은 _begintreadex 함수 내에서 TLS에 저장하였던 _tiddata 블록을 _callthreadstartex 함수 내에서 가져 와서 사용한다. – 사용자가 지정한 스레드 함수의 반환 값은 스레드의 종료 코드가 된다. – Callthreadstaartex는 단순히 _threadstartex로 반환되고, 계속해서 RtlUserThreadStart로 반환되는 구조가 아님에 주목, 만일 그렇다면 스레드는 종료되고, 스레드의 종료 코드는 올바르게 설정될지 모 르겠지만 _tiddata 메모리 블록은 해제되지 않을 것며 이것은 메모리 누수를 일으키지 않기 위해 _endtreadex라는 c/c++ 런타임 라이브러리 함수가 제공되며, 이 함수는 매개변수로 스레드 종료 코 드로 전달 받는다.
  • 31. C/C++런타임 라이브러리에 대한 고찰 • _endtreadex – c/c++런타임 라이브러리 함수인 _getpid_noexit함수는 이 함수를 호출하는 스레드의 _tiddata 메모리 블록을 가져오기 위해 내부적으로 운영체제의 TlsGetValue 함수를 호출 – _tiddata 블록이 삭제되고 운영체제의 ExitThread함수가 호출되어 스레드를 실제로 파괴한다. 물론 이 과정에서 종료 코 드가 전달되고 올바르게 설정된다. – C/C++런타임 라이브러리가 제공하는 _beginthreadex를 호출해 주면 된다. • 그외 – c/c++런타임 라이브러리는 두개의 스레드가 힙으로부터 동시에 메모리를 할당하는 것을 금지하고 있다. 호출한 malloc 함수로 부터 첫 번째 스레드가 반환될 때까지 두 번째 스레드는 malloc 함수를 수행하지 못하고 대기 상태가 되며, 첫 번 째 스레드가 반환된 이후라야 비로소 두 번째 스레드는 malloc 함수 내부로 진입 할 수 있다. – c/c++ 런타임 라이브러리의 dll 버전은 다른 애플리케이션이나 dll과 공유될 수 있도록 작성되어 있다. 따라서 멀티스레드 버전의 dll 라이브러리는 단 한번만 로드되면 된다. c/c++ 런타임 라이브러리가 dll 형태로 제공되기 때문에 애플리케이션과 dll은 c/c++ 런타임 라이브러리 함수의 코드를 각각 가질 필요가 없고, 그 결과로 생성파일은 좀 더 작아지고 ms가 c/c++ 런타임 라이브러리 dll 파일 내의 버그를 수정하게 되면 애플리케이션은 자동적으로 이러한 수정사항을 반영하게 된다.
  • 32. C/C++런타임 라이브러리에 대한 고찰 • 실수로 _beginthreadex 대신 createThread 를 호출하였다면 – 그냥 _beginThreadex 씁시다(힘들어요) • 절대로 쓰지 말아야 하는 c/c++ 런타임 라 이브러리 함수 – 마찬가지입니다. _beginThreadex, ex ex!!!
  • 33. 자신의 구분자 얻기 • 자신의 구분자 얻기(핸들 얻기) – 236~ 240page( 저도 하고 싶었어요;;) – 대부분은 허위 핸들에서 작업 가능하지만 다른 프로세스 쪽에 전달해서 쓰거나 제거하게 되면 Hell…
  • 34.