컴퓨터 시스템 구조

### CPU

  • 매 클럭마다 Memory에서 Instruction을 하나씩 읽어서 실행
  • Interrupt line의 값 체크. 값이 없으면 다음 메모리 명령어 실행

registers

  • CPU에 붙어 있는 메모리보다 빠른 저장공간
  • 프로그램 카운터 레지스터: 카운터가 가리키는 메모리 주소의 Instruction을 읽어와서 실행하고 다음 Instruction을 순차적으로 실행

mode bit

현 실행 프로그램이 운영체제인지 사용자 프로그램인지 구분

  • mode bit 0: 운영체제가 CPU 실행 중이므로 모두 접근 가능
  • mode bit 1: 사용자 프로그램이 CPU 실행 중이므로 제한된 Instruction만 실행 가능

Interrupt line

CPU 옆에 인터럽트 라인이 있어서 CPU가 자신의 작업을 하던 중간에 인터럽트 라인에 신호가 들어오면 하던 일을 멈추고 인터럽트와 관련된 일을 먼저 처리한다. 좀 더 정확히 설명하면, CPU는 명령 하나를 수행할 때마다 인터럽트가 발생했는지 확인한다.

Timer

  • 목적: 한 프로그램이 CPU를 독점(예: 무한루프)을 방지
  • 운영체제가 사용자 프로그램에 CPU사용권을 줄 때 Timer에 값(보통 수십 ms)을 세팅하고 넘겨준다.
  • 시간이 지나면 Timer가 CPU에게 Interrupt를 걸고 사용자 프로그램에서 운영체제로 다시 CPU 제어권이 넘어간다.

DMA Controller

  • 직접 메모리를 액세스할 수 있는 컨트롤러
  • 원래는 메모리 접근할 수 있는 장치는 CPU뿐이었는데 DMA Controller를 두면 DMA Controller도 메모리 접근 가능하다.
  • 잦은 I/O의 Interrupt를 방지하기 위해 CPU 대신 I/O device의 데이터를 메모리로 복사 등의 작업을 수행

Memory

  • CPU의 작업공간
  • 사용자 프로그램은 I/O 작업을 직접할 수 없다. 보안 등의 이유로 OS만 I/O가능하므로 운영체제에게 권한을 넘기고 운영체제는 CPU를 통해서 I/O 디바이스컨트롤러에게 작업 전달
  • Memory Controller
    • CPU와 DMA 접근의 교통제어 역할
  • I/O device
    • 종류
      • Disk
        • 보조기억장치이면서 I/O device
        • Input: 하드의 데이터를 읽어서 메모리의 Input으로 보낸다, output: 처리결과를 하드디스크에 저장
      • 키보드, 모니터..
    • Device Controller: I/O device들의 작은 CPU 역할, 모든 기기마다 하나씩 있음
      • 예: 디스크 헤드를 어떻게 돌릴지를 CPU가 아니라 디바이스마다 있는 디바이스 컨트롤러가 제어
    • local buffer: I/O device들의 작은 Memory 역할, 모든 기기마다 하나씩 있음

Device Controller

  • CPU가 I/O에 작업을 맡길 때
    • I/O device 제어 레지스터(device controller)가 제어를 담당한다.
    • I/O device 로컬 버퍼에 데이터를 담는다.
    • device driver
      • 각 디바이스의 인터페이스에 맞게 접근 가능하게 해주는 소프트웨어
      • 실제 I/O device를 움직이는 코드는 아님
      • CPU가 직접 일하는 게 아니라 메모리의 Instruction을 받아서 일하는 것처럼 디바이스를 실행하는 메뉴얼은 펌웨어에 있다?

I/O 수행

  • I/O는 운영체제를 통해서만 접근 가능
  • 사용자프로그램이 운영체제에 부탁하는 것 –> System call
    • 운영체제에 해당하는 주소로 넘어가야 하는데 Mode bit이 1인 상태
    • 사용자 프로그램이 직접 인터럽트 라인을 세팅
    • Mode bit이 0으로 바뀌고 운영체제가 I/O Device 컨트롤러에게 요청
  • Trap(소프트웨어 인터럽트) : 사용자프로그램
    • Exception: 프로그램이 오류를 범한 경우
    • System call: 프로그램이 커널 함수를 호출하는경우

Interrupt

  • 현대의 운영체제는 인터럽트에 의해 구동됨
    • 인터럽트가 들어올 때만 CPU가 운영체제에 넘어감
    • 그렇지 않으면 보통 사용자 프로그램이 CPU를 쓴다.

