시스템 소프트웨어

세마포어 (Semaphore)

masimelo 2023. 3. 11. 22:32

세마포어

세마포어

  • 프로세스 간 통신을 위한 메커니즘
  • 다른 메커니즘과 다르게 프로세스 간 데이터를 동기화하고 보호하는 데에 목적이 있음
    • 메시지 큐, 공유 메모리 등의 메커니즘은 프로세스 간 데이터 전송을 목적으로 함
  • 공유 메모리를 통해서 프로세스 간 통신을 하는 등의 공유된 자원에 여러 개의 프로세스가 동시에 접근을 하면, 데이터가 손상될 수 있음
  • 여러 개의 프로세스에 의해 공유되는 자원의 접근 제어를 위한 도구임
    • 커널에서 전역적으로 관리함
  • 경쟁 상태
    • 여러 개의 프로세스가 공유된 자원에 동시에 접근하는 경우에 데이터를 사용하기 위해 경쟁하는 현상
  • 임계영역
    • 여러 개의 프로세스에 의해 접근되는 공유 영역
    • 손상되지 않게 보호되어야 하는 자원가 연산을 포함한 영역
  • 동기화
    • 경쟁 상태를 해결하기 위해 프로세스의 동작을 제어하는 프로토콜

세마포어의 작동 원리

  • 세마포어는 상호 배제 알고리즘으로 임계영역의 자원을 보호함
  • 차단을 원하는 자원에 대해서 세마포어를 생성하면, 해당 자원을 가리키는 세마포어가 할당됨
    • 세마포어 값을 검사하여, 자원에 접근할 수 있는지 여부를 결정함
  • 세마포어 값이 0인 경우에, 임계영역의 자원에 접근할 수 없음
  • 세마포어 값이 0 보다 큰 정수인 경우에, 해당 정수의 크기만큼 프로세스가 접근할 수 있음
  • 세마포어의 값이 1인경우로 예를 들어봄
    • 프로세스가 임계영역의 자원에 접근하면, 세마 포어의 값을1 감소시켜 다른 프로세스의 접근을 방지함
    • 자원 사용을 종료하면 세마포어 값을 1 증가시켜 다른 프로세스의 접근을 허가함
  1. IPC키 회득
  2. 세마포어 생성
  3. 임계영역 잠금
  4. 공유자원사용
  5. 임계영역 해제
  6. 세마포어 제거

세마포어 함수

ftok()

semget()

#include <sys/sem.h>
#include <sys/types.h>

int semget(key_t key, int nsems, int semflags);
  • 설명
    • 세마포어 또는 세마포어 집합을 생성하고 접근할 수 있는 식별자를 반환함
  • 인자
    • key : 세마포어 또는 세마포어 집합을 식별하는 키값
    • nsems : 생성하고자 하는 세마포어의 개수
      • 최초로 세마포어를 생성하는 생성자의 경우에는 보통 1을 사용함
      • 그 외의 세마포어를 사용하는 소비자의 경우에 0을 사용함
    • semflags : 세마포어의 생성에 관한 플래그
  • 반환
    • 성공하면, 세마포어 식별자 또는 세마포어 집합 식별자
    • 실패하면 -1

struct sembuf

#include <sys/sem.h>
#include <sys/types.h>
struct sembuf
{
    short sem_num; // 세마포어 집합의 인덱스 사이즈
    short sem_op;  // 세마포어 연산
    short sem_flg; // 연산 옵션
};
  • sem_num
    • 세마포어 집합의 인덱스 사이즈
    • 보통 하나의 세마포어를 지정해서 사용하므로 0을 사용함
  • sem_op
    • 세마포어 연산 동작
    • 양의 정수인 경우에 세마포어 값을 해당 정수만큼 증가시킴 (다른 프로세스의 접근을 허용함)
      • 보통 1을 사용
    • 음의 정수인 경우에 세마포어 값을 해당 정수의 절대값만큼 감소 시킴
      • 보통 -1을 사용
  • sem_flg
    • 세마포어 연산 동작에 관한 플래그
    • IPC_NOWAIT
      • 임계영역에 접근 할 수 없는 경우에 대기하지 않음
    • SEM_UNDO
      • 프로세스가 세마포어를 반환하지 않고 종료하는 경우에 커널에서 스스로 세마포어 값을 증가 시킴

