블로그는 나의 힘!
[ Programing ]/Interview2022. 1. 19. 11:50

# 쓰레드 동기화

1. 실행순서의 동기화와 메모리 접근에 대한 동기화가 있다.
- 실행순서의 동기화는 쓰레드의 실행순서를 정의하고, 이 순서에 반드시 따르도록 하 것이 쓰레드 동기화 이다.
- 메모리 접근에 대한 동기화는 메모리 접근에 있어서 동시접근을 막는 것 또한 쓰레드의 동기화에 해당한다.


2. Windows에서 제공하는 동기화 기법은 제공하는 주체에 따라 크게 두 가지로 나눈다.

유저모드 동기화
동기화가 진행되는 과정에서 커널의 힘을 빌리지 않는 동기화 기법이다.
따라서 동기화를 위해서 커널 모드로의 전환이 불필요하기 때문에 성능상에 이점이 있다.
그만큼 기능상의 제한도 있다.

커널 모드 동기화
커널에서 제공하는 동기화 기능을 활용하는 방법이다.
따라서 동기화에 관련된 함수가 호출될 때마다 커널 모드로의 변경이 필요하고, 이는 성능의 저하로 이어지게 된다.
하지만 그만큼 유저 모드 동기화에서 제공하지 못하는 기능을 제공받을 수 있다.





# 임계영역 접근 동기화

임계 영역(Critical Section)이란 
배타적 접근(한 순간에 하나의 쓰레드만 접근)이 요구되는 공유 리소스에 접근하는 코드 블록을 의미한다.

* 이름있는 뮤텍스 기반의 동기화는 프로세스 동기화에 필요
* 이벤트 기반의 동기화는 실행순서 동기화에 필요.
* 크리티컬 섹션, 인터락 함수, 뮤텍스, 세마포어는 메모리 접근 동기화에 필요





# 유저 모드의 동기화

크리티컬 섹션 기반의 동기화

* 화장실에 들어가기 전 열쇠로 문을 열고 들어가고, 나와서는 다른 사람을 위해 화장실 앞에 열쇠를 둔다.
  이것이 크리티컬 섹션 동기화 방식이다.
  핵심은 열쇠를 얻은 자만이 화장실에 들어갈 수 있다는 것이다.

*  크리티컬 섹션 기반의 동기화를 사용하려면?
  크리티컬 섹션 오브젝트(자료형이 CRITICAL_SECTION인 변수)라는 것을 만들고 초기화해야 한다.
  CRITICAL_SECTION g_CriticalSection
  - 선언한 후 이걸 통해 초기화 과정을 거쳐야 한다.

*  초기화 과정을 통해 크리티컬 섹션 오브젝트는 사용 가능한 상태가 된다.
   void InintializeCriticalSection ( LPCRITICAL_SECTION  IpCriticalSection )
    - 1. IpCriticalSection 
         : 초기화 하고자 하는 크리티컬 섹션 오브젝트의 주소값을 인자로 전달한다.

* 위와 같이 열쇠를 만들었다면, 다음으론 열쇠를 획득하는 행위, 또 열쇠를 돌려놓는 행위를 해야한다.
   void EnterCriticalSection ( 1 )
   - 1. LPCRITICAL_SECTION IpCriticalSection
        : 임계영역에 진입하기 위해 필요한 크리티컬 섹션 오브젝트의 주소값을 인자로 전달. 
         누군가(다른 쓰레드)에 의해 이미 이 함수가 호출된 상태라면, 호출된 함수는 블로킹 된다. 
         그리고 열쇠가 반환되면 블로킹 상태에 있던 함수는 빠져나온다. 
         이 함수 호출하고 임계영역으로 들어갔을 때 이를 호출한 쓰레드가 크리티컬 섹션을 얻었다고 표현한다.
   void LeaveCriticalSection ( 1 )
   - 1. LPCRITICAL_SECTION IpCriticalSection
        : 임계영역을 빠져 나오고 나서 호출하는 함수. 
         화장실에 열쇠를 다시 걸어놓는 함수. 
         만약 EnterCriticalSection을 호출한 뒤 블로킹 상태에 놓인 쓰레드가 있다면, 
         이 함수 호출로 인해 블로킹 상태에서 빠져나와 임계영역으로 진입하게 된다. 
         호출이 완료되었을 때, 
         이를 호출한 쓰레드가 크리티컬 섹션 오브젝트를 반환했다고 표현한다.