하드웨어, 소프트웨어 인터럽트

CPU 옆 인터럽트 라인에 신호를 보내는 방식은 동일하다. 하드웨어, 소프트웨어 모두 인터럽트가 발생하면 운영처제 커널 내 해당 인터럽트 처리를 위해 정의된 코드(인터럽트 벡터)를 찾는다.

다만 하드웨어 인터럽트는 컨트롤러 등 하드웨어 장치가 CPU의 인터럽트 라인을 세팅한다.

소프트웨어 인터럽트는 소프트웨어가 그 일을 수행한다.

인터럽트 벡터

인터럽트 종류마다 번호를 정해서 이에 따라 처리애햐 할 코드가 위치한 부분을 가리키는 자료구조다. 해당 인터럽트 처리 루틴 주소를 가지고 있는 셈이다.

실제 처리할 코드는 인터럽트 처리 루틴(Interrupt service routine) 내지 인터럽트 핸들러(Interrupt handler)에 정의된다.

인터럽트 처리 루틴

인터럽트가 들어왔을 때 CPU가 해야 할 일들이 커널 내 코드로 정의돼 있다. 그 중 한 가지가 인터럽트 처리루틴인데 인터럽트에 대해 처리해야 할 다양한 업무들이 나열돼 있다.

인터럽트 처리를 완료하면 원래 수행하던 작업으로 돌아가야 하는데 그 위치를 저장하기 위한 장소를 운영체제는 별도로 가지고 있다.

소프트웨어 인터럽트

통상 인터럽트라고 하면 하드웨어 인터럽트를 의미하고 소프트웨어 인터럽트는 트랩(Trap)이라는 용어로 불린다.

소프트웨어 인터럽트의 예로 예외상황(Exception)과 시스템 콜이 있다. 시스템 콜은 사용자 프로그램이 운영체제 내부에 정의된 코드를 실행하고 싶을 때 운영체제 서비스에 요청을 하는 방법이라고 할 수 있다.

예를 들어 애플리케이션 개발자가 프로그램 작성 중 키보드 입력이나 화면 출력 등의 입출력 작업이 필요한 경우 본인이 직접 입출력 수행 코드를 작성하는 게 아니라 이미 조냊하는 커널의 코드를 호출해서 처리한다.

인터럽트 핸들링

인터럽트 핸들링이란 인터럽트가 발생한 경우에 처리해야 할 일의 절차를 의미한다. 프로그램 A가 실행되고 있을 때 인터럽트가 발생하면 A의 현재 상태를 먼저 저장한다.

운영체제는 실행 프로그램을 관리하기 위해 프로세스 제어블록(PCB)이라는 자료구조를 둔다. 각 프로그램마다 하나씩 존재하는데 프로그램의 어느 부분이 실행 중이었는지를 저장하고 있다. 구체적으로 메모리 주소, 레지스터값, 하드웨어 상태 등이 저장된다.

인터럽트가 발생하면 실행 상태를 PCB에 저장한 후 CPU 제어권이 인터럽트 처리루틴으로 넘어가며 인터럽트 처리가 끝나면 PCB에 저장된 상태를 복원해 다시 실행이 이어진다.

커널 주소 공간의 내용

  • PCB(Process Control Block): 프로그램이 돌아가면 이를 관리하기 위한 자료구조가 커널에 만들어지는데 이것이 PCB다.
  • Stack: 커널 코드를 함수로 사용하므로 스택, 사용자 프로그램마다 스택을 따로 씀
  • 커널함수, 라이브러리 함수, 사용자가 쓴 함수 모두 다른 영역
  • 커널 함수의 호출: 시스템 콜

입출력 구조

동기식 입출력(Synchronous I/O)

어떤 프로그램이 입출력 요청을 했을 때 입출력 작업이 완료된 후에야 그 프로그램이 후속 작업을 수행할 수 있는 방식을 말한다.

동기식 입출력을 요청한 프로그램은 입출력이 완료될 때까지 다음 명령을 수행할 수 없기 때문에 그동안 CPU가 낭비된다.

CPU의 효율적인 사용을 위해 입출력이 수행되는 동안 다른 프로그램에게 CPU를 양도할 수 있다. 그러나 동시에 다수 입출력 연산이 일어나 원하지 않는 결과가 나올 수 있으므로 동기성을 보장하기 위해 장치마다 큐를 두어 요청된 순서대로 처리한다.

컨트롤러는 큐의 순서에 따라 하나씩 입출력 작업을 처리하는데 CPU는 이때 입출력과 관련 없는 프로그램을 수행하고 요청된 입출력 연산이 완료되면 CPU에게 완료를 알려주는 방식(인터럽트)으로 진행된다.

