궁금한점
철학자 문제 해결 개선안
/*
** i 번째 철학자가 식사를 할 준비가 되었는지 확인한다.
** 양 옆의 철학자가 식사를 하고 있지 않아 젓가락을 모두 이용할 수 있다면, 철학자가 take_chopsticks에서 wait하지 않도록 signal을 보낸다.
** 주어진 조건을 만족하는 경우에는 philo[i]의 값이 1이므로 take_chopsticks에서 Block되지 않고 EATING 할 수 있다.
*/
check(int i)
{
if (state[i] == THINKING && state[LEFT] != EATING && state[RIGHT] != EATING)
{
state[i] = EATING;
signal(philo[i]);
}
}
/*
** mutex를 통해 i 번째 철학자의 상태를 변경한다.
** check를 통해 양쪽 철학자의 상태를 확인한다.
** i 번째 철학자는 자신이 식사를 할 수 있을 때까지 기다린다.
*/
take_chopsticks(int i)
{
wait(mutex);
state[i] = THINKING;
check(i);
signal(mutex);
wait(philo[i]);
}
/*
** mutex를 통해 i 번째 철학자의 상태를 변경한다.
** check를 통해 왼쪽 철학자와 오른쪽 철학자의 양 옆을 확인한다.
** 둘 중 식사가 가능한 철학자에게 check 내부에서 signal을 보낸다.
*/
put_chopsticks(int i)
{
wait(mutex);
state[i] = SLEEPING;
check(LEFT);
check(RIGHT);
signal(mutex);
}
/*
** Solution
*/
do
{
...
THINKING
...
take_chopsticks(i)
...
EATING
...
put_chopsticks(i);
...
SLEEPING
...
} while (1);
현재 상황
과정
철학자3가 take_chopsticks함수로 들어감
take_chopsticks(int i)
{
wait(mutex);
state[철학자3] = THINKING;
check(철학자3);
signal(mutex);
wait(philo[철학자3]);
}
wait(mutex)
철학자3 말고 다른 철학자에 대한 상태 접근 불가
state[철학자3] = THINKING;
밥을 먹기 전에 THINKING
check(철학자3)
check(int i)
{
if (state[철학자3] == THINKING && state[철학자2] != EATING && state[철학자4] != EATING)
{
state[i] = EATING;
signal(philo[i]);
}
}
철학자2가 Eating이기 때문에 조건문 안은 활성화되지 않고 넘어감
signal(mutex)
철학자3가 식사를 할 수 있기 위해서는 철학자2가 식사가 끝이 나야하기 때문에 다른 철학자들의 상태접근을 허용해줌
wait(philo[철학자3])
철학자2가 식사를 마칠때까지 기다린다
철학자2가 put_chopsticks함수로 들어감
put_chopsticks(int i)
{
wait(mutex);
state[철학자2] = SLEEPING;
check(철학자1);
check(철학자3);
signal(mutex);
}
wait(mutex)
다른 철학자 상태에 접근하지 못하게 guard
state[철학자2] = SLEEPING;
철학자2의 상태를 sleeping
check(철학자1)
철학자1이 sleeping이기 때문에 그냥 넘어감
check(철학자3)
check(int i)
{
if (state[철학자3] == THINKING && state[철학자2] != EATING && state[철학자4] != EATING)
{
state[i] = EATING;
signal(philo[i]);
}
}
이렇게 다시 철학자3의 상태를 확인하게 되고 철학자2가 Eating에서 sleeping이 되었기 때문에 이제 철학자3는 식사를 할 수 있게 된다
하나의 코어에는 하나의 작업만 존재
모든 프로세스를 처리하기 위해선 하나의 프로세스를 처리하고 그 다음 프로세스가 처리하는 식의 프로세스 간의 전환
Kernel의 Dispatcher라는 곳에서 일어나며 PCB라는 자료구조가 필요
Time Quantum이 모두 소진되거나, Interrupt에 의해 발생
Time Quantum은 프로세스가 한 번에 처리될 수 있는 시간 총량을 의미한다. 일반적으로 프로세스에게 할당되는 Time Quantum은 사용자가 체감하지 못할 정도로 작다. 덕분에 한 번에 하나의 프로세스 밖에 처리하지 못하는 상황임에도 사용자는 모든 프로세스가 동시에 처리되는 것처럼 느끼게 된다.
작업하던 내용을 PCB에 저장하고 작업하고 싶은 내용을 PCB로 부터 읽어들인다
Context Switch는 Interrupt에 의해 발생하지만 프로세스의 기록 자체는 시스템 콜에 기반하는 것을 알 수 있다.
Context Switch 자체는 위에서 언급된 것처럼 Dispatcher에 의해서 처리되는데, 이는 곧 Dispatcher의 호출로부터 시작된다. Dispatcher의 호출은 Interrupt에 의해 발생한다. Dispatcher를 호출하는 Interrupt는 Preemptive Scheduling과 Non-Preemptive Scheduling으로 나뉜다. Time Quantum을 모두 소진하여 운영체제 권한으로 프로세스의 권한을 뺏으면서 발생하는 Interrupt가 Preemptive Scheduling이고, I/O 호출과 같이 프로세스가 스스로 CPU 점유를 포기하면서 발생하는 Interrupt가 Non-Preemptive Scheduling이다.
Preemptive / Non-preemptive
비선점형 스케줄링
어떤 프로세스가 CPU를 할당 받으면 그 프로세스가 종료되거나 IO request가 발생하여 자발적으로 대기 상태로 들어갈 때까지 계속 실행
어떤 프로세스가 작업을 마치고 자발적으로 대기 상태로 들어가거나 종료되는 경우 다른 프로세스가 실행
선점형 스케줄링
어떤 프로세스가 실행되다 time slice를 모두 사용해 time-out되거나, IO가 발생하거나, event를 기다려야 하는 상황이라면 다른 프로세스에게 CPU 사용을 양보
프로세스를 쫒아 내고 CPU자원을 선점할 수 있다는 뜻
현재 OS는 대부분 시분할 선점형 스케쥴링을 사용. 비선점형은 해당 작업이 끝날 때 까지 계속 실행되기 때문에 멀티 프로세스 환경에서 응답성을 기대할 수 없다
프로세스에서 instruction을 수행할때 usermode 와 kernelmode에 따라서 cpu 실행이 달라짐
User Mode의 프로세스는 대체적으로 사용자에 의해 구동된 어플리케이션이며, Instruction에 대해 범용적인 권한을 갖고는 있지만 Kernel Mode 만큼의 권한을 갖지는 못한다.
trap
interrupt
특징
Trap의 경우에는 Trap Service Routine 내에서 처리 도중에 Interrupt를 받을 수도 있지만, Interrupt의 경우에는 Interrupt Service Routine 내에서 처리 도중에 Inetrrupt를 받을 수 없게 되어 있다. 이는 Interrupt의 Depth가 깊어짐에 따라 하나의 Interrupt가 끝나는데 긴 시간이 요구되는 구조가 될 수 있기 때문이다. 따라서 Interrupt를 처리할 때 다른 Interrupt들도 처리할 수 있도록 Interrupt Serivce Routine은 최대한 짧게 수행되도록 설계 되어 있다.
Trap은 프로세스의 Context를 저장하지 않아도 된다는 면에서 Interrupt보다 가볍지만 Trap에 대한 처리가 완료될 때까지 Block된다는 특징이 있다. Interrupt는 Trap의 반대의 특징을 갖고 있다고 보면 된다.
만일 Interrupt가 동시에 발생하면 우선 순위에 따라 처리한다고 했는데, 동일한 우선 순위를 가진 Interrupt를 받게 되면 운영체제와 하드웨어에 의해 하나의 Interrupt를 처리하고 나머지 Interrupt는 저장해두거나 무시하게 된다.