Search

<semaphore.h>

Created
2021/07/20
tag
C
Philosophers
semaphore.h
process
semaphore

Subjects

<semaphore.h>에는 semaphore를 다룰 수 있는 함수들이 다수 존재한다. <semaphore.h> 내에서 사용할 수 있는 semaphoreNamed SemaphoreUnnamed Semaphore로 나뉘는데, Mac OS X에서는 Unnamed SemaphoreDeprecated로 두어 이와 관련된 함수들을 허용하지 않는다. 물론 Linux 상에서는 <semaphore.h>에 존재하는 모든 함수들을 이용할 수 있지만, 여기서는 Mac OS X에서 사용할 수 있는 함수들만 다룬다는 것을 사전에 밝힌다.
또한 단순히 semaphore를 쓸 수 있고 없고에 대한 공부도 좋지만, Async-Signal-Safe, Thread-Safe, Reentratn와 같은 키워드를 접함으로써 이들에 대한 개념 정리를 해보는 것도 좋겠다는 생각이 들었다. 따라서 제시된 3가지 키워드에 대해서도 꼭 찾아보는 것을 권한다.

1. Semaphore

1) sem_open

함수 원형

sem_t *sem_open(const char *name, int oflag); sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
C
복사

함수 인자

semaphore의 이름을 const char * 타입의 name으로 결정할 수 있다.
oflagsem_open을 수행 작업에 대한 설정 값으로 O_CREATO_EXCL을 사용할 수 있다.
modesemaphore에 대한 권한 설정 값으로 8진수 값으로 기재하면 되는데, <stat.h>를 포함할 시에는 해당 헤더 내에 있는 매크로 값을 이용할 수도 있다.
valuesemaphore가 보유하고 있는 LOCK의 수를 설정할 때 이용된다.

반환 값

sem_open 작업을 성공했을 때는 semaphore에 대한 주소 값을 반환한다. 실패 시에는 SEM_FAILED라는 매크로 값을 반환한다.
sem_tint를 단순히 타입 정의한 타입에 불과하다. 이는 일반적으로 파일 디스크립터 값을 나타내기 때문에 sem_t *파일 디스크립터 값을 참조하고 있는 포인터에 불과하다. SEM_FAILED(sem_t *)-1 값이기 때문에 포인터 변수 내에 모든 비트가 1로 채워진 값을 의미한다.

참고

O_CREATsemaphore 생성에 대한 값이고, O_EXCLexclude를 의미하여 기존에 이미 name이라는 semaphore가 유지되고 있는지 확인하는 값이다.
함수 원형이 2개가 주어졌는데, oflag의 값으로 O_CREAT이 포함되는 경우에는 필수적으로 인자가 4개짜리 함수 원형을 이용해야 한다. 인자가 2개짜리인 함수 원형은 O_EXCL을 사용할 때만 이용한다.
oflag의 값은 생략할 수 없기 때문에 필수적으로 값을 채워야하므로 O_EXCL을 수행할 것이 아니라면 반드시 O_CREAT는 포함될 수 밖에 없다. 따라서 이미 존재하는 semaphore에 대해서 sem_open을 수행해야 한다면, O_CREAT이 포함됨에 따라 semaphore를 또 생성하게 되는 것 아닌가 라는 의문과 인자가 4개짜리인 함수를 이용해야 하니 mode와 value 값을 필수적으로 기재함에 따라 기존 값에 영향을 끼치는 것이 아닌가 하는 의문이 생길 수 있다.
우선 O_CREATname에 대한 semaphore가 존재하지 않을 때만 semaphore를 생성한다. 그리고 name에 대한 semaphore가 이미 존재할 때 O_CREAT을 통해 sem_open이 수행되면, 이 때 인자로 사용한 modevalue는 무시된다.
즉, modevalue는 기존 값을 유지한다.
O_EXCL이 포함된 경우에 sem_open 함수는 name이라는 semaphore가 이미 존재할 경우에 문제 상황에 대한 errno를 반환하도록 동작한다.
oflag에 대해서 <fcntl.h>의 다른 매크로 값들을 사용하는 행위는 코드의 이식성을 저하하므로 사용하지 않도록 한다. 일부 시스템에서는 이들을 Undefined Behavior로 보기도 하고, 무시하기도 하기 때문이다.