비동기식 입출력(Asynchronous I/O)

비동기식 입출력은 입출력 연산을 요청한 후에 연산이 끝나기를 기다리는 것이 아니라 CPU의 제어권을 입출력 연산을 호출한 프로그램에게 곧바로 다시 부여하는 방식을 말한다.

어떤 프로그램이 데이터를 디스크에서 읽어오라는 요청을 했을 때 보통은 읽어온 결과를 이용해서 다음 연산을 수행하지만 경우에 따라서는 그 데이터와 관련 없이 수행할 수 있는 일이 있을 수 있다.

비동기식 입출력에서는 그러한 작업을 먼저 수행하고, 읽어오는 데이터가 반드시 있어야 수행할 수 있는 일들은 입출력이 완료된 후에 수행한다.

또한 읽어오는 요청이 아니라 디스크에 쓰는 요청이라면 쓰기 작업이 완료되기 전에도 다음 명령을 수행할 수 있으므로 비동기식 입출력이 사용될 수 있다.

DMA

원칙적으로 메모리는 CPU에 의해서만 접근할 수 있는 장치다. 그러나 모든 메모리 접근 연산이 CPU에 의해서만 이루어지면 CPU 업무가 방해를 받으므로 효율성이 떨어진다.

이에 CPU 이외에 메모리 접근 가능한 장치를 하나 더 두는 경우가 많은데 이러한 장치를 DMA(Direct Memory Access)라 부른다.

DMA는 바이트 단위가 아니라 블록이라는 큰 단위로 정보를 메모리로 읽어온 후 CPU에게 인터럽트를 발생시켜 해당 작업의 완료를 알려준다.

저장장치 구조

주기억 장치: 메모리

보조기억장치는 파일을 저장하는 용도 이외에 메모리의 연장 공간이 스왑 영역(Swap area)로도 사용된다.

메모리 공간이 부족한 경우 당장 필요한 부분만 메모리에 올려놓고 그렇지 않은 부분은 스왑 영역에 내려놓는데 이를 스왑 아웃(Swap out)시킨다고 말한다.

프로그램의 실행(메모리 로드)

  • 실행파일을 실행하면 버추얼메모리에 올라가고 이후 메모리로 올라가 프로세스가 된다.
  • 실행 시 독자적인 버추얼 메모리 주소 공간이 생긴다.
  • 당장 필요한 부분만 버추얼 메모리에서 메인 메모리에 올린다. 그렇지 않은 메모리는 디스크(Swap area)로 내린다.
    • Swap area: 메인 메모리의 연장 공간으로써 하드디스크. 전원이 나가면 프로세스 종료되므로 의미 없어짐
  • Address Translation: 로지컬 메모리 주소를 메모리 주소로 변환

하드웨어의 보안

중요한 정보에 접근해 위험한 상황을 초래할 수 있는 연산은 커널모드에서만 실행하고 일반적인 연산만 사용자 모드에서 사용자 프로그램이 수행하도록 통제해 보안성을 확보한다.

CPU 내부에 모드 비트를 두어 사용자 프로그램이 운영체제의 코드를 실행하지 못하도록 감시한다. 모드비트가 0이면 커널모드로 모든 명령을 수행할 수 있고 1이면 사용자모드로 제한된 명령만 수행할 수 있다.

모드비트 0에서 수행하는 시스템의 보안과 관련된 명령을 특권명령이라 한다. 사용자 프로그램이 디스크 입출력 시 자신이 아닌 다른 사람의 파일에 접근할 수 있으므로 입출력은 특권명령으로 관리한다.

메모리 보안

여러 프로그램이 메모리에 동시에 올라가서 실행되므로 한 사용자 프로그램이 다른 프로그램이나 운영체제가 위치한 위치한 메모리 영역을 침범할 수 있다. 이를테면 주소 참조 연산을 잘못 사용해 다른 메모리 역역이나 커널이 위치한 영역을 참조하려는 시도다.

이에 적어도 인터럽트 벡터와 인터럽트 처리루틴은 각별한 보안이 필요하다. 보안을 위해 기준 레지스터와 한계 레지스터를 사용한다.

기준 레지스터는 어떤 프로그램이 수행되는 동안 프로그램이 합법적으로 접근할 수 있는 메모리상 가장 작은 주소를 보관하고 있다.

한계 레지스터는 그 프로그램이 기준 레지스터값부터 접근할 수 있는 메모리의 범위를 보관하고 있다.

