개요
식사하는 철학자 문제는 동시성 프로그래밍에서 자원 공유와 데드락 방지를 다루는 고전적인 문제다. 이 프로젝트는 세 가지 다른 동기화 메커니즘을 사용하여 이 문제를 해결한다:
- philo_one: pthread와 mutex를 사용한 구현
- philo_two: pthread와 semaphore를 사용한 구현
- philo_three: 프로세스(fork)와 semaphore를 사용한 구현
프로젝트 구조
philosophers/
├── philo_one/ # pthread + mutex 버전
├── philo_two/ # pthread + semaphore 버전
└── philo_three/ # process + semaphore 버전
각 디렉토리는 독립적인 프로젝트로 구성되어 있으며, 각각의 Makefile을 가지고 있다.
각 버전별 특징
philo_one
- 동기화 메커니즘: pthread mutex
- 특징:
- 각 포크에 대해 개별 mutex 사용
- 홀수/짝수 철학자를 분리하여 데드락 방지
- 각 철학자마다 모니터링 스레드 생성
pickup과putdownmutex로 포크 집기/놓기 동작 동기화
philo_two
- 동기화 메커니즘: pthread + semaphore
- 특징:
- named semaphore를 사용하여 포크 관리
- semaphore를 통한 동시성 제어
- 각 철학자마다 개별 eating semaphore 사용
pickup과putdownsemaphore로 포크 집기/놓기 동작 동기화
philo_three
- 동기화 메커니즘: process (fork) + semaphore
- 특징:
- 각 철학자가 별도의 프로세스로 실행
- 프로세스 간 통신을 위해 named semaphore 사용
- 부모 프로세스가 자식 프로세스들을 관리
- 철학자가 죽으면 모든 프로세스를 종료
빌드 및 실행
빌드
각 디렉토리에서 개별적으로 빌드할 수 있다:
# philo_one 빌드
cd philo_one
make
# philo_two 빌드
cd philo_two
make
# philo_three 빌드
cd philo_three
make
정리
# 오브젝트 파일 삭제
make clean
# 실행 파일까지 삭제
make fclean
# 재빌드
make re
사용법
실행 형식
./philo_[one|two|three] number_of_philosophers time_to_die time_to_eat time_to_sleep [number_of_times_each_philosopher_must_eat]
매개변수 설명
- number_of_philosophers: 철학자의 수 (최소 2명)
- time_to_die: 밀리초 단위. 철학자가 마지막 식사 후 이 시간 동안 식사를 하지 않으면 죽는다
- time_to_eat: 밀리초 단위. 철학자가 식사하는 데 걸리는 시간
- time_to_sleep: 밀리초 단위. 철학자가 잠을 자는 데 걸리는 시간
- number_of_times_each_philosopher_must_eat (선택): 각 철학자가 최소한 먹어야 하는 식사 횟수. 이 매개변수가 제공되면 모든 철학자가 이 횟수만큼 식사를 완료하면 시뮬레이션이 종료된다.
실행 예제
# 기본 실행 (5명의 철학자, 800ms 내에 식사하지 않으면 죽음, 200ms 식사, 200ms 수면)
./philo_one 5 800 200 200
# 각 철학자가 7번씩 식사해야 하는 경우
./philo_one 5 800 200 200 7
# 다른 버전 실행
./philo_two 4 410 200 200
./philo_three 3 400 200 200 5
출력 형식
프로그램은 다음과 같은 형식으로 상태를 출력한다:
[timestamp_in_ms] [philosopher_number] [status]
상태 종류:
is thinking: 철학자가 생각 중has taken a fork: 포크를 집음is eating: 식사 중is sleeping: 잠자는 중died: 죽음
예시:
0 1 is thinking
0 2 is thinking
200 1 has taken a fork
200 1 has taken a fork
200 1 is eating
400 1 is sleeping
프로젝트 구조 상세
공통 파일 구조
각 버전은 다음과 같은 파일 구조를 가지고 있다:
main.c: 프로그램 진입점, 인자 파싱, 초기화philo.c: 철학자 로직 구현print.c: 상태 출력 함수error.c: 에러 처리 함수utils_1.c,utils_2.c: 유틸리티 함수들inc/philosophers.h: 메인 헤더 파일inc/struct.h: 데이터 구조 정의
주요 데이터 구조
t_philo (철학자 구조체)
typedef struct s_philo
{
int index; // 철학자 인덱스
pthread_t thread; // 메인 스레드
pthread_t mthread; // 모니터링 스레드 (philo_one, philo_two)
int num_eat; // 식사 횟수
unsigned long start_time; // 시작 시간
unsigned long last_eat_time; // 마지막 식사 시간
} t_philo;
t_global (전역 구조체)
각 버전마다 약간씩 다르지만, 공통적으로 포함하는 필드:
num_philo: 철학자 수time_to_die,time_to_eat,time_to_sleep: 시간 설정num_must_eat: 필수 식사 횟수philos: 철학자 배열- 동기화 객체들 (mutex 또는 semaphore)
동기화 메커니즘 비교
| 버전 | 동기화 방식 | 장점 | 단점 |
|---|---|---|---|
| philo_one | Mutex | 간단하고 직관적 | 많은 mutex 필요 |
| philo_two | Semaphore | 유연한 동시성 제어 | Named semaphore 관리 필요 |
| philo_three | Process + Semaphore | 프로세스 격리 | 프로세스 관리 오버헤드 |
주의사항
-
Named Semaphore 정리:
philo_two와philo_three는 named semaphore를 사용하므로, 프로그램이 비정상 종료되면 semaphore가 남을 수 있다. 필요시 수동으로 정리해야 한다:# macOS/Linux에서 semaphore 확인 및 정리 ipcs -s # semaphore 목록 확인 ipcrm -s <semid> # 특정 semaphore 삭제 -
타이밍: 프로그램은
usleep()을 사용하여 시간을 제어하므로, 실제 시간은 시스템 부하에 따라 약간 다를 수 있다. -
데드락 방지: 각 버전은 서로 다른 방식으로 데드락을 방지한다:
philo_one: 홀수/짝수 철학자 분리 및pickupmutex 사용philo_two:pickupsemaphore 사용philo_three:pickupsemaphore 사용