CSI3101-01 운영체제 차호정 교수님 2015123002 경영학과 김상현
이번 3차 과제는 스케줄러의 동작 원리와 메모리와 가상 메모리를 이해하는 데에 있다. 스케줄링과 메모리 관리는 운영체제의 대표적인 역할로 프로세스 간 자원을 효율적으로 분배하고 프로세스가 수행하는 일을 마칠 수 있도록 돕는다. 특히 CPU와 메인 메모리는 컴퓨터의 주요 자원으로, 이를 두고 경쟁하는 상황에서 운영체제는 이를 효율적으로 관리할 필요가 있다. 이를 알아볼 수 있는 시뮬레이터를 제작하여 스케줄링 기법과 버디 시스템, 그리고 페이징을 알아보는 데에 이번 과제의 목적이 있다.
프로그램은 초기에 필요한 정보를 세팅하는 구간과 이후의 무한 while
루프로 이루어진 구간으로 나눠져 있다. 초기에는 input
파일을 불러와, TOTAL_EVENT_NUM
, VM_SIZE
, PM_SIZE
, PAGE/FRAME_SIZE
를 설정하는 역할을 하고 출력 파일을 생성한다. 이후 Priority의 수에 따른 각각의 Queue를 초기화하고, 실제 메모리를 의미하는 PM
을 초기화하고 Page Replacement Algorithm을 지정한다.
(...)
Queues queues(NUM_QUEUES);
(...)
PM* pm = new PM(PM_SIZE/PAGE_SIZE, pra);
std::vector<Process*> processes;
이후에는 while
루프 내에서 모든 프로세스가 종료되고 모든 event가 끝이 날 때까지 반복되는 루틴이 존재한다. 한 번의 while
루프는 하나의 CPU 사이클을 의미한다. 과제 명세에 제시된 바와 같이 각 cycle은 아래의 순서대로 수행된다.
Sleep Queue 체크 - 완료된 Sleep을 체크한다.
for (Process* process : queues.run_queues[NUM_QUEUES].queue) {
// Keep iterating
process->sleep_one_cycle();
queues.run_queues[NUM_QUEUES].queue.pop_front();
if (process->getSleepTime() == 0) {
// if sleep_time = 0, then move it back to original queue
queues.push_to_queue(process->getPriority(), process);
} else {
// else, go back to the sleep queue
queues.run_queues[NUM_QUEUES].queue.push_back(process);
}
}
I/O 작업 수행 - 완료된 I/O 작업을 체크한다.
if (events[current_line].time == cycle) {
Event& current_event = events[current_line];
if (current_event.code == "INPUT")
{
for (Process* process : queues.run_queues[NUM_QUEUES + 1].queue) {
queues.run_queues[NUM_QUEUES + 1].queue.pop_front();
if (process->getPID() == current_event.priority) {
// for "INPUT", the third element (priority) means PID
// if process_pid == input_pid, move it back to original queue
queues.push_to_queue(process->getPriority(), process);
} else {
// else, go back to the IO WAIT queue
queues.run_queues[NUM_QUEUES + 1].queue.push_back(process);
}
}
(...)
새로운 프로세스 생성 - FCFS의 경우 time_quantum
이 존재하지 않으며, RR의 경우는 기본값인 TIME_QUANTUM = 10
을 할당한다.
else {
Process* new_process = new Process();
processes.push_back(new_process);
(...)
if (current_event.priority < NUM_QUEUES / 2) {
// FCFS
new_process->setTimeQuantum(NO_TIME_QUANTUM);
} else {
// Round Robin
new_process->setTimeQuantum(TIME_QUANTUM);
}
queues.push_to_queue(current_event.priority, new_process);
}
(...)
}
프로세스 스케줄링 - 현재 실행 중인 프로세스가 없다면 높은 우선순위대로 프로세스를 선택하고, 현재 실행 중인 프로세스가 존재한다면 preemption이 가능한 (FCFS의 더 높은 우선순위 queue를 대상) 프로세스가 대기 중인지를 체크한다.
if (!(queues.running_process)) {
// if no process running
for (int q = 0; q < NUM_QUEUES; q++) {
// iterate over all queues (except sleep & io wait)
if (queues.run_queues[q].queue.empty()) {
continue;
} else {
Process* selected_process = queues.run_queues[q].queue.front();
queues.run_queues[q].queue.pop_front();
queues.running_process = selected_process;
break;
}
}
} else {
// else if a process is running
int current_priority = queues.running_process->getPriority();
for (int q = 0; (q < current_priority) && (q < (NUM_QUEUES / 2)); q++) {
if (queues.run_queues[q].queue.empty()) {
continue;
} else {
// preempt the running process
queues.push_to_queue(queues.running_process->getPriority(), queues.running_process);
queues.running_process = queues.run_queues[q].queue.front();
queues.run_queues[q].queue.pop_front();
break;
}
}
}
현재 실행 중인 프로세스의 코드를 실행한다. 이후에 프로세스의 종료 여부를 체크한다.
if (current_process) {
return_value = queues.running_process->execute();
(...)
}
(...)
if ((current_process) && (current_process->current_line == current_process->num_codes)) {
// terminate the process
current_process->terminate();
Process* terminated_process = queues.running_process;
queues.running_process = nullptr;
// release memory
terminated_process->vm->releaseAll();
}
마지막으로 모든 프로세스와 이벤트가 종료되었는지를 체크한다.
// Check if all jobs are done
if (queues.all_empty() && !queues.running_process) {
operating = false;
}
// increase the cycle number
cycle++;
물론 이외에도 중간 과정의 코드는 많지만, 주요 코드는 위와 같다. 이를 도식화해서 나타내면 아래 그림과 같다.