사용자 프로그램은 기준 레지스터에 있는 주소부터 기준 레지스터+한계 레지스터 값 사이의 주소 영역에만 접근할 수 있다.

범위를 벗어나면 인터럽트를 발생시켜 CPU 제어권이 프로그램에서 운영체제로 넘어가며 운영체제는 프로그램을 강제 종료시킨다.

단 위 경우는 메모리가 연속적으로 위치한 사례에 해당하고 메모리가 여러 영역에 나뉘어 위치한 페이징 기법 등을 사용하는 사례에서는 레지스터 뿐만 아니라 하드웨어의 지원이 필요하다.

CPU 보호

타이머라는 하드웨어를 사용해 운영체제는 특정 프로그램의 CPU 독점을 막는다. 타이머가 0이 되는 순간 인터럽트가 발생하는데 여기서 타이머 값을 세팅하는 명령을 로드 타이머라고 한다. 이는 특권명령에 속한다.

시스템 콜을 이용한 입출력 수행

사용자 프로그램이 디스크 파일에 데이터를 쓰거나 키보드로부터 입력을 받는 등의 행위는 모두 특권명령에 해당한다. 운영체제 코드에 이러한 입출력 명령이 구현돼 있고 사용자 프로그램은 시스템 콜이라는 서비스 대행 요청을 하여 입출력을 수행한다.

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

어떤 프로그램이든 내부 구조는 함수들로 구성된다.

프로그램이 CPU에서 명령을 수행하려면 그 명령을 담은 프로그램의 주소 영역이 메모리에 올라가 있어야 한다.

이때 주소 영역은 코드, 데이터, 스택 영역으로 구분된다.

코드 영역은 코드가 CPU에서 수행할 기계어 명령 형태로 저장되는 부분이다.

데이터 영역은 전역 변수 등 프로그램이 사용하는 데이터를 저장하는 부분이다.

스택 영역은 함수가 호출될 때 호출된 함수의 수행을 마치고 복귀할 주소 및 데이터를 임시로 저장하는 데 사용되는 부분이다.

반면 인터럽트 때문에 CPU를 빼앗긴 위치는 운영체제가 관리하는 프로세스 제어블록에 저장된다.

프로그램의 실행

현재 수행 중인 프로그램을 프로세스라고 부르고 커널의 데이터 영역 내에는 각 프로세스의 상태, CPU 사용 정보, 메모리 사용 정보 등을 유지하기 위한 자료구조인 PCB를 두고 있다.

커널의 스택 영역은 일반 프로그램의 스택 영역과 마찬가지로 함수호출 시의 복귀 주소를 저장하기 위한 용도로 사용된다. 다른 점은 일반 사용자 프로그램의 스택과 달리 현재 수행 중인 프로세스마다 별도의 스택을 두어 관리한다는 것이다.

이는 프로세스가 특권명령을 위해 시스템 콜을 호출하여 시스템 콜 내부 함수를 호출하면 복귀 주소가 커널 내 주소가 되므로 사용자 프로그램과는 별도의 저장공간이 필요하게 된다.

시스템 콜

시스템 콜은 함수호출이지만 자신의 주소 공간을 거르스는 영역에 존재하는 함수를 호출한다. 자신의 프로그램이 아닌 커널의 주소 공간에 존재하는 함수를 호출하는 것이다.

일반 함수호출이 자신의 스택에 복귀 주소를 저장한 후 호출된 함수 위치로 점프하는 것임에 비해 시스템 콜은 주소 공간 자체가 다른 곳으로 이동해야 한다. 이에 일반 함수호출과 달리 자신이 인터럽트 라인에 인터럽트를 세팅하는 명령을 통해 이루어진다.

디스크 파일 입출력 예를 살펴보자. 사용자 프로그램이 디스크 파일 명령을 읽어와야 할 경우 시스템 콜로 커널 함수를 호출한다. 이때 사용자프로그램 자신의 주소 공간에서 이뤄질 수 없어서 CPU 제어권을 운영체제에게 이양한다. 이는 인터럽트 라인을 세팅하는 명령으로 이뤄진다.

인터럽트 라인이 세팅되면 CPU는 제어권을 운영체제로 이양한다. 운영체제는 설정된 인터럽트 라인에 의해 이번에 발생한 인터럽트가 입출력을 요청하는 인터럽트임을 인지한다. 이후에 해당 서비스루틴으로 이동해 입출력 작업을 수행한다. 이 과정에서 CPU는 디스크 컨트롤러에게 파일을 읽어오라는 명령을 한다.

Sources