본문 바로가기
공부/OS

[운영체제] 컨텍스트 스위칭(context switching)

by algosketch 2021. 4. 18.

Context Switching

context : 한국어로 "문맥"이라는 뜻이다. 프로세스를 중단했다가 다시 실행하려고 할 때 복원할 수 있는 충분한 정보
context switching : 실행중인 프로세스 교체
컨텍스트 스위칭을 하기 위해 고려해야할 세 가지가 있다.

  1. 어떻게 현재 프로세스 상태를 저장할 것인가?

  2. 어떻게 현재 프로세스를 중단할 것인가?

  3. 어떻게 이전 프로세스를 복원할 것인가?

    각 프로세스는 스택을 가지고 있고, 운영체제에서는 컨텍스트 스위칭을 하기 위해 이 스택을 이용한다.
    스택은 지역변수, 함수 arguments 등을 저장하는 역할을 한다. 함수가 끝났을 때 돌아갈 주소도 스택에 저장한다.
    x86 아키텍처에서는 스택이 밑으로 자라난다. (리틀 엔디안)
    EIP : Instruction Pointer
    ESP : 현재 스택 포인터
    EBP : 현재 스택 프레임의 시작점
    스택 프레임 : 함수와 관련된 스택 메모리 공간
    참고로 SP 는 Stack Pointer 이고 BP 는 Base Pointer 인데 앞에 붙은 E 는 32비트 운영체제라는 뜻이다. R 이 붙으면 64비트 운영체제이다.

    스택 연산
    push, pop, call, ret, int, iret
    push 랑 pop 은 자료구조에서 배운다. call 과 ret 은 함수의 호출과 리턴, int 는 인터럽트(시스템 호출), iret 는 인터럽트 후 돌아갈 때의 연산이다.

    컨텍스트 스위칭을 이해하기 위해서는 함수의 호출과 반환에 대해서 이해해야 한다.

    call (주소)<function1>
    ...
    <function1>
    push ebp
    mov ebp, esp
    sub esp, (함수에 할당할 메모리)
    ...
    leave
    ret
    1. 함수를 호출한다. (call) 동시에 return address 로 EIP 다음
    2. EBP 를 저장한다.
    3. EBP 를 ESP 위치로 바꿔준다.
    4. ESP 위치를 함수에 할당할 메모리 크기만큼 이동시켜 준다.
      ...
    5. leave : ESP 위치를 EBP 위치로 옮기고 EBP 위치를 이전 위치로 이동 시킨다.
    6. ret : ESP 위치에 저장되어 있는 return address 로 EIP 를 보낸다.
      참고로 반환 값은 eax 에 저장된다.

    스택과 레지스터에는 현재 프로세스의 컨트롤 플로우가 저장되어 있다. 그래서 스택을 스위칭 해주면 컨텍스트 스위칭과 유사한 효과를 낼 수 있다.(실제로 이게 전부는 아니라고 한다.)

    컨텍스트 스위칭은 현재 프로세스가 사용하고 있는 스택의 레지스터값들을 저장하고 현재 스택의 위치를 저장한다. 그리고 이전 프로세스가 사용하던 스택 위치를 복원하고 이 스택(여기서도 이미 컨텍스트 스위칭이 일어났기 때문에 사용했던 레지스터 값이 저장되어 있다.)에서 레지스터 값을 복원한다. 그러면 컨텍스트 스위칭 효과가 생긴다. (각 프로세스는 스택 메모리를 갖고 있지만, CPU가 갖고 있는 스택은 하나 뿐이다. CPU가 갖고 있는 스택은 무엇일까...? 일단은 ESP 를 말하는 것이라 생각하고 넘어갔다.)

    process2 에서 process1 로 이미 컨텍스트 스위칭 했고, 다시 process1 에서 process2 로 컨텍스트 스위칭을 하려고 한다. 이 때 일어나는 일들을 살펴보자. switch() 함수를 사용하면 컨텍스트 스위칭이 일어난다고 가정한다. 컨텍스트 스위칭 과정은 함수 호출과 반환 과정에서 레지스터와 저장하는 부분이 추가되고 ESP 를 저장하는 방법이 바뀐 것으로 생각하면 될 것 같다.

    1. 프로그램에서 switch() 함수를 만났다.
    2. 다음 명령어(EIP 의 다음) 위치(return address)를 현재 스택에 저장한다.
    3. 현재 레지스터 값들(eax, ebx, ...)을 process1 의 스택 영역에 push 한다.
    4. 현재 esp 주소를 OS 메모리에 저장한다.
    5. 이전에 사용하던 스택(process2 에서 썼던 ESP) 주소를 ESP 에 복원한다.
    6. process2 의 레지스터들을 복원한다. (pop) (이미 process2 에서 process1 로 컨텍스트 스위칭한 이력이 있기 때문에 저장된 레지스터 값이 있을 것이다.)
    7. 레지스터를 복원했으면 ESP 는 process2 에서 process1 으로 갈 때 호출했던 switch(), 이 함수를 호출하면서 저장했던 return address 를 갖고 있다. EIP 를 이 위치로 바꿔준다. 따라서 process1 에서 switch() 를 호출한 결과가 process2 로 반환 됐다고 볼 수 있다.

    위 과정은 process1 에서 process2 로 컨텍스트 스위칭이 일어나는 과정을 보여주는데, process2 에서 process1 로 컨텍스트 스위칭이 이미 일어났었다고 가정하고 있다. 그래서 레지스터를 복원하는 작업을 했었다. 만약 process2 에서 process1 로 컨텍스트 스위칭이 일어난 적이 없다면 어떡할까? 컨텍스트 스위칭 이력을 저장하여 두 경우를 별도로 처리하는 것보다는 일반화 시키는 게 나을 것 같다. 아마 fork() 하는 상황은 위에서 설명한 과정과 다르게 동작해야할 것 같지만, fork() 하여 처음 스택 공간을 초기화 할 때 스택에 레지스터 (실제로 의미 있는 값은 아니므로 null 을 저장)값들을 저장해주면 된다. 마치 컨텍스트 스위칭이 일어났던 것처럼.
    그럼 process1 에서 process2 로 컨텍스트 스위칭이 일어날 때 process2 는 처음 실행되는 것이라 가정해도 위의 과정과 동일할 것이다. 다만, 복원할 레지스터 값들은 null 값이고 return address 는 main 함수를 가리키게 된다.

  • 인천대학교 컴퓨터공학부 3학년 전공필수 과목인 박문주 교수님의 운영체제 강의를 듣고 작성한 글입니다.