semop()

#include <sys/sem.h>
#include <sys/types.h>

int semop(int semid, struct sembuf * buf, unsigned int nsops);
  • 설명
    • 세마포어 또는 세마포어 집합의 값을 변경함
  • 인자
    • semid : 세마포어의 식별자
    • buf : 세마포어 연산을 저장한 메모리 영역의 주소
    • nsops : 변경하고자 하는 세마포어의 개수
  • 반환
    • 성공하면 0
    • 실패하면 -1

struct semid_ds

#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
struct semid_ds
{
    struct ipc_perm sem_perm; // 퍼미션
    struct sem *sem_base;     // 집합의 첫번째 세마포어를 가리키는 포인터
    time_t sem_otime;         // 마지막 연산 시간
    time_t sem_ctime;         // 마지막 변경 시간
    unsigned short sem_nsems; // 세마포어 개수
};

union semun

  • 사용자 정의 공용체
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h> 
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short int *array;
};

semctl()

#include <sys/sem.h>

int semctl(int semid, int semnum, int cmd, union semun buf);
  • 설명
    • 세마포어 또는 세마포어 집합을 제어함
  • 인자
    • semid : 세마포어의 식별자
    • semnum : 세마포어 집합에서 해당 세마포어의 인덱스
    • cmd : 제어 동작에 관한 플래그
      • GETVAL : 세마포어를 값을 지정한 메모리 공간에 저장함.
      • GETPID : 세마포어를 가장 최근에 사용한 프로세스의 PID를 지정한 메모리 공간에 저장함
      • GETNCNT : 세마포어 값이 증가하기를 기다리는 프로세스의 개수를 지정한 메모리 공간에 저장함
      • GETZCNT : 세마포어 값이 0이 되기를 기다리는 프로세스의 개수를 지정한 메모리 공간에 저장함
      • GETALL : 세마포어 집합의 모든 세마포어 값을 지정한 메모리 공간에 저장함
      • SETVAL : 세마포어 값을 설정함
      • SETALL : 세마포어 집합의 모든 세마포어 값을 설정함
      • IPC_RMID : 지정한 세마포어를 제거함
      • IPC_SET : 세마포어의 정보를 지정한 정보로 변경함
      • IPC_STAT : 세마포어의 정보를 지정한 메모리 공간에 저장함
    • buf : 제어에 사용되는 공용체
  • 반환
    • 성공하면 0
    • 실패하면 -1
  • 예시
    • shm 헤더 (MyShm.h)
#ifndef __MYSHM_H__
#define __MYSHM_H__
#define SHM_MAX_SIZE 512
#define SHM_PERMS 0600
#define MY_SHM_KEY 1234
int creatShm(int key);
int openShm(int key);
int closeShm(int shmid);
#endif // !__MYSHM_H__AKG

#include "MyShm.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

int creatShm(int key){
    int shmid = shmget((key_t)key, SHM_MAX_SIZE, 
																	IPC_CREAT|IPC_EXCL|SHM_PERMS);
    if(shmid == -1){
        perror("createShm() error");
        exit(0);
    }

    return shmid;
}

int openShm(int key){
    int shmid = shmget((key_t)key,SHM_MAX_SIZE,IPC_CREAT);

    return shmid;
}

int closeShm(int shmid){
    return shmctl(shmid, IPC_RMID,0);
}
  • sem 헤더 (MySem.h)
#ifndef __MYSEM_H__
#define __MYSEM_H__
#define SEM_MAX_NUM 1
#define SEM_ERMS 0600
#define MY_SEM_SERV_KEY 1357
#define MY_SEM_CLNT_KEY 2468
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short int *array;
};
int creatSem(int key);
int openSem(int key);
int lockSem(int semid);
int unlockSem(int semid);
int closeSem(int semid);
#endif // !__MYSEM_H__