2) sem_unlink

함수 원형

int sem_unlink(const char *name);
C
복사

함수 인자

semaphore를 삭제할 수 있도록 cosnt char * 타입의 name 인자를 사용한다.

반환 값

sem_unlink 작업을 성공했을 때는 0을 반환한다. 실패 시에는 -1을 반환한다.

참고

semaphoreopenclose를 한다고 해서 시스템 상에서 없어지지 않는다. 물론 시스템을 재부팅하게 되면 기존에 이용하고 있던 semaphore들은 삭제되지만, 그 전까지는 컴퓨팅 자원을 소모하고 있게 된다. 따라서 사용하지 않는 semaphore에 대해서는 반드시 sem_unlink를 통해 삭제해줘야 한다.
sem_unlinksemaphore의 최초 sem_open 호출 직후에 바로 호출되기도 한다. 이에 따라 semaphore가 생성되자마자 삭제되는 것처럼 보이지만, 실제로는 그렇지 않다. 프로세스 혹은 쓰레드에 의해 참조되고 있지 않는 semaphore는 즉시 삭제되지만, 단 하나의 작업이라도 semaphoresem_open 하여 사용하고 있다면 sem_close를 통해 semaphore의 참조 값이 0이 되기 전까지는 삭제되지 않는다. 따라서 sem_unlink를 미리 호출해두어도 semaphore를 즉시 삭제하지 않는 것이다.

3) sem_close

함수 원형

int sem_close(sem_t *sem);
C
복사

함수 인자

대상이 되는 semaphore의 주소를 인자로 받는다.

반환 값

sem_unlink 작업을 성공했을 때는 0을 반환한다. 실패 시에는 -1을 반환한다.

참고

close 함수와 동일하게, 프로세스가 종료되면 sem_opensemaphore에 대해 자동으로 sem_close를 수행한다. 이는 execve와 같은 exec 계열 함수에 대해서도 자동으로 sem_close가 수행된다.

4) sem_trywait

함수 원형

int sem_trywait(sem_t *sem);
C
복사

함수 인자

LOCK을 취득하고자 하는 semaphore의 주소를 인자로 받는다.

반환 값

sem_trywait 작업을 성공했을 때는 0을 반환한다. 실패 시에는 -1을 반환한다.

참고

LOCK을 취득할 수 있다면 LOCK을 취득한 뒤에 함수가 종료되고, 취득할 수 있는 LOCK이 없어도 함수가 종료된다. sem_trywait에 대해선 취득할 수 있는 LOCK이 없는 것도 함수 수행 실패로 간주되어 -1이 반환된다.

5) sem_wait

함수 원형

int sem_wait(sem_t *sem);
C
복사

함수 인자

LOCK을 취득하고자 하는 semaphore의 주소를 인자로 받는다.

반환 값

sem_wait 작업을 성공했을 때는 0을 반환한다. 실패 시에는 -1을 반환한다.

참고

LOCK을 취득할 수 있다면 LOCK을 취득한 뒤에 함수가 종료되고, 취득할 수 있는 LOCK이 없으면 sem_trywait과는 달리 Sleep 상태로 대기했다가 LOCK을 얻는다. LOCK을 당장 얻을 수 있는지 없는지 여부가 중요하지 않음에도 sem_trywait과 같이 int 값을 반환하는 이유는 문제 상황을 감별하기 위해서이다.

6) sem_post

함수 원형

int sem_post(sem_t *sem);
C
복사

함수 인자

LOCK을 반납할 수 있도록 LOCK을 갖고 있는 semaphore의 주소를 인자로 받는다.

반환 값

sem_unlink 작업을 성공했을 때는 0을 반환한다. 실패 시에는 -1을 반환한다.

참고

sem_post의 호출은 semaphore의 현재 LOCK 수를 증가 (increment)하는 작업을 유도한다. 이 때 결과적으로 semaphore의 현재 LOCK 수는 적어도 0보다 크게 되는데, sem_wait에 의해 Sleep 상태로 있는 다른 프로세스 혹은 쓰레드를 깨워서 LOCK을 잡을 수 있도록 해준다.

2. Reference