[운영체제와 정보기술의 원리] 4. 프로그램의 구조와 실행

'운영체제와 정보기술의 원리' 스터디를 진행하며 정리한 내용이다.


1. 프로그램의 구조와 인터럽트

  • 컴퓨터 프로그램은 어떠한 프로그래밍 언어로 작성되었든 그 내부 구조는 함수들로 구성된다.
  • 프로그램의 주소 영역은 크게 코드(code), 데이터(data), 스택(stack) 영역으로 구분됨.

    • 코드 영역 - 우리가 작성한 프로그램 함수들의 코드가 CPU에서 수행할 수 있는 기계어 명령 형태로 변환되어 저장되는 부분.
    • 데이터 영역 - 전역 변수 등 프로그램이 사용하는 데이터를 저장하는 부분.
    • 스택 영역 - 함수가 호출될 때 호출된 함수의 수행을 마치고 복귀할 주소 및 데이터를 임시로 저장하는 데에 사용되는 공간.
  • 하나의 함수가 수행되는 중에 다른 함수를 호출하고, 호출된 함수가 끝나면 다시 원래 호출했던 함수의 위치로 돌아가 프로그램을 계속 실행하게 됨.

    • 인터럽트 동작 원리와 비슷함.

2. 컴퓨터 시스템의 작동 개요

  • CPU는 빠른 속도로 처리하는 계산 능력은 가지고 있지만, 어떠한 작업을 수행해야 하는지 스스로 결정하는 능력은 갖추고 있지 못함.

Untitled (70)

  • CPU가 수행해야 할 메모리 주소를 담고 있는 레지스터를 프로그램 카운터(Program Counter: PC)라고 부름. 즉 CPU는 매번 프로그램 카운터가 가리키는 메모리 위치의 명령을 처리하게 됨.

    • 일반적으로 주소 이동이 없는 이상 프로그램 카운터는 항상 바로 다음 명령을 가리키게 되어 코드의 순차적인 수행이 이루어짐.
  • 메모리에는 사용자 프로그램들과 운영체제가 같이 올라가 수행됨.
  • 프로그램 카운터가 메모리 주소 중 운영체제가 존재하는 부분을 가리키고 있다면 현재 운영체제의 코드를 수행 중이며, 이 경우 CPU가 커널모드에서 수행 중이라는 뜻.
  • 프로그램 카운터가 사용자 프로그램이 존재하는 메모리 위치를 가리키고 있다면 그 메모리 위치에 올라가 있는 사용자 프로그램이 수행 중이며, 이 경우 사용자모드에서 CPU가 수행되고 있다는 뜻.
  • 일반명령

    • 메모리에서 자료를 읽어와 CPU에서 계산하고 결과를 메모리에 쓰는 일련의 명령들을 말하는데, 이러한 일반명령은 모든 프로그램이 수행할 수 있음.
  • 특권명령

    • 특권명령은 보안이 필요한 명령으로 입출력 장치, 타이머 등 각종 장치에 접근하는 명령임.
    • 특권명령은 항상 운영체제만이 수행할 수 있도록 제한하고 있음.
  • 두 명령어의 실행가능성을 체크하기 위해 CPU 내에 모드비트를 둠.
  • 사용자 프로그램이 스스로 특권 명령을 수행할 수 없으므로 운영체제에게 시스템 콜을 통해 특권명령의 대행을 요청하게 됨.
  • 주변장치는 CPU의 도움이 필요한 경우 인터럽트를 사용해 서비스를 요청하는데 이를 위해 주변장치는 인터럽트 라인을 세팅함. CPU는 매번 명령을 수행한 직후 인터럽트 라인을 체크해 서비스 요청이 들어왔는지 확인함.

