컴퓨터 시스템 구조

Hanjmo·2023년 8월 2일
0

운영체제를 공부하기에 앞서 컴퓨터 시스템의 구조가 어떻게 생겼고, 각각의 하드웨어가 무슨 역할을 하는지 간단하게 알아보자.

컴퓨터는 CPU가 Memory에서 Instruction을 읽고 실행함으로써 동작하게 된다.

Memory는 일시적으로 데이터를 저장하는 공간이며, 데이터를 영구적으로 저장하려면 Disk라는 하드웨어에 저장해야 한다.

Input/Output Device

Disk는 I/O 디바이스로 데이터를 Memory에 전달하거나, CPU의 처리 결과를 저장하는 역할을 수행한다. 즉, Disk는 input, output을 모두 수행한다.

I/O 디바이스에는 Disk 말고도 키보드, 프린터, 모니터 등이 존재하며, 이러한 각 디바이스들을 전담하는 device controller가 존재한다.

Device controller는 각 디바이스의 입력이나 출력이 끝났을 경우 CPU에 인터럽트를 거는 등 해당 디바이스를 제어하는 작은 CPU라고 생각하면 된다.

Device controller가 하드웨어라면, 소프트웨어인 device driver가 존재한다. Device driver는 운영체제가 해당 장치에 접근할 수 있도록(즉, 각 장치의 명령을 이해할 수 있도록) instruction을 변환해주는 역할을 수행한다.

CPU가 작업하는 공간으로 Memory가 있듯이 각각의 I/O 디바이스들도 그들만의 작업 공간이 있는데, 이를 local buffer라고 부른다. 이 local buffer에 입출력 데이터가 쌓이게 되며, 쌓인 데이터는 나중에 Memory에 복사되어 CPU가 실행하게 된다.

누가 local buffer에 있는 데이터를 memory에 복사해줄까?

local buffer에 있는 데이터를 memory에 복사해주는 친구는 바로 DMA Controller라는 친구다.

DMA는 Direct Memory Access의 약어로 직접 메모리에 접근 가능한 컨트롤러를 의미한다.

기존에는 I/O 장치가 너무 자주 인터럽트를 걸게 되어 CPU가 효율적으로 작업을 수행하지 못했는데, 이를 막기 위해 다음과 같은 방법으로 인터럽트 요청 빈도를 감소시켰다.

CPU는 계속해서 작업을 수행하고, DMA 컨트롤러가 직접 local buffer에 쌓인 내용을 메모리에 복사하도록 하고, DMA 컨트롤러의 작업이 모두 끝나면 CPU에게 인터럽트를 걸게 된다.

이러한 일련의 작업으로 인해 CPU가 인터럽트 요청을 받는 빈도가 감소하여 효율적으로 작업을 수행할 수 있게 되었다.

사용자 프로그램이 입출력 작업을 수행하는 방법

Memory 저장된 사용자 프로그램은 I/O 장치에 직접 접근할 수 없다.

I/O 장치에 접근하는 모든 명령은 보안 등의 이유로 OS를 통해서만 실행할 수 있도록 막아놓았기 때문이다.

그래서 프로그램이 입출력 작업을 수행하려면, 우선 OS에게 CPU 제어권을 넘겨주고 OS가 I/O 컨트롤러에게 해당 작업을 수행하도록 지시해야 한다.

이렇게 입출력 작업을 수행하는 동안 다른 프로그램에게 CPU를 넘겨 다음 작업들이 밀리지 않도록 한다.

입출력 작업이 끝났을 경우(예를 들어 키보드로 입력된 데이터가 local buffer에 들어오는 경우) 컨트롤러가 CPU에 인터럽트를 걸게 된다.

Local buffer에 입력된 값이 memory에 복사되고, CPU는 실행중인 작업을 끝낸 후 인터럽트를 확인하여 해당 입출력 명령을 수행한다.

Mode bit

만약 사용자 프로그램이 잘못된 수행을 하게 된다면, 다른 프로그램 및 OS에 큰 피해가 갈 것이다. 이를 막기 위해서 보호 장치를 만들었으며, 그것이 Mode bit다.

