[42-Seoul/Philosophers] 과제 소개 및 함수 사용법

yebeen·2022년 12월 16일
0

42-Seoul/Philosophers

목록 보기
1/1

Dining-Philosophers Problem

식사하는 철학자들 문제는 동시성과 교착 상태를 설명하는 예시로 프로세스가 동시에 돌아갈 때 교착 상태가 나타나는 원인을 직관적으로 알 수 있습니다.

다섯 명의 철학자가 원탁에 앉아있고, 각자의 앞에 식사가 있고 양 옆에 젓가락이 하나씩 있습니다. 이 때 식사를 하기 위해서는 젓가락 한쌍이 되도록 양 옆의 젓가락을 동시에 들어야 합니다.
이런 상황에서 교착 상태나 기아 상태가 발생할 수 있고, 몇몇 철학자가 다른 철학자 보다 식사를 적게하는 경우가 발생하기도 합니다.


Mandatory part

스레드와 뮤텍스를 사용한 철학자의 구현

  • 두 철학자 사이에 한 개의 포크가 존재하므로, 철학자가 여러명일 경우 각 철학자의 왼쪽과 오른쪽에 포크가 하나씩 존재해야 합니다.
  • 철학자가 포크를 복제하는 것을 막기 위해서, 각 포크의 현재 상태를 뮤텍스를 이용하여 보호해주어야 합니다.
  • 각 철학자는 스레드로 구현되어 있어야 합니다.

허용 함수

memset, printf, malloc, free, write, usleep, gettimeofday, pthread_create, pthread_detach, pthread_join, pthread_mutex_init, pthread_mutex_destroy, pthread_mutex_lock, pthread_mutex_unlock


Bonus

프로세스와 세마포어를 이용한 철학자 구현

  • 모든 포크는 테이블 가운데에 있습니다.
  • 메모리의 상태는 알 수 없지만, 대신 사용가능한 포크의 수가 세마포어로 표현됩니다.
  • 각 철학자는 프로세스로 이루어져 있어야 하고, 메인 프로세스가 철학자가 되어선 안 됩니다.

허용 함수

memset, printf, malloc, free, write, fork, kill, exit, pthread_create, pthread_detach, pthread_join, usleep, gettimeofday, waitpid, sem_open, sem_close, sem_post, sem_wait, sem_unlink


함수 설명

usleep

마이크로(1/1000000)초 단위로 측정된 간격 동안 스레드 실행을 일시 중지합니다.

#include <unistd.h>

int	usleep(useconds_t microseconds);
  • return
    0: 함수는 성공하면 값 0을 반환합니다.
    -1: 실패 시 -1이 반환하며 오류를 나타내도록 errno를 설정 합니다.

gettimeofday

time(2)과 비스사지만 마이크로초 단위의 시간 까지 되돌려줍니다. 가능한 time(2)대신 사용하는 것을 권장합니다.
두번째 인자의 경우 사용되지 않고 있으며, 앞으로도 지원되지 않을 것이라고 합니다. 간혹 커널 소스등에 필드가 사용되는 경우, 모든 경우에 버그로 판단되어 무시되므로 NULL을 사용하도록 합니다.

#include <sys/time.h>

struct timeval {
	time_t       tv_sec;   /* 초 */
	suseconds_t  tv_usec;  /* 마이크로초 */
};

struct timezone
{
    int tz_minuteswest:    /* 그리니치 서측분차 */ 
    int tz_dsttime         /* DST 보정타입(일광 절약시간) */
}

int gettimeofday(struct timeval *restrict tp, void *restrict tzp);
  • return
    0: 함수는 성공하면 값 0을 반환합니다.
    -1: 실패 시 -1이 반환하며 오류를 나타내도록 errno를 설정 합니다.

함수 설명

Pthread

POSIX 스레드는 병렬적으로 작동하는 소프트웨어의 작성을 위해서 제공되는 표준 API입니다.

pthread_create

새로운 스레드가 추가로 생성되어 main 스레드와 새로 생성한 스레드가 동작하게 됩니다.