3. 프로그램의 실행

  • '프로그램이 실행되고 있다'는 것은 컴퓨터 시스템 차원에서 볼 때 크게 두 가지 중요한 의미를 가짐.

    1. 디스크에 존재하던 실행파일이 메모리에 적재된다는 의미
    2. 프로그램이 CPU를 할당받고 명령을 수행하고 있는 상태라는 의미
  • 실행파일이 메모리에 적재될 때, 프로그램의 주소 공간 중 당장 CPU의 수행에 필요한 부분은 메모리에 올려놓고 그렇지 않은 부분은 디스크 중 메모리 연장 공간으로 사용되는 스왑 영역에 내려놓음.
  • 각각의 프로그램마다 주소 공간을 별도로 가지며, 이를 가상메모리(virtual memory) 또는 논리적 메모리(logical memory)라고 부른다.
  • 운영체제도 하나의 프로그램이므로 운영체제 커널 역시 코드, 데이터, 스택의 주소 공간 구성을 가지고 있음.

    Untitled (71)

    • 커널의 코드는 CPU, 메모리 등의 자원을 관리하기 위한 부분과 사용자에게 편리한 인터페이스를 제공하기 위한 부분이 주를 이룸. 시스템 콜 및 인터럽트를 처리하기 위한 부분도 포함함.
    • 커널의 데이터 영역에는 각종 자원을 관리하기 위한 자료구조가 저장됨.

      • CPU나 메모리와 같은 하드웨어 자원을 관리하기 위한 자료구조 + 프로세스 상태, CPU 사용 정보, 메모리 사용 정보 등을 유지하기 위한 PCB
    • 커널의 스택 영역은 일반 스택 영역과 마찬가지로 함수호출 시의 복귀 주소를 저장하기 위한 용도로 사용된다.

      • 다만, 현재 수행 중인 프로세스마다 별도의 스택을 두어 관리함.
      • 프로세스가 시스템 콜을 호출하고 시스템 콜 내부에서 다른 함수를 호출하는 경우 그 복귀 주소는 커널 내의 주소가 되어 사용자 프로그램의 스택과는 별도의 저장공간이 필요하기 때문.
      • 또한 커널은 일종의 공유 코드로서 모든 사용자 프로그램이 시스템 콜을 통해 커널의 함수를 접근할 수 있으므로, 일관성을 유지하기 위해서 현재 수행 중인 프로세스마다 별도의 스택을 두어 관리함.
      • 프로그램 내의 함수호출 시 해당 프로그램의 스택에 복귀 주소를 저장하지만, 시스템 콜이나 인터럽트 발생으로 CPU의 수행 주체가 운영체제로 바뀌는 순간에는 직전에 수행되던 프로그램의 복귀 정보를 스택이 아닌 PCB에 저장한다.

4. 사용자 프로그램이 사용하는 함수

  • 사용자 정의함수

    • 프로그래머 본인이 직접 작성한 함수.
  • 라이브러리 함수

    • 프로그래머 본인이 직접 작성하지는 않았지만 이미 누군가 작성해놓은 함수를 호출만 하여 사용하는 경우를 뜻함.
    • 사용자 정의함수와 라이브러리 함수 모두 프로그램의 코드 영역에 기계어 명령 형태로 존재한다. 따라서 프로그램이 실행될 때에 해당 프로세스의 주소 공간에 포함되며, 또한 함수호출 시에도 자신의 주소 공간에 있는 스택을 사용하게 된다.
  • 커널 함수

    • 운영체제 커널의 코드에 정의된 함수.
    • 사용자 프로그램이 운영체제의 서비스를 요청하기 위해 호출하는 시스템 콜 함수와, 각종 하드웨어 및 소프트웨어가 CPU의 서비스를 요청하기 위해 발생시키는 인터럽트 처리함수가 있다.
    • 운영체제 내에 있는 함수를 사용자 프로그램이 호출해서 사용하는 것.
    • 시스템 콜은 운영체제라는 별개의 프로그램에 CPU를 넘겨서 실행하는 것으로 넘기기 위해서 CPU의 인터럽트 라인을 세팅하는 방법을 사용함.

5. 인터럽트

  • 원칙적으로는 인터럽트 처리 중에 또 다른 인터럽트가 발생하는 것을 허용하지 않는다.

    • 그 이유는 인터럽트 처리 중에 다른 인터럽트를 처리하면 데이터의 일관성이 유지되지 않는 문제가 발생할 수 있기 때문.
  • 단, 인터럽트마다 중요도가 다르기 때문에 상대적으로 낮은 중요도를 가진 인터럽트를 처리하는 도중에 중요도가 더 높은 인터럽트가 발생하는 것을 허락할 필요는 있음.

    • 현재 처리 중이던 인터럽트 코드의 수행 지점을 저장하고 우선순위가 높은 인터럽트를 처리하게 됨.
    • 처리가 끝나면 저장된 주소로 복귀하고 마저 수행함.