Mode bit가 1이냐 0이냐에 따라 사용자 모드, 모니터(커널) 모드로 구분된다.

  • 사용자 모드: CPU가 사용자 프로그램을 수행하며, 한정된 명령만 수행할 수 있게 된다.
  • 커널 모드: OS가 CPU의 제어권을 가지면서 어떤 작업이든 수행할 수 있게 된다.

보안을 해칠 수 있는 중요한 명령어는 커널 모드에서만 수행할 수 있도록 규정하여 사용자 모드에서 해당 명령어를 실행할 수 없도록 보호한다.

평소 사용자 프로그램에게 CPU를 넘길 때는 mode bit를 1로 세팅하여 사용자 모드가 되고, 인터럽트나 특정 예외가 발생하면 하드웨어가 mode bit를 0으로 바꿔 커널 모드가 된다.

시분할 방식(time sharing)을 구현하는 원리 (feat. timer)

만약 특정 프로그램이 무한 루프같은 작업으로 CPU를 독점하게 되면, 다음 명령은 실행되지 못하는 문제가 발생한다. 이 문제를 해결하기 위해 timer를 만들었으며, 동작 원리는 다음과 같다.

timer에 임의 시간을 설정하고, CPU가 어떤 프로그램을 실행하다가 timer에 설정한 시간이 만료되면 timer가 CPU에 인터럽트를 걸어서 해당 사실을 알려준다.

CPU는 하나의 명령을 실행한 직후에 반드시 인터럽트 라인을 확인하는데, timer가 보낸 인터럽트를 확인하게 되면서 CPU의 제어권이 OS에 넘어간다.

그럼 OS는 다시 timer의 시간을 설정하여 CPU가 다음 프로그램을 실행하게 한다.

이렇게 timer는 특정 프로그램이 CPU를 독점하는 것으로부터 보호하는 역할을 수행한다.

동기식 입출력과 비동기식 입출력

입출력 방식에는 동기식 입출력과 비동기식 입출력이 있으며, 두 방식의 차이는 다음과 같다.

  • 동기식 입출력(Synchronous I/O)
    사용자 프로그램이 커널에 입출력을 요청하면, 해당 입출력에 맞는 디바이스 드라이버를 거쳐 입출력이 진행된다. 입출력 작업이 끝나면 디바이스 컨트롤러가 인터럽트를 걸게 되면서 사용자 프로그램이 다음 작업을 진행한다.

  • 비동기식 입출력(Asynchronous I/O)
    사용자 프로그램이 커널에 입출력을 요청하여 입출력이 진행되는 과정은 동기식과 똑같다. 하지만 입출력이 모두 끝날 때까지 시간이 좀 걸리기 때문에 다음 작업들이 밀리게 된다. 그래서 입출력이 끝나기를 기다리지 않고, 사용자 프로그램이 바로 다음 작업을 진행한다.

결국 시간이 걸리는 입출력 과정을 기다리냐, 기다리지 않는냐에 대한 차이를 가지고 있는 것이다.

동기식 입출력처럼 입출력 과정을 기다려서 CPU가 아무것도 하지 못하면 그것은 낭비라고 볼 수 있다. 따라서 동기식 입출력은 두 가지 구현 방식이 존재한다.

  1. 위에서 말한 것처럼 입출력이 끝날 때까지 다른 작업을 수행하지 않는 방식으로 CPU를 낭비시킨다.
  2. 입출력이 끝날 때까지 해당 프로그램에게서 CPU를 빼앗고, 입출력 처리를 기다리는 줄에 그 프로그램을 줄 세운다. 그리고나서 빼앗은 CPU를 다른 프로그램에게 넘겨준다.

여기서 내가 한 가지 헷갈리는 부분이 있었는데, 동기식 입출력의 2번 구현 방식에서 입출력이 끝나지 않았는데도 다른 프로그램이 작업을 수행할 수 있다는 것을 보고 비동기식 입출력과 뭐가 다른지 이해가 되지 않았다.

