POSIX semaphore를 이용하려면 mutex와 마찬가지로 객체를 초기화하거나 생성하는 작업이 필요하다.
Critical section을 4가지로 나눠보자
Entry section : 임계영역에 들어가기 전에 OS에게 임계영역에 들어가도 되냐고 허가를 묻는 섹션(뮤텍스를 사용한다면 뮤텍스를 초기화하고 lock을 요청하는 코드가 될 것이다), 허가를 받으면 들어올 수 있고 그렇지 못하면 대기한다.
critical section : 공유 리소스를 동시에 접근해서 충돌이 발생하지 않도록 하는 구역, 이코드는 nonreentrant(코드를 실행하고 난 다음에 다른 프로세스가 이 코드에 진입을 해서는 안되는 것)하다
exit section : 임계영역에서 코드를 다 실행한 다음에 exit section으로 진입을 해서 lock을 다시 반납해야 한다. 대기하고 있던 다음 프로세스에게 lock을 주기 위해서 엑시트섹션에서 lock을 release 해야 한다.
remainder section : exit section이후를 나머지 섹션이라고 표현한다.
임계영역 문제를 해결하기 위한 여러 가지 해결책을 위한 3가지 조건
1. Mutual Exclusion : 임계영역에서는 한 번에 하나의 프로세스만 진입할 수 있다.
2. Progress : 만약 임계영역에 어떠한 프로세스가 없고 임계영역에 진입하길 원하는 프로세스가 있다면 이러한 대기하는 프로세스 중(remainder section에 있지 않은 프로세스)에서 하나는 임계영역에 진입할 수 있어야 한다.
3. Bounded Waiting : 기다리는 프로세스는 기다리는 시간이 한정적이어야 한다.(bounded waiting 해야 한다). 한없이 기다려서는 안 되고 제한시간 안에는 임계영역에 접근할 수 있어야 한다. 즉, 기다리는 모든 프로세스들에게 동등한 기회를 주기 위해서
Semaphores : 프로세스들이 동기화할 때 다양한 방식으로 동기화하도록 하는 동기화 메커니즘이다.
세마포를 이용하면 임계영역 문제해결은 기본이고 다른 방식의 프로세스들의 동기화도 구현이 가능하다.
세마포는 int 변수인데 수행할 수 있는 오퍼레이션이 두개가 있다. atomic 오퍼레이션은 원자성 오퍼레이션으로 더이상 쪼갤 수 없는 오퍼레이션을 의미한다. 즉, 이 오퍼레이션이 실행중일때 다른 오퍼레이션이 끼어들 수 없는것을 의미한다. atomic 오퍼레이션은 개발자입장에서 할 수 있는것이 아니고 커널영역에서 OS가 지원해줘야한다. 세마포는 wait와 signal 오퍼레이션을 수행할 수 있는 정수형 변수이다.그래서 이 두개의 오퍼레이션을 이요아면 mutual exclusion과 동기화 문제를 구현할 수 있다.
wait함수는 세마포의 값이 0보다 크다면 정수값을 하나 감소시키고 값이 0이면 호출한 프로세스는 대기하게 된다.
signal함수는 세마포값을 증가시키는 함수이다. 만약 세마포의 값을 증가시키는 것이 아닌 대기하는 프로세스가 있다면 깨워주는 역할도 한다. 스레드나 프로세스가 블락되어 있을 경우에 세마포의 값을 0으로 하고 기다리고 있는 스레드나 프로세스하나를 깨운다. 만약 대기 중인 스레드가 없다면 세마포의 값을 하나 증가시킨다.
세마포를 이용해서 임계영역 문제를 해결하려면 세마포의 초기값을 1로 초기화를 하는것이 중요하다. 만약에 초기값을 0으로 초기화 했다면 모든 wait함수호출이 프로세스를 block시킬거고 deadlock에 빠질것이다. 초기값이 8이라면 임계영역에 8개의 프로세스가 진입할 수 있을것이다. 이 부분은 임계영역문제는 발생하지만 새로운 동기화가 생겼다. 세마포가 8이라면 8개의 프로세스가 진입가능세마포를 이용해서 또다른 방식으로 동기화하는 예제이다. concurrent한 환경에서 a와b중에서 누가 먼저 실행할지 할 수 없었는데 세마포를 이용해서 실행순서를 제어할 수 있다. a가 항상 b보다 먼저 실행이 되게끔 하기 위해서 sync라는 세마포의 초기값을 0으로 주자. 즉,어떤 프로세스가 먼저 실행이되든 상관없이 a가먼저 실행이되고 b가 실행되도록 할 수 있다.두개의 프로세스가 concurrent하기 때문에 누가 먼저 출력이 될지 알 수 없는 상황이다. 그러나 세마포를 사용해서 반복적으로 실행되는 상황도 제어할 수 있다.S와 Q를 1로 준다면 a와b중 누가 실행이 먼저될지는 모르지만 둘중 하나의 프로세스중에서 하나 이상의 iteration이 차이가 나지 않는다. 만약 세마포 하나는 1 다른건 0으로 줄 경우 프로세스들이 strict alternation(번갈아가면서)으로 실행된다. 둘다 0으로 할경우 deadlock에 빠지게된다.또다른 예제, 세마포의 값을 1로 줄경우 상황마다 다르다,어떤 프로세스가 CPU를 갖느냐에 따라서 동기화가 될 수도 있고 deadlock에 빠질수도 있다. 만약 P1이 wait를 실행한 다음에 CPU를 잃어버리고 CPU를 P2가 갖게된다면 두 프로세스 모두 첫번째 wiat만 실행을 하고 다음 wait문을 실행한다면 deadlock상태에 빠진다.
POSIX:SEM unnamed semaphores
unname세마포를 sem_init()함수로 초기화를 하자. 세마포를 다 쓰고 나면 seum_destory을 이용해서 반환하자. init의 두번재 파라미터 pshared를 0으로 선언하면 이 세마포는 이 세마포를 초기화한 프로세스의 스레들만 사용할 수 있다. 0이 아니라면 이 세마포 변수에 엑세스가능하다면 이 세마포를 사용할 수 있다.초기화를하고 난 다음에 세마포 변수로 사용할 수 있는 함수 3가지.sem_post함수는 세마포의 signal 오퍼레이션을 수행하는 함수이다. 이 함수는 signal-safe한 함수이다. sem_wait함수는 세마포의 wait함수를 구현한것이다. sem_trywait함수는 wiat의 비동기식 방식으로 세마포의값을 줄일수있으면 줄이고 줄이지못하면 블락되는것이 아니라 바로 -1을 리턴한다현재 세마포의 값을 확인하고 싶을떄 사용하는 함수, 이함수는 세마포의 값을 확인할때 어느 시점 값을 가져오는게 아니기 때문에 세마포의 값이 자주바뀌는 상태라면 조심해서 써야겠다.unnamed 세마포의 예제
POSIX:SEM Named semaphores
named 세마포에서는 이름을 지정할 수 있는데 / 로 시작해야한다.이름이 있는 세마포는 파일을 오픈할때 open함수를 쓰는것 처럼 sem_open함수를 써야한다. 두번쨰 파라미터에 O_CREATE를 넣으면 첫번째 파라미터로 지정한 이름의 파일이 없다면 새로 만들어서 오픈을 해주고 이름이 이미 있다면 같은 이름의 세마포가 오픈이 된다. 그러나 기존의 파일을 열어서 덮어씌어지는 문제를 막기 위해서 O_EXCL을 같이 쓰면 된다. 같은이름의 파일이 존재하면 에러를 리턴한다.