6. 시스템 콜

  • 시스템 콜은 자신의 프로그램이 아닌, 커널이라는 다른 프로그램의 주소 공간에 존재하는 함수를 호출하는 것.
  • 주소 공간 자체가 다른 곳으로 이동해야 하므로 일반 함수호출과는 상이한 방법을 사용하는데 그 방법은 프로그램 자신이 인터럽트 라인에 인터럽트를 세팅하는 명령을 통해 이루어짐.
  • 디스크 파일 입출력이 이루어지는 과정을 통해 시스템 콜 사용의 예를 살펴보자.

    • 사용자 프로그램이 디스크의 파일을 읽어와야할 경우 시스템 콜로 커널의 함수를 호출하게 됨.
    • CPU의 제어권을 운영체제에 이양하게 되는데, 이는 인터럽트 라인을 세팅하는 명령을 통해 이루어짐.
    • 인터럽트 라인이 세팅되면 CPU는 다음 명령을 수행하기 전에 인터럽트가 발생했는지 점검하고 인터럽트가 발생된 것을 인지하면 현재 수행 중인 프로그램을 잠시 멈추고 CPU 제어권을 운영체제로 이양시킴.
    • 입출력 요청 인터럽트를 인지하게 되면 해당 서비스루틴으로 이동해 입출력 작업을 수행하게 되는데 이때 CPU는 디스크 컨트롤러에게 파일을 읽어오라는 명령을 하게 됨.
    • 입출력 작업은 시간이 많이 소요가 되므로 CPU의 제어권은 다른 프로세스에게 이양함.
    • 입출력 작업이 완료되면 디스크 컨트롤러가 CPU에게 인터럽트를 발생시켜 작업이 완료되었음을 알리게 됨.
    • CPU는 현재 프로세스 수행을 멈추고 인터럽트 처리루틴으로 제어권이 넘어가고 이때 발생한 인터럽트는 하드웨어 인터럽트에 해당함.
    • 디스크로부터 로컬버퍼로 읽어온 내용을 컴퓨터 내의 메모리로 복사한 후 디스크 입출력을 요청했던 프로세스에게 다시 CPU를 획득할 수 있는 권한을 줌(blocked state 해제)
    • 해당 프로세스는 CPU를 기다리는 큐에 삽입되고 CPU 제어권은 인터럽트를 당한 프로세스로넘어감.
  • 중간에 CPU를 빼앗기는 경우는 크게 두 가지가 있음.

    1. 타이머에 의해 인터럽트가 발생하는 경우
    2. 입출력 요청을 위해 시스템 콜을 하는 경우

7. 프로세스의 두 가지 실행 상태

  • 하나의 프로세스가 시작되어 수행을 완료하기까지 프로세스 자신의 주소 공간에 있는 코드만 실행되는 것이 아니라 커널의 주소 공간에 있는 코드도 실행됨.
  • 자신의 주소 공간에 정의된 코드를 실행하는 것을 사용자모드에서 실행 상태(user mode running)라 하고, 커널의 시스템 콜 함수를 실행하는 것을 커널모드에서의 실행 상태(kernel mode running)라 함.

    • 시스템 콜을 통해 실행되는 것이 프로세스 코드가 아닌 운영체제 커널의 코드라 해도 시스템 콜이 수행되는 동안 커널이 실행 상태(running state)에 있다고 하지 않고 프로세스가 실행 상태에 있다고 말한다.
    • 단, 구분지어서 '프로세스 A가 커널모드에서 실행 중'이라고 이야기함.
  • 정리하자면 프로그램은 다양한 함수호출을 하며 실행되는데, 이를 사용자모드, 커널모드의 실행 상태로 구분 지을 수 있음. 시스템 콜을 하는 경우 커널모드로 진입해 커널의 주소 공간에 정의된 함수를 실행하게 된다. 시스템 콜이 끝나면 다시 사용자모드로 복귀해 명령들을 계속 실행함. 프로그램 실행이 끝날 때는 커널모드로 진입해 프로그램을 종료함.