본문 바로가기
C언어/Pintos

Synchronization

by lacuca9 2024. 9. 25.

스레드들 간 자원 공유하는것은 주의를 요함.

pintos는 이를 위한 다양한 synchronization 함수들을 제공함

 

Disabling Interrupts 인터럽트 비활성화

synchroniztation을 하는 가장 단순한 방법은

인터럽트(CPU의 실행을 잠시 멈추고, 특정 작업을 처리한 후 실행하던 작업으로 복귀)를 불가능하게 하는 것.

인터럽트가 꺼지면, 다른 스레드는 진행중인 스레드를 선점 불가.

스레드 선점은(preempt)은 timer interrupt에 의해 이뤄지기 때문. 

 

암튼, pintos는 '선점가능한(preemptible) 커널' 임.

선점 가능한 커널은 더 명시적인 동기화가 필요함

 

인터럽트를 비활성화 시키는 주된 이유는 외부의 인터럽트 핸들러와 커널 스레드를 동기화 시키기 위해서임.

 

몇가지 외부 인터럽트는 비활성화로도 막을 수 없다. - 마스크 불가능 인터럽트(NMIs)

pintos는 이건 안다룸.

 

인터럽트 활성화, 비활성화 시키는 자료형 함수들은

include/threads/interrupt.h 에 있음

 

enum intr_level;

INTR_OFF or INTR_ON으로 상태 확인 알려줌

 

enum intr_level intr_get_level (void)

현재 인터럽트 상태(inter_level)를 리턴하는 함수.

 

enum intr_level intr_set_level (enum intr_level level);

현재 상태(inter_level)에 따라 인터럽트를 활성화하거나 비활성화. 이전 상태 리턴.

 

 

Semaphores 세마포어

세마포어는 비음수 정수 값을 갖는 변수로 두개의 연산자를 통해서 원자적으로 조작할 수 있습니다.

  • "Down" or "P" : 값이 양수가 되기를 기다렸다가, 양수가되면 감소시킴
  • "UP" or "V" : 값을 증가시킵니다. (그리고 P연산에서 wait 중인 쓰레드가 있다면 하나를 깨움)

0으로 초기화 된 세마포어는 한번만 일어날 이벤트를 기다리는데 사용됨.

스레드A가 스레드B를 시작시키고 B의 어떤 활동이 끝났다는 시그널을 기다린다고 치자.

A는 0으로 초기화된 세마포어를 만들어서 B에게 넘겨주고 A는 그 세마포어를 down 하는 것과 B가 

up 하는건 순서 상관없이 일어남.

 

1로 초기화된 세마포어는 자원의 접근을 제어하는데 사용됨.

한 코드블럭이 자원을 사용하기 전에, 세마포어를 down 연산함. 다 사용한 후에 up 연산을 함.

 

1보다 큰 값으로 초기화 될 수 있음.

 

세마포어는 인터럽트 비활성화와 쓰레드 블로킹(thread_block()) 쓰레드 언브로킹(thread_unblock())으로 이뤄짐.

세마포어는 연결리스트를 사용해서 대기중인 쓰레드의 리스트를 유지함.
(연결 리스트 구현 - lib/kernel/list.c)

 

locks 락

락은 초기값을 1로 하는 세마포어와 같음 up은 락에서 release와 같고 down연산은 acquire 임.

각 자료형과 함수들은 include/threads/synch.h 에 선언되어 있음.

 

 

monitors 모니터

세마포어느 락 보다 더 높은 (추상화) 수준의 동기화 방법.

모니터는 동기화된 데이터, 모니터락이라 부르는 락, 한개 이상의 컨디션 변수로 이루어진다.

보호받는 데이터에 접근하기전에 쓰레드는 모니터락을 얻음. 모니터 안에서 쓰레드는 모든 보호받는

데이터에 접근 할 수 있다. 맘대로 확인 수정 가능.

 

컨디션 변수는 특정 조건이 참이 될 때까지 모니터안의 코드가 기다릴 수 있게 함

컨디션 변수 자료형과 함수들은 include/threads/synch.h에 선언됨.

 

 

최적화 장벽 - 컴파일러가 메모리 상태에 대해 어떤 가정을 못하게 막아주는 특별한 명령문.

컴파일러는 장벽에 막혀서 read와 write의 순서를 재정렬하지 않고, 변수의 값이 수정되지 않는다고 가정.

핀토스에선 include/threads/synch.h 가 barrier() 매크로를 최적화 장벽으로 정의해둠.

쓰는 이유는 컴파일러가 알지 못하는 사이에 다른 인터럽트 핸들러가 비동기적으로 데이터를 바꿀 때가 있기 때문.

devices/timer.c 안의 too_many_loops() 함수가 그 예임.

이 함수는 timer tick이 발생할 때 까지 반복문을 busy-wait(spin)하며 시작

 

결과적으로 메모리의 읽기, 쓰기 순서를 강제로 정해줄 때 사용될 수 있음.

장벽이 없으면 버그 생김. 컴파일러가 순서 유지해야 할 이유가 안보이면 맘대로 연산함.

 

같은 소스 파일 안에서 정의된 함수나 포함된 헤더를 최적화 장벽으로 기대하면 안됨.

최적화 장벽은 함수를 정의하기 전에도, 해당 함수의 사용에 대해 적용될 수 있음.

이유 - 컴파일러는 최적화를 수행하기 전에 먼저 전체 소스 파일을 읽고 파싱하기 때

 

'C언어 > Pintos' 카테고리의 다른 글

WIL3 (1)  (0) 2024.10.10
WIL  (0) 2024.10.01
Understanding Threads  (0) 2024.09.25
개요  (0) 2024.09.25