#include <pthread.h>

int	pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
  • pthread_t *thread: 스레드 식별자로서 생성된 스레드를 저장합니다.
  • const pthread_attr_t *attr: 스레드와 관련된 특성을 지정하기 위한 용도이며 pthread_attr_init을 활용합니다. NULL일 경우 기본 특성으로 지정됩니다.
  • void *(*start_routine)(void *): thread가 실행되었을 대 시작할 함수를 나타냅니다.
  • void *arg: start_rutine 함수의 인자로 사용됩니다.
  • return
    0: 함수는 성공하면 식별자인 thread에 스레드 식별 번호를 저장하고, 0을 반환합니다.
    0이외 값: 실패 시 0이외의 에러코드 값을 반환합니다.

pthread_detach

특정 스레드를 해제합니다. 스레드가 종료되면 자원을 돌려줄 것을 보증합니다.
pthread_detach()는 pthread_join()을 동시에 사용할 수 없습니다.

#include <pthread.h>

int	pthread_detach(pthread_t thread);
  • return
    0: 함수는 성공하면 값 0을 반환합니다.
    0이외 값: 실패 시 0이외의 에러코드 값을 반환합니다.

pthread_join

특정 스레드가 종료되는 걸 기다린 후 종료되면 자원을 반납하게 됩니다.
즉, 메모리 누수가 발생하지 않도록 모든 joinable 스레드에 대해서 pthread_join()을 호출합니다.
pthread_join()는 pthread_detach()을 동시에 사용할 수 없습니다.

#include <pthread.h>

int pthread_join(pthread_t thread, void **value_ptr);
  • return
    0: 함수는 성공하면 식별자인 thread에 스레드 식별 번호를 저장하고, 0을 반환합니다.
    0이외 값: 실패 시 0이외의 에러코드 값을 반환합니다.

mutex

mutex는 MUTual EXclusion(상호배제)devide의 줄임말로 스레드간 공유하는 데이터 영역을 보호하기 위해 사용됩니다. 임계 영역을 만들고 임계 영역내에 단 하나의 스레드만 진입가능 하도록 하는 방식을 사용합니다.

pthread_mutex_init

attr을 통해 mutex의 특징을 정의합니다. attr값이 NULL일 경우 기본 뮤텍스 특징으로 정의됩니다.

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
  • return
    0: 함수는 성공하면 값 0을 반환합니다.
    0이외 값: 실패 시 0이외의 에러코드 값을 반환하며 오류를 나타내도록 errno를 설정 합니다.

pthread_mutex_destroy

mutex가 가리키는 뮤텍스 객체를 삭제합니다. 스레드는 뮤텍스 객체와 별개로 스레드가 종료되어도 뮤텍스 객체는 여전히 남아 있으므로 pthread_mutex_destroy 함수를 통해 뮤텍스 객체를 삭제해야 합니다.

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);
  • return
    0: 함수는 성공하면 0을 반환합니다.
    0이외 값: 실패 시 0이외의 에러코드 값을 반환합니다.

pthread_mutex_lock

임계영역에 진입하기 위해 뮤텍스 잠금을 요청합니다. 다른 스레드가 잠금을 얻은 상태라면 잠금을 얻을 수 있을 대까지 기다리게 됩니다.

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);
  • return
    0: 함수는 성공하면 값 0을 반환합니다.
    0이외 값: 실패 시 0이외의 에러코드 값을 반환하며 오류를 나타내도록 errno를 설정 합니다.

pthread_mutex_unlock

뮤텍스 잠금을 되돌려줍니다.

#include <pthread.h>

int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • return
    0: 함수는 성공하면 값 0을 반환합니다.
    0이외 값: 실패 시 0이외의 에러코드 값을 반환하며 오류를 나타내도록 errno를 설정 합니다.

semaphore

