42 Seoul: philosophers: 개념 정리

Chaewon Kang·2021년 4월 26일
0

42 seoul

목록 보기
11/17
post-custom-banner

기본적으로, 복수의 프로세스 및 스레드가 동시동작할 때 발생하는 문제를 해결하기 위함.

동시성 문제 (동기화 문제)

자원을 공유하는 병렬 처리 시스템에서 의도하지 않은 데이터 변질이 발생하는 것

교착 상태 (데드락, Dead-lock)

  • 상호 배제(Mutual Exclusion)
    프로세스들이 필요로 하는 자원에 대해 배타적인 통제권을 요구한다
  • 점유 대기(Hold and wait)
    프로세스가 할당된 자원을 가진 상태에서 다른 자원을 기다린다
  • 비선점(No preemption)
    프로세스가 어떤 자원의 사용을 끝낼 때 까지 그 자원을 뺏을 수 없다
  • 순환대기(Circular wait)
    각 프로세스는 순환적으로 다음 프로세스가 요구하는 자원을 가지고 있다

교착 상태는, 위의 4가지 조건이 동시에 충족될 때를 말한다.

프로세스 (Process)

실행 중인 프로그램(Program). 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행 중인 상태를 말함.

  • 프로그램에 사용되는 데이터
  • 메모리 등의 자원
  • 스레드

스레드 Thread

프로세스보다 작은 실행 단위. 프로세스 내에서 실제로 작업을 수행하는 주체를 의미함. 모든 프로세스 내에는 한 개 이상의 스레드가 존재함. 두 개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스 (multi-threaded process)라고 함.

  • pthread (Posix thread)
    C에서 스레드를 조작하는 표준 인터페이스. 모든 리눅스 시스템에서 사용 가능하고, 60여 개의 함수가 있다.
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routinf)(void *), void *arg);

새로운 스레드를 스레드 속성 attr에 따라 생성한다. 스레드 속성 객체 attr이 NULL이라면, 기본 스레드 속성으로 스레드를 생성한다. 스레드가 성공적으로 생성되면 생성된 스레드 ID가 thread에 저장된다. 생성된 스레드는 start_routine arg 인자를 사용하여 실행한다. start_routine이 반환되면, 내부적으로 pthread_exit() 함수가 호출되어 스레드가 종료된다.

int pthread_join(pthread_t thread, void **value_ptr);

스레드 종료를 대기. 대기하는 스레드가 종료되면, value_ptr 인자의 값은 pthread_exit() 함수가 전달한 종료 값을 얻음.

예제

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

void *thread(void *vargp);

int main()                                  /* 메인 스레드가 시작되었다 */
{
  pthread_t tid;                            /* 피어 스레드의 스레드ID를 저장하는 데에 쓸 것이다 */
  pthread_create(&tid, NULL, thread, NULL); /* 피어 스레드 1개를 생성했다. 이제 메인 스레드와 피어 스레드는 동시에 돌고있다 */
  pthread_join(tid, NULL);                  /* 메인 스레드가 피어 스레드의 종료를 기다린다 */
  exit(0);                                  /* 현재 프로세스에 돌고있는 모든 스레드를 종료한다. 현재의 경우, 메인 스레드 1개가 전부다. */
}

void *thread(void *vargp)                   /* 스레드 루틴을 정의한다 */
{
  printf("Hello World\n");
  return (NULL);
}

뮤텍스 Mutex

Mutex lock: 특정 코드 영역의 스레드를 실행할 때 한 번에 하나의 스레드만 실행 가능하도록 하는 방법. <pthread.h>를 include한다.

뮤텍스 객체 초기화

int pthread_mutex_init(pthread_mutex_t *mutex, const pthred_mutexattr_t *attr);

뮤텍스 객체 잠그기

int pthread_mutex_lock(pthread_mutex_t *mutex);

뮤텍스 객체 잠금 해제

int pthread_mutex_unlock(pthread_mutex_t *mutex)

뮤텍스 객체 파괴

int pthread_mutex_destroy(pthread_mutex_t *mutex);

인자 thread를 터널에서 분리시키기. 분리된 스레드는 수행을 종료하고, 할당된 자원을 회수한다.

int pthread_detach(pthread_t thread);

세마포어 semaphore

