Semaphore - 코드로 이해하기

두비니

·

2021. 6. 2. 03:52

 

 

 

 

 

앞선 글들에서 공유메모리를 사용하기 위한 방법을 서술하였습니다.

그러나 이런 기법을 구현할 때, synchronization관련 이슈를 해결하는 것은 필수적입니다.

이런 부분들을 고려하지 않는다면 race condition등의 이슈가 발생합니다.

 

 

1. (IPC) Semaphore?

 

따라서 사용하는 대표적인 수단 중 하나가 semaphore입니다.

특히 이번 글에서 다룰 것은 IPC Semaphore인데, 이는 공유하는 자료 구조에 대한 통제된 접근을 위해서 사용됩니다.

 

주요 특징을 정리하자면,

  • 기본적으로 semaphore은 양의 정수값을 가집니다.
  • 자원에 프로세스가 접근할 때 마다 semaphore값은 감소합니다.
  • semaphore값이 0이되면 semaphore값이 다시 양수가 될 때까지 프로세스의 접근을 막습니다.
  • 프로세스가 보호된 자원을 해지하는 경우 semaphore의 값을 증가시킵니다.

 

이런식으로 사용됩니다. 나중에 다룰 수 있다면 다룰건데, mutex와 기본적으로 같은 맥락을 가지고 있습니다.

 

2. 관련 함수들

** 모든 sem* 함수들은, 원자적으로(atomic)하게 진행됩니다. **

1) semget

int semget(key_t key, int nsems, int semflg);

semget함수는 semaphore을 처음 정의할 때 사용하는 함수입니다.

매개변수의 뜻은 다음과 같습니다.

  • key : 시스템에서 세머포어를 식별하는 집합 번호, 이걸 기반으로 semid 생성
  • nsems : 세마포어 집합 내의 세마포어 개수로 접급 제한하려는 자원의 개수
  • semflg : 동작 옵션
    • 동작 옵션의 경우 참고링크 첨부합니다.
  • 리턴값 : semid

 

참고 : https://man7.org/linux/man-pages/man2/semget.2.html

 

semget(2) - Linux manual page

semget(2) — Linux manual page SEMGET(2) Linux Programmer's Manual SEMGET(2) NAME         top semget - get a System V semaphore set identifier SYNOPSIS         top #include #include int semget(key_t key, int nsems, int semflg); DESCRIPTION    

man7.org

 

 

2) semop

int semop(int semid, struct sembuf *spos, size_t nsops);

semaphore의 값을 변경하고 싶은 경우 이 함수를 호출합니다.

매개변수의 뜻은 다음과 같습니다.

  • semid : 시스템에서 세머포어를 식별하는 집합 번호
  • sops : 세마포어 값을 계산하기위한 설정 값
  • nsops : 변경하려는 세마포어 개수로 변경하려는 세마포어 개수가 여러 개일 때 한정해서 사용
  • 리턴값
    • 0 : 성공
    • 1 : 실패

 

참고) struct sembuf는 다음과 같습니다.

struct sembuf {
    short sem_num;   //세마포어 번호
    short sem_op;     //세마포어 증감값
    short sem_flg;     //옵션
}

 

 

3) semctl

int semctl(int semid, int semnum, int cmd, union semun arg);

shmctl처럼 컨트롤할 수 있는 함수입니다.

매개변수는 다음과 같습니다.

  • semid : 시스템에서 세머포어를 식별하는 집합 번호
  • semnum : 세마포어 집합 내에서의 세마포어 위치
  • cmd : 제어 명령. 자세한 건 1번함수에 참조한 문서를 확인 부탁드립니다.
  • arg : cmd 에 따라 달라지며, 설정 또는 값을 구하는 변수

 

참고) union semun은 다음과 같이 생겼습니다.

union semun{
   int                  val;
   struct   semid_ds   *buf;
   unsigned short int  *arrary;
}

 

 

 

3. 예제 코드

 

예제 코드는 semaphore가 없을때, 그리고 있을 때와 비교하는걸로 하겠습니다.

 

다음 코드는 저번 실습때 사용한 코드에서 마지막에 for문만 추가한 것입니다.

semaphore가 없이 코드가 작성되었다는 부분을 보시면 될 것 같습니다.

 

 

다음은 semaphore를 추가한 코드입니다.

얼핏 보기에는 엄청 길어진 것처럼 보이지만, 그냥 semaphore을 만들고 설정하기 위한 부분들만 작성한 것이니 천천히 코드를 보시면 도움이 될 것 같습니다.

그럼 이제 결과를 봅시다.

run.sh파일로 한번에 실행을 시켰는데, 그냥 두 파일을 모두 동시에 실행시키는 코드를 작성하였습니다.

다음의 코드는 한번에 너무 많은 접근이 일어나므로, 원래는 1000000번이 되어야하지만, race condition으로 인해 제대로 counting이 안됩니다.

반면에 semaphore을 포함한 코드는 제대로 counting된 것을 볼 수 있습니다.