본문 바로가기

컴퓨터 과학/리눅스 프로그래밍

[이론] 세마포어(Semaphore)

반응형

 세마포어는 Edsger Wybe Dikstra가 고안한 두 개의 원자적 함수(=한 번 실행되면 중간에 멈출 수 없는 함수)로 조작되는 두 정적 변수를 의미한다. 공유 자원에 대한 접근을 제한하는 방법으로 사용된다.

 

식사하는 철학자들 문제(The Dining-Philosophers Problem) : 동시성과 교착 상태를 설명하는 문제

식사하는 철학자들 문제

 

주어진 상황과 같이 5 명의 철학자들 앞에는 5 개의 스파게티가 있고, 스파게티 사이마다 포크가 있다. 철학자들은 서로 대화할 수 없다. 철학자가 스파게티를 먹기 위해서는 양 옆의 포크를 동시에 들어야 한다. 만약 모든 철학자들이 왼쪽 포크를 들고 오른 쪽 포크를 든다면, 모든 철학자들이 오른 쪽 포크를 들 수 없어서 스파게티를 먹지 못하는 교착 상태에 빠질 수 있다. 이러한 문제를 식사하는 철학자들 문제라 한다.

 

해결 방법 : 세마포어 변수, P(감소), V(증가) 연산을 이용하여 동기화 한다.

P(sem) :
	if(sem > 0){
    	sem--;
    }
    else{
    	wait until sem > 0;
        sem--;
    }
    
V(sem) : 
	sem++;
    wakeup waiting process

 

리눅스는 세마포어 기능을 <sys/sem.h>를 이용하여 제공한다.

semget() : 세마포어 생성

semop() : 세마포어 연산(p, v)

semctl() : 세마포어 제어 및 삭제

 

다음은 세마포어를 이용하여 공유 자원을 관리하는 예제이다. cnt는 전체 자원의 개수가 된다.

#include <stdio.h>
#include <unistd.h>
#include <sys/sem.h>

int cnt = 0;
static int semid;

void p() /* 세마포어의 P 연산 */
{
    struct sembuf pbuf;
    pbuf.sem_num = 0; //배열에서의 세마포어의 인덱스, 세마포어가 여러 개일 수 있다.
    pbuf.sem_op = -1; //세마포어 동작
    pbuf.sem_flg = SEM_UNDO; //동작을 위한 플래그

    if (semop(semid, &pbuf, 1) == -1) /* 세마포어의 감소 연산을 수행한다. */
        perror("p : semop()");
}

void v() /* 세마포어의 V 연산 */
{
    struct sembuf vbuf;
    vbuf.sem_num = 0;
    vbuf.sem_op = 1;
    vbuf.sem_flg = SEM_UNDO;

    if (semop(semid, &vbuf, 1) == -1) /* 세마포어의 증가 연산을 수행한다. */
        perror("v : semop()");
}

int main()
{

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

    /* 세마포어에 대한 채널 얻기 */
    if ((semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666)) == -1)
    {
        perror("semget()");
        return -1;
    }

    arg.val = 1; /* 세마포어 값을 1로 설정 */
    if (semctl(semid, 0, SETVAL, arg) == -1)
    {
        perror("semctl() : SETVAL");
        return -1;
    }

    while (1)
    {
        if (cnt >= 8)
        {
            cnt--;
            p();
            printf("decrease : %d\n", cnt);
            break;
        }
        else
        {
            cnt++;
            v();
            printf("increase : %d\n", cnt);
            usleep(100);
        }
    }

    /* 세마포어에 대한 채널 삭제 */
    if (semctl(semid, 0, IPC_RMID, arg) == -1)
    {
        perror("semctl() : IPC_RMID");
        return -1;
    }

    return 0;
}

코드 출처 : https://github.com/valentis/LinuxProgrammingWithRaspberryPi

 

GitHub - valentis/LinuxProgrammingWithRaspberryPi: Linux Programming With Raspberry Pi

Linux Programming With Raspberry Pi. Contribute to valentis/LinuxProgrammingWithRaspberryPi development by creating an account on GitHub.

github.com

 

반응형