* 끝으로 초기화 함수 호출 과정에서 할당되었던 리소스를 반환해야 한다.
   void DeleteCriticalSection ( 1 )
   - 1. LPCRITICAL_SECTION IpCriticalSection
        : 반환하고자 하는 크리티컬 섹션 오브젝트의 주소값을 인자로 전달한다.






# 인터락 함수 기반의 동기화

* 인터락 함수는 함수 내부적으로 한 순간에 하나의 쓰레드에 의해서만 실행되도록 동기화 됨.

* 유저 모드 기반으로 동작
* InterlockedIncrement 함수는 둘 이상 쓰레드가 공유하는 메모리에 저장 값을,   
  이 함수를 통해 증가시킬 경우 동기화된 상태에서 접근하는 것과 동일한 안정성을 보장.

* InterlockedDecrement 함수는 둘 이상 쓰레드가 공유하는 메모리에 저장 값을, 
   이 함수를 통해 감소시킬 경우 동기화된 상태에서 접근하는 것과 동일한 안정성을 보장.

* 크리티컬 섹션 동기화 기법도 내부적으로 인터락 함수를 기반으로 구현됨.

* 전달인자가 volatile로 선언되어 있어서 포인터를 이용해 함수 내부적으로 최적화를 수행하지 않으며, 
  해당 포인터가 가리키는 메모리 영역을 캐쉬하지 않는다.




# 커널 모드의 동기화

Windows 커널 레벨에서 제공해 주는 동기화 기법이기 때문에, 유저 모드 동기화가
제공해 주지 못하는 기능을 제공받을 수 있다.


뮤텍스 기반

* 보안 설정 부분은 프로세스와 마찬가지로 커널 오브젝트이기 때문에 새로운 프로세스 생성 시
  핸들을 상속해 줄 것이냐, 말 것이냐를 결정하 데 있어서 이 전달인자를 활용.
  뮤텍스 라는 열쇠를 생성하는 함수.

* 소유자 지정 부분은 크리티컬 섹션처럼 먼저 차지하는 사람이 임자가 되게 할 수 있고(FALSE)
  뮤텍스를 생성하는 쓰레드가 먼저 기회를 얻을 수도 있다.(TRUE)

* 반환 타입은 HANDLE이다. 
  반환 타입이 HANDLE이라는 것은 뮤텍스가 커널 오브젝트임을 말하는 것이다.

* 커널 오브젝트는 상태를 지니는데, 그 하나는 Signaled 상태이고, 다른 하나는 Non-Signaled 상태이다.

* 보통 커널 오브젝트는 Non-Signaled 상태에 놓여 있다가, 특정 상황이 되면 Signaled 상태가 된다.

* 뮤텍스는 누군가가 열쇠를 취득했을 때 Non-Signaled 상태가 되고, 
  취득한 열쇠를 반환했을 때 Signaled 상태가 된다.

* WaitForSingleObject 함수는 커널 오브젝트가 Siganled 상태가 되어 반환될 경우, 
  해당 커널 오브젝트의 상태를 Non-Signaled 상태로 변경하므로, 
  다른 쓰레드들은 임계 영역으로의 진입이 제한된다.

* ReleaseMutex 함수는 임계 영역에서 일을 마친 쓰레드가 반환할때 호출한다.
  즉 Signaled 상태가 되어서 다른 쓰레드의 진입을 허용한다.



세마포어 기반

* LPSECURITY_ATTRIBUTES 보안 설정 부분은 프로세스와 마찬가지로 
  커널 오브젝트이기 때문에 새로운 프로세스 생성 시 핸들을 
  상속해 줄 것이냐, 말 것이냐를 결정하데 있어서 이 전달인자를 활용.

* InitialCount는 세마포어는 값을 지니는데, 이 값을 기반으로 임계 영역에 접근 가능한 쓰레드의 개수를 제한한다.

