Dining-Philosophers Problem
Philosophers who spend their lives thinking and eating
- When a philosopher thinks, he/she does not interact with his/her colleagues
- When a philosopher get hungry, he/she tries to pick up the two chopsticks that are closest to him/her
- Chopsticks that are between him/her left and right neighbors
- He/she cannot pick up a chopstick that is already in the hand of a neighbor
- When he/she is finished eating, he/she puts down both of his/her chopsticks and starts thinking again
철학자들은 생각하거나 먹으면서 인생을 보냅니다. 이들이 생각할때는 주변의 동료들과 소통하지 않고, 만약 배고파진다면, 그들은 그들 옆에 있는 젓가락을 들어 밥을 먹습니다.
그들이 밥 먹는것이 끝나면 젓가락을 다시 원래 자리로 돌려놓습니다.
즉, 젓가락이 공유자원이 됩니다.
Dining-Philosophers Problem psudocode
while(1){
think for a random number of seconds
pickup(p); // Critical Section!!
eat for a random number of seconds
putdown(p);
}
A Mutex for Each Chopstick
typedef struct{
int id; /* The phil's id: 0 to 4 */
long t0; /* The time when the program started */
long ms; /* The maximum time that phil sleeps/eats */
void *v; /* The void * that you define: Sticks */
int *blocktime /* Total time that a phil is blocked */
int phil_count /* 5 */
pthread_mutex_t *blockmon; /* monitor for blocktime */
} Phil_struct;
typedef struct sticks{
pthread_mutex_t *lock[MAXTHREADS]; /* MAXTHREADS is the number of phil */
int phil_count;
} Sticks;
void pickup(Phil_struct *ps)
{
Sticks *pp;
int phil_count;
pp = (Sticks *) ps->v;
phil_count = pp->phil_count;
/* lock up left stick */
pthread_mutex_lock(pp->lock[ps->id]);
/* lock up right stick */
pthread_mutex_lock(pp->lock[(ps->id+1)%phil_count]);
}
void putdown(Phil_struct *ps)
{
Sticks *pp;
int phil_count;
pp = (Sticks *) ps->v;
phil_count = pp->phil_count;
/* unlock right stick */
pthread_mutex_unlock(pp->lock[(ps->id+1)%phil_count]);
/* unlock left stick */
pthread_mutex_unlock(pp->lock[ps->id]);
}
Deadlock
Although this solution guarantees that no two neighbors are eating simultaneously, it could create a deadlock
만약 5명의 철학자가 동시에 오른쪽에 있는 젓가락을 집으면, 모든 철학자는 왼쪽에 있는 젓가락이 unlock이 될때까지 기다리게 되므로, 이는 교착상태가 될 수 있습니다.
Deadlock-Free Solution
Possible solutions
- Allow at most n-1 philosophers to be sitting simultaneously at the table → 철학자의 수를 n-1명으로 줄이거나, 반대로 젓가락의 수를 n+1개로 늘리면 해결할 수 있다.
- Allow a philosopher to pick up his/her chopsticks only if both chopsticks are available → 두 젓가락이 동시에 사용가능한지를 확인한 후에 젓가락을 집게 하면 해결할 수 있다. 하지만 이렇게 한다면 병렬성이 떨어진다는 문제가 있다.
- Must pick them up in a critical section
- Asymmetric solution
- Odd philosophers start left-hand first, and even philosophers start right-hand first
void pickup(Phil_struct *ps)
{
...
if (ps->id % 2 == 1) { /* Odd */
/* lock up left stick */
pthread_mutex_lock(pp->lock[ps->id]);
/* lock up right stick */
pthread_mutex_lock(pp->lock[(ps->id+1)%phil_count]);
} else { /* Even */
/* lock up right stick */
pthread_mutex_lock(pp->lock[(ps->id+1)%phil_count]);
/* lock up left stick */
pthread_mutex_lock(pp->lock[ps->id]);
}
}
Fairness and Starvation
Philosophers are not equally weighted, and even starve
- Depending on how the thread system is implemented
- Suppose philosopher A is waiting for a chopstick
- The owner of the chopstick (philosopher B) will eat and put the chopstick down, but there's no guarantee that philosopher A will get it if philosopher B wants to eat again before philosopher A's thread is rescheduled
위에 방법은 공평성의 문제가 있을 수 있습니다.
Possible solution
- Using condition variables
- Will be coverd in the Operation System class
Sending SIGTERM to a Multi-threaded Process
Which thread will be terminated?
void* thread1(void *data)
{
while(1){
printf("thread 1\n");
sleep(2);
}
}
void* thread2(void *data)
{
while(1){
printf("thread 2\n");
sleep(2);
}
}
void* thread3(void *data)
{
while(1){
printf("thread 3\n");
sleep(2);
}
}
main(void)
{
...
thr_ret = pthread_create(&tid[0], NULL, thread1, NULL);
thr_ret = pthread_create(&tid[1], NULL, thread2, NULL);
thr_ret = pthread_create(&tid[2], NULL, thread3, NULL);
sleep(3);
kill(getpid(), SIGTERM);
printf("Main return\n");
return 0;
}
result
+ main을 포함하는 모든 thread가 종료됩니다.
+ SIGTERM은 process 자체를 종료시키는 것이기 때문에 해당 process안에 있는 모든 thread들은 종료됩니다. 이때 종료되는 시점은 어떤 thread에게 Program Counter가 향해있는지에 따라 다릅니다.
Sending SIGALRM to a Multi-threaded Process
Which thread will be preempted?
main(void)
{
...
thr_ret = pthread_create(&tid[0], NULL, thread1, NULL);
thr_ret = pthread_create(&tid[1], NULL, thread2, NULL);
thr_ret = pthread_create(&tid[2], NULL, thread3, NULL);
sleep(3);
kill(getpid(), SIGALRM);
printf("Main return\n");
return 0;
}
+ 현재 실행중인 thread에서 signal을 처리합니다.
+ 해당 thread의 context안에서 signal_handler가 실행됩니다. 즉 지금 실행중인 thread의 stack을 빌리는 것입니다.
Sending a Signal to a Thread
void sig_handler(int signum){
for(int i=0; i<5; i++){
printf("this is signal handler\n");
sleep(2);
}
}
int main(int argc, void* argv[]){
...
signal(SIGUSR1, sighandler);
thr_ret = pthread_create(&tid[0], NULL, thread1, NULL);
thr_ret = pthread_create(&tid[1], NULL, thread2, NULL);
thr_ret = pthread_create(&tid[2], NULL, thread3, NULL);
sleep(3);
pthread_kill(tid[select-1], SIGTERM);
sleep(10);
printf("Main return\n");
return 0;
}
+ 여전히 SIGKILL/SIGTERM은 모든 thread를 종료합니다
result
+ 왼쪽 결과의 경우, thread 2번만 signal_handler가 불리고, 1,3번은 정상적으로 계속 돌아감을 알 수 있습니다.
+ signal을 받는 즉시 handler가 불리는 것이 아니라 그 thread에 CPU가 할당되었을때, handler가 불리는 것입니다.
+ signal mask의 경우는 process단위입니다.
'[학교 수업] > [학교 수업] 시스템 프로그래밍' 카테고리의 다른 글
[시스템 프로그래밍] File Offset (0) | 2024.11.14 |
---|---|
[시스템 프로그래밍] File I/O (0) | 2024.11.13 |
[시스템 프로그래밍] Thread Synchronization (0) | 2024.10.31 |
[시스템 프로그래밍] Threads (2) | 2024.10.30 |
[시스템 프로그래밍] Message Queue (3) | 2024.10.17 |