C언어 세마포어 생성 및 접근 함수 semget()

2020. 3. 16. 09:41 컴퓨터/프로그래밍

C함수 세마포어 생성 및 접근 semget()

semget() 함수는 세마포어 집합을 생성하거난 이미 생성된 세마포어에 접근하기 위해 사용합니다.

  • 헤더: sys/types.h, sys/ipc.h, sys/sem.h
  • 형태: int semget ( key_t key, int nsems, int semflg )
  • 인수: key_t key 시스템에서 세머포어를 식별하는 집합 번호
    int nsems 세마포어 집합 내의 세마포어 개수로 접급 제한하려는 자원의 개수
    int semflg 동작 옵션

    semflg 옵션 내용
    IPC_CREATE key에 해당하는 공유 세머포어가 없다면 새로 생성한다. 만약있다면 무시하며 생성을 위해 접근 권한을 지정해 주어야 한다.
    IPC_EXCL 세머포어가 이미 있다면 실패로 반환하며 세머포어에 접근하지 못한다. 이 옵션이 없어야 기존 세마포어를 사용할 수 있다.
  • 반환: -1 == 실패, -1 이외 새로 만들어진 세마포어 식별자 또는 key 와 일치하는 세마포어 식별자

세마포어는 공유 자원을 동시에 여러 프로세스가 사용하는 것을 막고, 오직 하나의 프로세스만 사용할 수 있도록 보장해 주는 방법입니다. 즉, 여러 프로세스 중 공유 자원을 자신이 사용할 수 있는지 있다면 작업이 끝나기 전까지 다른 프로세스가 공유 자원을 사용할 수 없도록 다른 프로세스의 접근을 제어하는 방법을 세마포어를 이용하여 구현할 수 있습니다.

세마포어를 제일 간단히 설명한다면 특정 변수를 두고, 이 변수를 미리 확인합니다. 특정 변수를 확인하는 함수를 p()라고 하겠습니다.

  1. 프로세스는 공유자원을 사용하기 전에 p()함수를 호출합니다.
  2. p()는 특정 변수 값을 확인하여 공유 자원이 다른 프로세스가 사용 중인지의 여부를 판단하고, 다른 프로세스가 사용 중이면 그 프로세스가 사용을 중지할 때까지 대기합니다. 공유 자원이 사용 가능하다면 특정 변수값을 변경하고 복귀합니다.
  3. 프로세스는 공유 자원을 사용합니다.
  4. 프로세스는 공유 자원에 대한 작업을 완료했다면 s() 함수를 호출하여 특정 변수값을 원래의 값으로 복원합니다.

이렇게 프로세스가 s() 함수를 호출하여 변수값을 원래 값으로 변경하면 다른 프로세스의 p()함수는 공유 자원의 사용이 가능하다고 판단하게 됩니다.

예제

아래의 예제에서는 두개의 쓰레드를 만들어 하나의 자원인 카운터 cnt 를 서로 방해하지 않고 사용하는 모습을 보여 주고 있습니다.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <pthread.h>

int          cnt   = 0;
static int  semid;

void p()
{
   struct sembuf pbuf;

   pbuf.sem_num   = 0;
   pbuf.sem_op    = -1;
   pbuf.sem_flg   = SEM_UNDO;

   if ( -1 == semop(semid, &pbuf, 1))
      printf( "p()-semop() 실행 오류\n");
}
void v()
{
   struct sembuf vbuf;

   vbuf.sem_num   = 0;
   vbuf.sem_op    = 1;
   vbuf.sem_flg   = SEM_UNDO;

   if ( -1 == semop( semid, &vbuf, 1))
      printf( "v()-semop() 실행 오류\n");
}
void *fun_thread1(void *arg)
{
   while( cnt < 5){
      p();
      printf( "thread1 실행\n");
      cnt++;
      usleep( 100);
      printf( "thread1 종료\n");
      v();
   }
   return NULL;
}
void *fun_thread2(void *arg)
{
   while( cnt < 5){
      p();
      printf( "thread2 실행\n");
      printf( "카운터= %d\n", cnt);
      usleep( 100);
      printf( "thread2 종료\n");
      v();
   }
   return NULL;
}

int main(int argc, char *argv[])
{
   pthread_t thread1;
   pthread_t thread2;

   union semun{
      int                  val;
      struct   semid_ds   *buf;
      unsigned short int  *arrary;
   }  arg;
   if ( -1 == (semid = semget( IPC_PRIVATE, 1, IPC_CREAT ¦ 0666))){
      printf( "semget() 실행 오류\n");
      return -1;
   }
   arg.val  =  1;                // 세마포어 값을 1로 설정
   if ( -1 == semctl(semid, 0, SETVAL, arg)){
      printf( "semctl()-SETVAL 실행 오류\n");
      return -1;
   }
   pthread_create(&thread1, NULL, fun_thread1, NULL);
   pthread_create(&thread2, NULL, fun_thread2, NULL);
   pthread_join( thread1, NULL);
   pthread_join( thread2, NULL);
   if ( -1 == semctl(semid, 0, IPC_RMID, arg)){
      printf( "semctl()-IPC_RMID 실행 오류\n");
      return -1;
   }
   printf( "프로그램 종료\n");
   return 0;
}

실행 결과

]$ gcc test.c -lpthread
]$ ./a.out
thread2 실행
카운터= 0
thread2 종료
thread1 실행
thread1 종료
thread2 실행
카운터= 1
thread2 종료
thread1 실행
thread1 종료
thread2 실행
카운터= 2
thread2 종료
thread1 실행
thread1 종료
thread2 실행
카운터= 3
thread2 종료
thread1 실행
thread1 종료
thread2 실행
카운터= 4
thread2 종료
thread1 실행
thread1 종료
thread2 실행
카운터= 5
thread2 종료
프로그램 종료
]$
이 댓글을 비밀 댓글로