이전에 스레드와 관련된 글에서도 언급을 한적 있지만, 이전에 공부한 개념을 다시 일깨울겸 Context Switching에 대해서 다시 정리해보고자 한다.
만약 컴퓨터가 한번에 하나의 Task를 처리한다면 어떤 일이 일어날까? 🤔
그렇다면 어떻게 다양한 사람들이 동시에 사용하는 것처럼 할 수 있을까? 🤔
답은 멀티태스킹으로 빠른 속도로 응답을 할 수 있도록 해결할 수 있다. 빠른 속도로 Task를 번갈아가며 실행하여 실시간으로 처리하는 것 처럼 보이게 CPU가 Task를 바꿔가면서 실행할 수 있도록 Context Switching을 한다.
간단하게 말하자면, Context Switching은 현재 진행하고 있는 Task(프로세스, 스레드)의 상태를 저장하고 다음 진행할 Task의 상태 값을 읽어 적용하는 과정을 말한다.
더 정확하게 말하자면, Context Switching 이란 CPU가 어떤 Task를 실행하고 있는 상태에서 운영체제의 스케줄러가 인터럽트를 진행하여 더 높은 우선순위를 가진 Task가 실행되어야 할 때, 스케줄러가 레지스터에 저장된 기존 Task 정보 값이나 기존 Task 상태 값을 커널 내부에 존재하는 PCB(Process Control Block)에 저장하고, 새로운 Task의 정보 값이나 새 Task 상태 값을 PCB에서 다시 가져와 교체하는 작업을 말한다.
나는 CS가 약하니, 정의된 키워드를 간단하게 정리해보자.
어떤 프로세스에게 자원을 할당할지 순서와 방법을 결정하는 운영체제 커널의 모듈을 의미한다.
n-bit의 정보를 저장할 수 있는 고속도의 기억장치를 말한다. CPU에서는 외부 요청을 처리하는데 필요한 데이터를 일시적으로 저장하고 이동하는 고속도의 기억장치로 사용된다.
운영체제 중 항상 필요한 부분만을 전원이 켜짐과 함께 메모리에 올려놓고 그렇지 않은 부분은 필요할 때 메모리에 올려서 사용하게 되는데, 이때 메모리에 상주하는 운영체제의 부분을 커널이라고 한다. 운영체제도 소프트웨어이기에 메모리에 올라가야 하나, 규모가 큰 프로그램이기 때문에 모두 올라가면 한정된 메모리 공간의 낭비가 심할 것이기 때문에 커널로 분리해둔 것이다.
Context는 CPU가 다루는 Task에 대한 정보로, 대부분의 정보는 Register에 저장되며 PCB(Process Control Block)로 관리된다.
프로세스와 스레드를 처리하는 Context Switching은 조금 다르다. PCB는 운영체제에 의해 스케줄링되는 Process Control Block이고, 스레드는 프로세스 내의 TCB(Task Control Block)로 관리된다.
Task의 PCB 정보는 Process, Ready Queue 자료구조로 관리되며, Context Switching시 PCB의 정보를 바탕으로 이전에 수행하던 작업 혹은 신규 작업의 수행이 가능해진다.
PCB는 다음과 같은 정보들을 저장한다.
앞에서 언급했듯이, Context Switching은 운영체제의 스케줄러가 인터럽트 요청으로 발생시킨다. 여기서 말하는 인터럽트란 CPU가 프로그램을 실행하고 있을 때 실행 중인 프로그램 밖에서 예외 상황이 발생하여 처리가 필요한 경우, CPU에게 알려 작동이 중단되지 않고 예외 상황을 처리할 수 있도록 하는 기능을 말한다.
Context Switching은 다음과 같은 인터럽트 요청이 와야 발생한다.
내가 참고한 블로그의 예로 동작을 이해해보자.
먼저 프로그램이 2개가 있고 각각 해당하는 프로세스를 A, B라고 할 때 CPU Context에는 Program Counter와 Stack Pointer의 정보를 가지고 있다. A 부터 실행을 진행하며 Program Counter가 각 코드를 가르키면서 지나가고 Stack Pointer도 알맞게 값이 변경된다.
그러다 스케줄러의 요청에 의해 Context Switching을 실시하게 된다고 해보자.
이 순서대로 B도 작업이 수행되다가 다시 Context Switching이 진행되고, 동일한 절차로 실행된다.
Context Switching 할 때 CPU는 아무런 일을 하지 못한다. 따라서 Context Switching이 잦아지면 오히려 오버헤드가 발생해 성능이 떨어진다. (오버헤드란 사용된 시간과 사용된 메모리의 양을 의미한다.)
하단의 이미지를 살펴보면, 프로세스 P0이 executing에서 idle가 될 때 프로세스 P1이 곧바로 executing되지 않고 idle을 좀 더 하다가 executing이 된다. 곧바로 실행되지 않는 이유는 프로세스의 P0 상태를 저장하고 프로세스 P1 상태를 PCB에서 가져와야하기 때문이다. 이 과정에서 PCB를 저장하고 가져올 때 CPU는 어떠한 일도 하지 못하게 되는데, 이러한 현상이 잦을 수록 성능 저하로 이어지게 된다.
Context Switching에는 캐시 초기화, 메모리 맵핑 초기화, 메모리 접근을 위해 항상 실행되는 커널 등 많은 비용이 발생한다.
우선 캐시부터 정의해보자. 캐시는 CPU와 메인 메모리 사이에 위치하여 CPU가 한번 이상 읽어들인 메모리의 데이터를 저장하고 있다가, CPU가 다시 그 메모리에 저장된 데이터를 요구할 때 메인 메모리를 통하지 않고 데이터를 전달해주는 용도다. 이제 프로세스와 스레드의 Context Switching을 살펴보자.
프로세스 Context Swtiching은 공유하는 데이터가 없기 때문에 캐시가 쌓아놓은 데이터들이 무너지고 새로운 캐시 정보를 쌓는다. 반면 스레드의 경우 스택을 제외한 나머지 영역은 모두 공유하기 때문에 스택 및 간단한 정보만을 PCB에 저장하므로 Context Switching이 더 빠르다. 따라서 Context Switching 오버헤드를 개선하기 위해 스레드 Context Switching을 많이 사용한다.