동시에 리소스에 접근 허용이 가능한 개수를 의미하는 정수형 변수. (동시에 사용 가능한 포크 개수)

sem_t *sem_open(const char *name, int oflag, ...);
name이라는 세마포어 객체를 oflag에 따라 생성 혹은 접근.

int sem_close(sem_t *sem);
sem이 가리키는 세마포어를 사용하여 종료

int sem_wait( sem_t *sem);
sem이 가리키는 세마포어를 잠금

int sem_post( sem_t *sem);
sem이 가리키는 세마포어를 잠금 해제

int sem_unlink( const char *name);
name이라는 세마포어 객체를 제거

정리...

프로그램 1에서는 철학자들 사이에 포크가 하나씩 있는 구조.
프로그램 2에서는 포크들이 테이블 중앙에 모여 있는 구조.

한 철학자가 식사를 시작한 시점으로부터 time_to_eat 만큼의 시간이 지나면, 바로 잠에 든다.

실행 옵션

./philo_one 3 410 200 200

3명의 철학자, 3개의 포크.
200 밀리세컨만큼 먹고, 200 밀리세컨만큼 자고.
410 밀리세컨 내에 못 먹으면 철학자는 죽음.

number_of_philosophers : 철학자의 수, 즉 포크의 갯수
time_to_die : 밀리세컨 단위. 한 철학자가 식사를 시작하지 않으면, 그가 마지막 식사를 시작하거나 시뮬레이션이 시작된지 ‘time_to_die’ 초 내에 그는 죽는다.
time_to_eat : 밀리세컨 단위. 철학자가 식사를 하는 시간. 이 시간동안 철학자는 포크 두 개를 들고 있다.
time_to_sleep: 밀리세컨 단위. 철학자가 자는 시간.

Philo_one

Threads와 mutex를 사용하는 철학자
각각의 철학자 사이에 하나의 포크가 있어서, 결국 모든 철학자들의 양쪽에 포크가 놓여 있게 된다.
철학자들이 포크를 중복 사용하는 것을 막기 위해, 각각의 포크의 상태를 mutex로 보호해야 한다.
각각의 철학자는 thread여야 한다.

  • 각각의 철학자에게 하나의 스레드가 할당 되었는지.
  • 각각의 포크에 뮤텍스를 걸었는지, 포크 값을 확인하고 바꾸는데 사용되었는지.
  • 상태와 시간을 출력할 때 꼬이지 않도록 뮤텍스를 잘 걸었는지.
  • 철학자가 죽는 상황이 잘 확인 되는지, 죽는 것과 먹는 것이 동시에 발생하지 않도록 잘 처리하는지.

Philo_two

Threads와 semaphore을 사용하는 철학자
모든 포크들이 테이블 한가운데에 있다.
메모리 안에 상태는 없지만, 사용 가능한 포크의 개수가 semaphore을 통해 표시된다.
각각의 철학자는 thread여야 한다.

  • 각각의 철학자에게 하나의 스레드가 할당 되었는지.
  • 포크 개수를 나타내는 하나의 세마포어가 있는지. (한 개의 세마포어로 테이블 중앙의 리소스를 관리)
  • 상태와 시간을 출력할 때 꼬이지 않도록 뮤텍스를 잘 걸었는지.
  • 철학자가 죽는 상황이 잘 확인 되는지, 죽는 것과 먹는 것이 동시에 발생하지 않도록 잘 처리하는지.

Philo_three

processes와 semaphore을 사용하는 철학자
모든 포크들이 테이블 한가운데에 있다.
메모리 안에 상태는 없지만, 사용 가능한 포크의 개수가 semaphore을 통해 표시된다.
각각의 철학자들은 process여야 하고, 메인 프로세스는 철학자가 아니여야 한다.

  • 첫번 째 프로세스 (메인 프로세스)가 자식 프로세스 (각각의 철학자 프로세스)를 잘 기다리는지 체크.
  • pid 가 -1 일 때 아무 프로세스나 waiting 하는 것이고 0 은 결과를 return 할때까지 계속 wait 한다는 뜻이므로
  • 포크 개수 나타내는 하나의 세마포어가 있는지.
profile
문학적 상상력과 기술적 가능성
post-custom-banner

0개의 댓글