* lMaximumCount 세마포어가 지닐 수 있는 값의 최대 크기를 지정한다.
  이 값이 1이 될 경우 뮤텍스와 동일한 기능하는 바이너리 세마포어가 구성된다.

* 세마포어가 생성될 때 전달인자에 의해 초기 카운트가 결정.

* 카운트가 0인 경우 Non-Signaled 상태에 놓이게 되며, 1 이상인 경우 Signaled 상태에 있게 된다.

* WaitForSingleObject 함수를 호출할 경우, 카운트가 하니씩 감소하면서 함수를 반환한다.
  - 초기 카운트가 10이면 WaitForSingleObject 함수가 총 열 번 호출될 때까지 블로킹이 되지 않고
    열한 번째 호출 시 세마포어 카운트가 0인 관계로 블로킹 상태가 된다.

* 초기 카운트가 1이면 뮤텍스와 동일하게 돌아간다.



이름있는 뮤텍스 기반의 프로세스 동기화

* 서로 다른 프로세스 영역에 존재하는 쓰레드가 뮤텍스를 이용해서 동기화되고 있는 상황.

* 뮤텍스는 커널 오브젝이트므로,
 
프로세스 A의 요청에 의해서 생성되었다고 해서 프로세스 A의 영역에 존재하는 것이 아니다. 
  커널이 관리하는 오브젝트이므로 프로세스 B도 접근이 가능하다.

* 핸들 테이블은 커널 오브젝트와 이를 지칭하는 핸들값에 대한 정보를 담고 있는 테이블인데, 
  이는 각각의 프로세스별로 독립적이다. (핸들 값이 동일해도 독립적이기 때문에 접근 불가능)

* 뮤텍스 이름을 붙여줘서 프로세스 별로 커널 오브젝트에 접근 가능한 핸들 정보를 얻을 수 있다.



뮤텍스의 소유와 WAIT_ABANDONED

* 두 개의 쓰레드 A, B와 동기화 오브젝트인 뮤텍스 C가 존재한다면 쓰레드 A가 뮤텍스 C를 획득했다.
  그리고 쓰레드 B는 쓰레드 A가 뮤텍스를 반환하기를 기다린다.
  그런데, 갑자기 쓰레드 A가 종료되어 버렸다.
  뮤텍스를 반환하지도 않고 사라져버린 것이다. 이제 Windows는 이러한 상황을 파악한다. 
  Windows는 쓰레드의 상태와 뮤텍스의 상태를 예의 주시하기 때문에 
  이러한 문제가 발생했음을 인지하고 더 이상 정상적인 방법으로 반환이 불가능한 뮤텍스를 대신 반환해주고, 
  다음 대기자인 쓰레드가 B가 뮤텍스를 소유할 수 있도록 도와준다.
  이때 쓰레드 B는 WAIT_ABANDONED 값을 반환받게 된다.





참고 및 출처 : 

https://blog.naver.com/ya3344/222427414380

 

쓰레드 동기화 기법(메모리 접근에 대한 동기화)

쓰레드 동기화 1. 실행순서의 동기화와 메모리 접근에 대한 동기화가 있다. - 실행순서의 동기화는 쓰레드...

blog.naver.com

https://8iggy.tistory.com/184?category=1023741 

 

기술면접을 위한 OS 개념정리 04 - 프로세스 동기화, 데드락

읽기 전 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다. 개인적으로 사용해보면서 배운 점을 정리한 글입니다. 기술면접만을 준비하기보다 비전공자 입장에서 OS의 기본

8iggy.tistory.com

[13. 쓰레드 동기화 기법] :: 어떤 프로그래머 (tistory.com) 

 

[13. 쓰레드 동기화 기법]

* 이 내용은 '뇌를 자극하는 윈도우즈 시스템 프로그래밍' 책의 내용을 정리한 것 입니다. 쓰레드 동기화란 무엇인가? 두 가지 관점에서의 쓰레드 동기화 여기서 말하는 동기화는, 순서에 있어서

popcorntree.tistory.com



 

Posted by Mister_Q