그렇게 30분? 정도 찾아보다가 겨우 감을 잡았고, 그 내용은 다음과 같다.

동기식 입출력의 2번째 방식은 입출력을 요청한 프로그램이 다음 작업을 수행하지 못하는 것은 맞다. 하지만 해당 프로그램이 아닌 다른 프로그램은 본인의 작업을 수행할 수는 있다.

그럼 비동기식 입출력이랑 뭐가 다르냐?

비동기식 입출력의 경우 입출력이 진행되는 동안에 다른 프로그램이 아닌 입출력을 요청한 프로그램 본인의 다음 작업을 수행할 수 있다는 점이 동기식 입출력의 두 번째 구현 방법과 다르다.

내가 잘 이해한건지는 잘 모르겠지만, 우선 이렇게 두 방식의 차이를 구분하려고 한다.

서로 다른 입출력 명령어

참고로 입출력 명령어 형식은 일반적인 I/O와 Memory Mapped I/O 두 가지 방식에 따라 다르다.

좌측이 일반적인 I/O로 메모리에 접근하는 주소와 디바이스에 접근하는 주소가 각각 따로 있다. 각 주소가 따로 있기에 명령어도 따로 존재한다.

우측은 Memory Mapped I/O라고 불리며, 디바이스까지 메모리를 확장하여 메모리 주소를 할당한다. 그래서 메모리 주소에 접근하는 명령으로 각 디바이스에 접근이 가능하다.

저장 장치 계층 구조

저장 장치 계층 구조는 다음과 같이 생겼으며, 위에 있는 장치가 CPU와 가장 가까운 장치다.

위로 갈수록 CPU와 가까워진다는 뜻으로 속도가 빠르다. 대신에 일반적으로 가격이 비싸기 때문에 용량이 작다.

내부 기억장치는 휘발성이라서 전원이 꺼지면 데이터가 날아간다. 반대로 외부 기억장치는 비휘발성으로 데이터가 영구적으로 존재한다.
그리고 CPU는 내부 기억장치에 직접 접근해서 데이터를 처리할 수 있지만, 외부 기억장치에 있는 데이터는 직접 처리할 수 없다.

이러한 점들로 인해 캐싱이라는 기법을 사용하는데, 캐싱이란 용량이 크지만 느린 외부 기억장치에 있는 데이터를 더 빠른 내부 기억장치로 복사하는 기법으로 빠른 재사용이 가능해진다.

프로그램의 실행

프로그램은 어떤 과정을 거쳐 실행되는 것일까?

File system으로 사용되는 하드 디스크에 저장되어 있는 파일을 실행하면 가상 메모리에 해당 프로그램의 독립적인 주소 공간이 형성되는데, 이 주소 공간은 코드, 데이터, 스택으로 구성되어 있다.

  • 코드: 프로그램을 실행시키기 위한 기계어
  • 데이터: 전역 변수같은 프로그램이 사용하는 자료구조
  • 스택: 함수 호출과 관련된 데이터를 저장, 관리하는 영역

위와 같이 구성된 주소 공간을 물리적인 메모리에 올리는데, 이때 메모리 낭비를 막귀 위해 주소 공간에서 당장 필요한 부분만 물리적인 메모리에 올리고, 그렇지 않은 부분은 하드 디스크인 Swap area에 보관한다.

여기서 설명한 두 개의 하드 디스크는 각각 다음과 같은 용도로 사용된다.

  • File system: 전원이 나가도 파일의 내용이 유지되며, 비휘발성 용도로 사용
  • Swap area: 당장 필요하지 않은 부분을 저장하기 위한 메모리의 연장 공간으로 사용

💬

지금까지 운영체제를 배우기 전에 운영체제 밑단에 존재하는 컴퓨터 하드웨어 구조에 대해서 간단하게 살펴보았습니다.

이해한 컴퓨터 구조를 기반으로 이제부터 본격적으로 운영체제를 공부하여 포스팅하겠습니다.

0개의 댓글