Critical Sections
Critical sections
- Critical Section은 공유 자원에 여러가지 접근이 있을 때 문제가 생기지 않도록 하는 영역이다.
- 예를 들어, A스레드와 B스레드가 동시에 어떤 값을 변경한다고 해보자.
- 이 때 A스레드가 읽고 반환하기 전에 B스레드가 읽으면, A의 결과와 B의 결과가 합쳐져 나오는 게 아니라 마지막에 나오는 스레드의 값만 적용이 될 것이다.
- 이처럼 여러개로부터 영향을 받더라도 Mutex 변수에 따라 배타적으로 프로세스가 수행되는 영역을 Critical Section이라고 부른다.
Parts of Critical Sections
- Critical Section을 포함한 코드는 4가지 부분으로 나눌 수 있다.
- Entry section : Critiacal Section에 들어가기 전에 먼저 들어가도 되는지 허가를 받는 부분이다.
- Critical Section : 공유하는 자원이 있을 때 한번에 하나만 자원에 접근하도록 하는 부분이다.
- Exit section : Critical Section에서 모든 작업을 수행하고 허가를 풀어줌으로써 다른 접근이 접근할 수 있도록 하는 부분이다.(Unlock)
- Remainder section : 위 3가지에 들어가지 않는 나머지 부분이다.
Solution to Critical-Section Problem
Critical-Section 문제를 해결하는 방법이 갖추어야 하는 3가지 요소가 있다.
1. Mutual Exclusion : 어떤 프로세스 P가 Critical-Section에서 작업을 하고 있다면, 나머지 프로세스는 접근할 수 없도록 해야한다.
2. Progress : 어떤 프로세스도 Critical-Section의 허가를 얻어서 작업을 하고 있지 않는 상태이다. 이 상태에서 허가를 얻고 싶은 다른 프로세스들이 있으면(대기중), 이들 중 하나가 Critical-Section에 들어갈 수 있는 허가를 받아야 한다. 다시 말해 계속해서 진행이 되어야 한다.
3. Bounded Waiting : 허가를 얻고 싶은 프로세스의 대기 시간은 한정적이여야 한다. 제한된 시간 안에 허가를 얻어야 한다는 뜻이다. 이러한 이유는 모든 프로세스가 동일한 기회를 얻어야 하기 때문이다.
Semaphores
- Semaphore는 단순한 int형 변수이다.
- 두가지 atomic operation을 가진다. 다른 기능이 끼어들 수 없는 특정한 기능 2가지를 가진다는 의미이다.
- wait : Semaphore 값을 하나 감소시킨다.
- S > 0이면, 값을 하나 줄인다.
- S = 0이면, 값을 줄이지 않고 호출한 프로세스를 대기시킨다.
- signal : Semaphore 값을 하나 증가시킨다 .
- S = 0이면, 다시 말해 대기하는 프로세스가 있으면, 프로세스들 중 하나의 대기 상태를 푼다.
- 아무런 프로세스도 대기하지 않으면, S 값을 하나 늘린다.
Initialization Semaphore
- Semaphore는 초기화 값이 중요하다. Critical을 해결하기 위한 Semaphore는 S값을 1로 초기화한다.
- S=1
- 먼저 wait한다. S=0이 된다.
- S=0이 되었으므로 Critical Section이 된다.
- 만약 다른 프로세스가 와서 wait하면, S=0이므로 대기하게 된다.
- 모든 작업을 마치면 signal한다.
- signal하면서 대기하는 프로세스가 있는지 확인한다. 지금 대기하는 프로세스가 있으므로, 그 프로세스의 대기를 푼다.
- 모든 작업을 마치고 signal했을 때, 이제 더 이상 기다리눈 프로세스가 없으므로 S=1이 된다.
- S=0
- S=0이므로 wait하면 프로세스는 대기하게 된다.
- signal로 S값을 변경해야 하지만, 모든 프로세스가 대기하고 있으므로 S값은 변경되지 않는다.
- 그렇다고 S=0일 때 signal을 먼저 하면 모든 프로세스가 Critical-Section으로 들어오므로 해결이 되지 않는다.
- S=8
- S=8이므로 wait하면 S=7이 된다.
- 다른 프로세스가 와서 wait하면 S=6이 되고 들어오게 된다. 이미 Critical-Section이 지켜지지 않고 있다.
- 이렇게 8개의 프로세스가 동시에 작업할 때 까지 모든 프로세스는 접근할 수 있게 된다.
- 물론 Critical-Section은 깨지지만, 프로세스 개수를 조절할 수 있다.
- 위와 같은 예시를 통해 이런 결론을 얻을 수 있다.
Semaphore를 초기화 할 때, N으로 초기화하게 되면 이후 원하는 영역에는 동시에 N개의 프로세스가 접근할 수 있다.
Unnamed-Semaphore
- 이름을 가지지 않고 생성할 수 있는 Semaphore다.
- Semaphore는 sem_t라는 타입을 가진다.
- #include <semaphore.h>를 해줘야 사용할 수 있다.
- unistd.h 안에 있는 _POSIX_SEMAPHORES가 있어야 사용할 수 있다.
Initialization/Destroy
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned value);
int sem_destroy(sem_t *sem);
- Semaphore 변수는 선언하고 무조건 초기화를 해주어야 한다.
- sem_init : Semaphore 변수를 초기화하는 함수이다.
- sem_t *sem : 생성한 Semaphore를 넣어준다.
- int pshared : 이 Semaphore를 공유할 것인지 설정한다. 0이면 초기화한 프로세스의 스레드들끼리만 사용 가능하다. 1이면 다른 프로세스의 스레드들도 사용 가능하다.
- unsigned value : 초기화 값이다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
- sem_destroy : Semaphore 변수를 제거하는 함수이다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
Semaphore Operations
#include <semaphore.h>
int sem_post(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_wait(sem_t *sem);
- sem_post : semaphore 변수의 signaling을 구현해놓았다. Signal-safe하다.
- sem_wait : semaphore 변수를 기다리게 한다.
- sem_trywait : wait를 시도해본다. 만약 semaphore의 값을 줄일 수 있다면 줄이며, 줄일 수 없다면 에러 코드와 같이 return한다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
#include <semaphore.h>
int sem_getvalue(sem_t *restrict sem, int *restrict sval);
- sem_getvalue : semaphore의 현재 값을 확인하고자 할 때 사용한다. 값에 영향을 주진 않는다.
- sem_t *restrict sem : 값을 읽을 semaphore이다.
- int *restrict sval : 값을 반환하는 output parameter이다.
- sval에 값을 넣고 있을 때 실제 값이 변화할 수 있으므로, 확실한 값이 아닐 수 있다.
- 성공하면 0을 반환하며, 실패하면 에러 코드를 반환한다.
Named-Semaphore
- 위와 다르게 이름을 가지는 Semaphore다.
- 그러므로 Semaphore의 이름과 권한만 가지고 있으면 어떤 프로세스든지 Semaphore에 접근할 수 있다.
- 이름은 '/'로 시작해야 하며, 같은 이름의 Semaphore를 통해서 동기화 할 수 있다.
- 만약 이름이 '/'로 시작하지 않으면, 예상치 못한 오류가 일어날 수 있다.
Creating and opening
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag, …)
- sem_open : 새로운 semaphore를 만들거나 기존의 semaphore에 접근하는 함수이다.
- const char *name : semaphore의 이름이다.
- int oflag : 만들거나 접근하는 행동을 설정한다.
- O_CREAT로 설정하면 name에 적혀있는 이름을 가진 semaphore를 생성한다. 이미 존재하면 생성하지 않고(무시하고) 접근한다.
- O_CREAT와 O_EXCL를 같이 설정하면 에러를 반환한다.
- 성공하면 semaphore의 주소를 반환하며, 실패하면 에러 코드와 같이 SEM_FAILED를 반환한다.