운영 체계 똔느 프로그램 작성 내에서 공유 자원에 대한 접속을 제어하기 위해 사용되는 신호입니다.
둘 이상의 프로세서 사이에서 마이크로프로세서 시간이나 입출력 접속구와 같은 공유 자원ㅇ르 동시에 사용할 수 없기 때문에, 한 프로세서가 사용하고 있는 동안에 세마포어를 세워서 다른 프로세서를 대기시키고 사용이 끝나면 해제시키는 방법으로 사용합니다.

sem_open

세마포어의 이름을 const char * 타입의 name으로 설정합니다.
이름이 지정된 세마포어를 초기화하고 엽니다.

#include <semaphore.h>

sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
  • const char *name: 세마포어의 이름을 설정합니다.
  • int oflag: 세마포어 생성 시 플래그(O_CREAT, O_EXCL)
  • mode_t mode: 세마포어에 대한 권한 설정 값(8진수)으로 <stat.h>를 포함하여 매크로 값을 이용할 수 있습니다.
    - S_IRWXR : 그룹 접근
    - S_IRWXO : 타인 접근
    - S_IRWXU : 개인 접근
  • unsigned int value: 세모포어 초기 값으로 0보다 크고 SEM_VALUE_MAX이하의 양수여야 하며 unlock된 세마포어의 수를 의비합니다.
  • return
    sem_t *: 함수는 성공하면 세마포어에 대한 주소 값을 반환합니다.
    SEM_FAILED: 실패 시 SEM_FAILED(sem_t *)-1)라는 매크로 값을 반환하며 오류를 나타내도록 errno를 설정 합니다.

sem_close

세모포어를 종료합니다. 즉, 프로세스가 세모포어를 사용하도록 시스템이 할당한 자원을 전부 할당 해제합니다.

#include <semaphore.h>

int sem_close(sem_t *sem);
  • return
    0: 함수는 성공하면 값 0을 반환합니다.
    0이외 값: 실패 시 0이외의 에러코드 값을 반환하며 오류를 나타내도록 errno를 설정 합니다.

sem_post

세마포어에 대한 세마포어 잠금 해제 조작을 수행하여 세마포어를 잠금 해제합니다.
세마포어 값이 0이면, 세마포어에 대해 대기 중인 스레드 중 하나가 해당 호출에서 sem_wait 서브루틴으로 성공적으로 리턴할 수 있습니다.

#include <semaphore.h>

int sem_post(sem_t *sem);
  • return
    0: 함수는 성공하면 값 0을 반환합니다.
    -1: 실패 시 -1 값을 반환하며 오류를 나타내도록 errno를 설정 합니다.

sem_wait

서브루틴은 해당 세마포어에 대한 세마포어 잠금 조작을 수행하여 sem 매개변수에서 참조하는 세마포어를 잠급니다. 세마포어 값이 현재 0인 경우, 호출 스레드는 세마포어를 잠그거나 호출이 신호에 의해 인터럽트될 때까지 호출에서 sem_wait 서브루틴으로 리턴하지 않습니다.

#include <semaphore.h>

int sem_wait(sem_t *sem);
  • return
    0: 함수는 성공하면 값 0을 반환합니다.
    -1: 실패 시 -1 값을 반환하며 오류를 나타내도록 errno를 설정 합니다.

이름있는 세마포어를 삭제합니다.

#include <semaphore.h>

int sem_unlink(const char *name);
  • return
    0: 함수는 성공하면 값 0을 반환합니다.
    -1: 실패 시 -1이 반환하며 오류를 나타내도록 errno를 설정 합니다.

마무리

새로운 함수가 많아서 확인하는데 시간이 많이 걸렸다.
경우에 따라서 우선 detach와 join의 차이에 대해서 간단하게 확인해봤다.
join의 경우 추가 생성된 스레드가 종료될 때까지 메인 스레드는 대기상태가 되는 것이다. 이후 종료가 되면 스레드 자원을 해제하게 된다.
detach의 경우 호출 즉시 스레드 리소스를 해제한다. 따라서 추가된 스레드의 작업을 진행하지 않게된다.
실습을 좀 더 해보면 동작에대해서 잘 이해할 수 있을 것 같다.


profile
🐣🐥

0개의 댓글