Home Philosophers ④ 구현 과정 (Bonus part)
Post
Cancel

Philosophers ④ 구현 과정 (Bonus part)

thumbnail

세마포어를 사용하는 과제

방법 구상

  • 프로세스, 세마포어 관련 개념 및 함수 숙지
  • 세마포어로 임계구역을 보호
  • 모니터링을 위한 별도의 스레드/프로세스 설정

구현 순서

  1. 인자 파싱
  2. 초기화
  3. 프로세스 생성
  4. 자원 해제

구현 방법

순서도

philo-bonus-flowchart.png

문제 해결 방법

Bonus 파트는 mandatory와는 조금 다르게 두 철학자 사이에 포크가 놓여 있는 것이 아닌, 한 곳에 모든 포크가 모여있는 구조이다. 따라서 교착상태와 기아상태를 mandatory와는 다른 방법으로 해결하였다.

  • 경쟁 상태 (Race condition)
    • 읽기와 쓰기가 동시에 일어날 수 있는 변수의 경우 세마포어를 통해 한 번에 한 스레드만이 접근하도록 하였다.
  • 교착 상태 (Deadlock)
    • 포크를 집는 과정 전체를 감싸는 세마포어를 설정하여 한 철학자가 2개의 포크를 연속으로 집는 것을 보장해주었다.
  • 기아 상태 (Starvation)
    • 위의 추가사항으로 인해 자연스럽게 철학자들이 순서대로 포크를 2개씩 집게 되므로 기아가 발생할 가능성도 제거하였다.

초기화

세마포어

  • 세마포어의 종류
    • 계수 세마포어 (Counting Semaphore)
      • 세마포어 변수의 값을 포크의 전체 개수로 설정한다.
    • 이진 세마포어 (Binary Semaphore)
      • 0과 1을 오가며 mutex lock과 유사한 용도로 사용한다.
  • sem_open
    • 세마포어를 열기 전에 우선 unlink를 하여 최초로 연다는 것을 보장해준다.
    • info->sem_print = sem_open("/print", O_CREAT, 0600, 1);
    • 위와 같이 세마포어를 열어주었다. 두번째 매개변수인 oflag에 O_CREAT만 사용하면 단순히 세마포어를 생성하지만 O_EXCL과 비트연산자 or을 사용하여 함께 설정하게되면 이미 존재하는 세마포어인 경우 오류를 반환한다.
    • 세번째 매개변수인 mode에 들어가는 값은 세마포어에 대한 접근권한이다. chmod와 사용방법이 유사하며 8진수의 각 자리수의 값은 차례대로 사용자(owner) 권한, 그룹 권한, 다른 사용자 권한을 의미한다. 과제에서는 세마포어에 접근하는 사용자에게만 읽기(r), 쓰기(w) 권한(rw-)을 주었다.

모니터링 스레드

  • 스레드 생성
    • pthread_create
  • 스레드 해제
    • pthread_detach
  • pthread_join이 아닌 pthread_detach를 사용한 이유
    • mandatory에서는 메인 프로세스에서 한번에 스레드들을 생성한 후에 pthread_join을 통해 스레드들이 종료되는 것을 기다렸다.
    • 하지만 bonus에서는 자식 프로세스 각각이 모니터 스레드를 생성하고 종료될 때는 exit을 통해 빠져나가기 때문에 join을 호출하는데에 어려움이 있다. 따라서 생성 후에 detach를 해주어 각 스레드들이 스스로 자원을 해제하도록 하였다.

결과물

  • 인자가 4 210 100 100 15인 경우 result1
  • 인자가 9 610 200 200 10인 경우 result2

    문제점

  • 메모리 누수 발생

    Untitled

    • 발생 원인 1
      • 우선 아래와 같이 세마포어의 이름을 생성하는 함수 내부에서 메모리 할당 이후에 사용을 마친 변수를 제대로 해제해주지 않았다.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      
        char    *generate_sem_name(char *prefix, int i)
        {
            char    *name;
            char    *index;
              
            index = ft_itoa(i);
            name = ft_strjoin(prefix, index);
            return (name);
        }
      
    • 해결 방법 1
      • 세마포어 이름을 생성하는 함수 내부에서 할당하여 사용을 끝낸 변수는 free 해주었다.

      Untitled

    • 발생 원인 2

      Untitled

      • 위와 같이 문자열을 할당받은 메모리에 저장해서 unlink하는 식의 코드를 세마포어의 이름이 필요할 때마다 사용하고 해제하지 않는 오류를 범했다.
    • 해결 방법 2
      • 철학자 구조체의 세마포어명이 필요한 경우, 매 순간 이름을 생성하지 않고 최초 1회 생성 후 구조체에 저장하여 사용한 뒤, 종료 직전에 free하였다.
      • 해당 부분을 수정하니 더이상의 메모리 누수는 발생하지 않았다.

      Untitled

  • 철학자의 사망 메시지가 프로그램의 시작 직후 출력되는 문제

    Untitled

    • 원인
      • 철학자 프로세스가 시간 변수에 값을 저장하기 전에 모니터가 값에 접근해서 발생
    • 해결 방법
      • 시간 변수를 초기화하는 시점을 모니터 스레드가 생성되기 전으로 변경
  • 철학자의 사망 메시지를 출력한 이후에도 다른 철학자 프로세스가 동작하는 문제

    Untitled

    • 원인

      1
      2
      3
      4
      5
      6
      
        struct s_information
        {
            ...
            int  is_die_printed;
            ...
        };
      

      처음엔 위와 같이 info 구조체에 사망 출력 여부를 나타내는 변수를 설정하고 아래 함수로 변수를 체크해서 한 철학자 프로세스만 사망 메시지를 출력하도록 하려고 했다.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      
        int is_die_printed(t_info *info)
        {
            sem_wait(info->sem_monitor);
            if (info->is_die_printed == TRUE)
            {
                sem_post(info->sem_monitor);
                return (TRUE);
            }
            sem_post(info->sem_monitor);
            return (FALSE);
        }
      

      하지만 이렇게 작성해도 한 철학자의 사망을 다른 철학자 프로세스들이 알 수 없었다. fork된 이후에는 각 프로세스는 자신만의 주소 공간에 프로세스의 사본을 갖기 때문에 변수의 값이 공유되지 않는다는 개념을 놓쳤기 때문에 범한 실수였다.

    • 해결 방법

      • 각 철학자를 감시하는 모니터링 스레드가 사망을 인지하게되면 sem_wait을 통해 프로세스끼리 공유하는 세마포어를 block해두어 다른 프로세스가 출력하지 못하도록 막아주었다.
      • 이후 자신의 프로세스를 status = 1로 종료하게되면 메인 프로세스에서 다른 철학자 프로세스들을 SIGKILL로 직접 종료해주었다.

Ref.

https://stackoverflow.com/questions/4298678/after-forking-are-global-variables-shared
https://stackoverflow.com/questions/71765047/why-we-unlink-semaphores-before-we-initializes-them

This post is licensed under CC BY 4.0 by the author.

Philosophers ③ 구현 과정 (Mandatory part)

Philosophers ⑤ 회고