#include "MySem.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>

int creatSem(int key){
    int semid = semget((key_t)key, SEM_MAX_NUM,IPC_CREAT|SEM_ERMS);
    if(semid == -1){
        perror("creatSem() error");
        exit(0);
    }
    return semid;
}

int openSem(int key){
    int semid = semget((key_t)key, SEM_MAX_NUM, IPC_CREAT);
    return semid;
}

int lockSem(int semid){
    struct sembuf buf = {0,-1,SEM_UNDO};
    return semop(semid , &buf, SEM_MAX_NUM);
}
int unlockSem(int semid){
    struct sembuf buf = {0,1,SEM_UNDO};
    return semop(semid, &buf, SEM_MAX_NUM);
}

int closeSem(int semid){
    return semctl(semid, 0,IPC_RMID,0);
}
  • 서버
#include "MyShm.h"
#include "MySem.h"

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/types.h>

void signalHandler(int signum);

int shmid =0;
int *shmaddr = NULL;
int semidServ = 0;
int semidClnt =0;

int main(int argc, char const *argv[]){
    int req =0;
    int res =0;

    union semun semunBuf;

    shmid = creatShm(MY_SHM_KEY);
    shmaddr = shmat(shmid, NULL,0);

    semidServ = creatSem(MY_SEM_SERV_KEY);
    semunBuf.val = 0;
    semctl(semidServ, 0, SETVAL,semunBuf);

    semidClnt = creatSem(MY_SEM_CLNT_KEY);
    semunBuf.val = 1;
    semctl(semidClnt,0,SETVAL, semunBuf);

    signal(SIGINT, signalHandler);

    while(1){
        puts("wait ....");

        lockSem(semidServ);
        memcpy(&req, shmaddr,sizeof(int));
        res = req + 1;
        memcpy(shmaddr, &res,sizeof(int));
        unlockSem(semidClnt);

        printf("Request : %d\\n",req);
        printf("Response : %d\\n",res);
        fflush(stdout);
    }
    return 0;

}

void signalHandler(int signum){
    if (signum ==SIGINT){
        closeSem(semidClnt);
        closeSem(semidServ);
        shmdt(shmaddr);
        closeShm(shmid);
        exit(0);
    }
}
  • 클라이언트
#include "MySem.h"
#include "MyShm.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/types.h>

void signalHandler(int signum);

int shmid =0;
int *shmaddr = NULL;
int semidServ = 0;
int semidClnt =0;

int main(int argc, char const*argv[]){
    int req =0;
    int res =0;

    shmid = openShm(MY_SHM_KEY);
    shmaddr = shmat(shmid,NULL,0);

    semidServ = openSem(MY_SEM_SERV_KEY);
    semidClnt = openSem(MY_SEM_CLNT_KEY);

    signal(SIGINT, signalHandler);

    while(1){
        printf("<< ");
        scanf("%d", &req);
        fflush(stdout);
        fflush(stdin);

        lockSem(semidClnt);
        memcpy(shmaddr, &req, sizeof(int));
        unlockSem(semidServ);

        lockSem(semidClnt);
        memcpy(&res,shmaddr, sizeof(int));
        unlockSem(semidClnt);

        printf(">> %d\\n",res);
    }
    return 0;
}
void signalHandler(int signum){
    if (signum == SIGINT){
        shmdt(shmaddr);
        exit(0);
    }
}

'시스템 소프트웨어' 카테고리의 다른 글

공유 메모리 (Shared Memory)  (0) 2023.03.11
메세지 큐 (Massage Queue)  (0) 2023.03.09
파이프 (Pipe)  (0) 2023.03.09
연결 리스트 (Linked list)  (0) 2023.03.08
배열과 문자열 (Array and String)  (0) 2023.03.07