컴퓨터 시스템의 동작원리

aaron lee·2024년 10월 10일

이화여대 컴퓨터공학과 운영체제 공개강의 수업을 듣고 정리한 내용입니다.
최초 발행일 2020-01-04

목차

컴퓨터 시스템의 구조

컴퓨터 시스템의 구조는 컴퓨터 내부 장치인 CPU, 메모리와 컴퓨터 외부 장치인 디스크, 키보드, 마우스, 모니터, 네트워크 장치 등으로 구분된다. 컴퓨터 내의 각 하드웨어 장치에는 컨트롤러라는 것이 붙어 있다. 컨트롤러는 일종의 작은 CPU로서, 컴퓨터 전체에 CPU라는 중앙처리 장치가 있듯이 컨트롤러는 각 하드웨어 장치마다 존재하면서 이들을 제어하는 작은 CPU라고 할 수 있다.

CPU와 I/O 연산

위 그림에서 볼 수 있듯이 각 하드웨어 장치마다 이를 제어하기 위해 설치된 장치 컨트롤러에는 장치로부터 들어오고 나가는 데이터를 임시로 저장하기 위한 작은 메모리를 가지고 있다. 이를 로컬 버퍼라고 부른다. 디스크나 키보드 등에서 데이터를 읽어오는 경우, 우선 로컬 버퍼에 데이터가 임시로 저장된 후 메모리에 전달된다.

이 때, 장치에서 로컬 버퍼로 읽어오는 일은 컨트롤러가 담당한다. 컨트롤러가 데이터를 읽어와 로컬 버퍼에 담은 후 인터럽트를 발생시켜 CPU에게 보고한다. 이때 인터럽트란 컨트롤러들이 CPU의 서비스가 필요할 때 이를 통보하는 방법을 말한다.

기본적으로 CPU는 매 시점 메모리에서 명령을 하나씩 읽어와서 수행한다. 이 때 CPU 옆에는 인터럽트 라인이 있어서 CPU가 자신의 작업을 하던 중간에 인터럽트 라인에 신호가 들어오면 하던 일을 멈추고, 인터럽트와 관련된 일을 처리한다. 좀더 정확히는 CPU는 명령 하나를 수행할 때마다 인터럽트가 발생했는지를 체크한다.

인터럽트의 일반적 기능

운영체제 커널에는 인터럽트가 들어왔을 때 해야 할 일을 미리 다 프로그래밍해서 보관하고 있다. 이를 인터럽트 처리 루틴이라고 부른다. 운영체제 커널 내에 있는 인터럽트 처리 루틴은 다양한 인터럽트에 대해 각각 처리해야 할 업무들을 정의하고 있다.

한편 인터럽트에는 하드웨어 인터럽트소프트웨어 인터럽트가 있다. 하드웨어 인터럽트는 컨트롤러 등 하드웨어 장치가 CPU의 인터럽트 라인을 셋팅하는 반면 소프트웨어 인터럽트는 소프트웨어가 이 일을 수행한다. 어떤 인터럽트이든 일단 인터럽트가 발생하면 CPU는 하던 일을 멈추고 인터럽트를 처리하기 위한 루틴에 들어가서 정의된 일을 찾는다.

운영체제는 할 일을 쉽게 찾아가기 위해 인터럽트 벡터를 가지고 있다. 인터럽트 벡터란 인터럽트 종류마다 번호를 정해서, 번호에 따라 처리해야 할 코드가 위치한 부분을 포인터로 가리키고 있는 자료구조를 말한다. 실제 처리해야 할 내용은 인터럽트 서비스 루틴이라는 다른 곳에 정의된다.

인터럽트 서비스 루틴을 통해 해당하는 인터럽트 처리를 완료하고 나면 원래 수행하던 작업으로 돌아가 정지되었던 일을 계속해서 수행하게 된다. 인터럽트 처리 후에 되돌아갈 위치를 알아야 하므로 인터럽트 처리 전에 수행중이던 작업이 무엇이었는지 반드시 저장해 두어야 한다. 이러한 정보를 저장하기 위한 장소를 운영체제는 별도로 가지고 있다.

프로그램의 메모리 주소를 조사해 보면 크게 코드, 데이터, 스택 등의 영역으로 나뉜다.

스택이란 실행중인 함수에서 다른 함수를 호출한 경우 호출된 함수의 종료 후 원래 함수의 실행중이던 위치로 돌아가기 위해 복귀 주소를 저장하는 영역이다. 호출된 함수를 모두 수행하고나면 원래 함수의 수행되던 위치로 돌아가게 되는데 이때 스택에 저장되어 있는 복귀 주소를 사용하게 된다.

데이터 영역은 전역변수 등 프로그램이 사용하는 가종 데이터가 저장되는 공간이다. 데이터 영역에 저장되어 있는 전역 변수들은 동일한 프로그램 내의 모든 함수들이 공유해 사용할 수 있다.

코드 부분은 프로그래머가 작성한 코드가 기계어 명령 형태로 저장되는 영역이다. CPU는 매 시점 코드 부분에 있는 명령을 하나씩 읽어와서 수행한다.

인터럽트 핸들링

인터럽트 핸들링이란 인터럽트가 발생한 경우에 처리해야 할 일의 절차를 의미한다. 프로그램 A가 수행되고 있을 때 인터럽트가 발생하면 A의 현재 상태를 저장하게 된다. 현재 상태란 현재 CPU가 수행중이던 메모리 주소를 포함해 몇 가지 부가적인 정보들을 의미한다.

CPU에서 명령이 수행될 때에는 CPU 내부에 있는 임시 기억장치인 레지스터(register)에 데이터를 읽어오거나 쓰면서 작업을 수행하는데, 이 때 인터럽트가 발생해 새로운 명령을 수행하면 기존의 레지스터 값들이 지워지게 되므로 레지스터 값 등 CPU 내의 하드웨어 상태를 저장해 두어야 한다. 이것을 모두 저장한 후에야 인터럽트 처리가 이루어질 수 있다.

운영체제 커널 영역에는 현재 시스템 내에서 수행되는 프로그램들을 관리하기 위한 자료구조인 프로세스 제어 블록(PCB: Process Control Block)을 두고 있다. 여기에 저장되는 내용으로는 현재 수행중이던 메모리 주소와 레지스터 값, 그리고 하드웨어 상태 등이 있다. 이러한 정보가 저장되었다가 인터럽트 수행이 끝나면 저장된 값을 CPU 상에 다시 복원해 인터럽트당하기 직전의 명령을 계속 수행할 수 있는 것이다.

소프트웨어 인터럽트

통상적으로 인터럽트라고 하면 하드웨어 인터럽트를 의미하고, 소프트웨어 인터럽트는 트랩(trap)이라는 용어로 주로 불린다. 소프트웨어 인터럽트의 예로는 예외 상황(exception), 시스템 콜(system call) 등이 있다.

예외 상황이란 프로세스가 0으로 나누는 연산 등 불가능한 작업을 시도하거나, 자신의 메모리 영역 바깥을 접근하려는 시도를 할 때 이에 대한 처리를 위해 발생시키는 인터럽트를 말한다. 소프트웨어 인터럽트는 프로그램 수행 도중 직접 CPU에 인터럽트 라인을 셋팅하여 발생시킨다.

오늘날의 컴퓨터에서 운영 체제는 인터럽트 발생시에만 수행된다. 이는 시스템이 부팅 후 정상 상태에 머무르게 되면 CPU는 항상 사용자 프로그램에 의해 사용되며 운영체제는 단지 인터럽트 발생시에만 잠깐 CPU의 제어권을 획득할 수 있기 때문이다. 즉 운영체제가 직접 CPU를 점유하는 경우는 인터럽트에 의하지 않고는 발생하지 않는다.

시스템 콜의 예로는 프로그래밍을 처음 시작하는 사람들이 가장 많이 접하는 printf() 함수가 있다. 이 함수를 통해 원하는 내용을 화면에 출력할 수 있지만 실제 printf() 함수의 내용은 직접 작성하지 않는다. 이것은 printf()의 내용을 이미 라이브러리 함수로 누군가 작성해 두었고, 이 라이브러리 함수가 궁극적으로는 운영체제 내에 정의된 write()라는 시스템 콜을 호출하기 때문이다.

화면에 정보를 출력하는 printf() 함수를 호출하면 라이브러리 함수인 printf()는 일종의 인터럽트 매커니즘인 write() 시스템 콜을 통해 커널에 입출력 명령을 대신 수행하도록 요청하게 된다. 그러면 운영 체제는 해당하는 인터럽트를 처리하기 위한 루틴을 찾고 화면에 출력하라는 요청에 따라 정보를 화면에 출력하게 된다.

입출력 구조 : 동기식 입출력

입출력(I/O)이란 컴퓨터 시스템이 컴퓨터 외부의 주변 장치들과 데이터를 주고받는 것을 말한다. 입출력 방식에는 동기식 입출력과 비동기식 입출력이 있다. 동기식 입출력(synchronous I/O)은 입출력 요청 후 입출력 작업이 완료된 후에야 CPU의 제어권이 그 프로그램에게 다시 넘어갈 수 있는 방식을 말한다.

입출력이 진행되는 동안 그 프로그램의 다음 명령을 수행하지 않고 기다리게 된다. 입출력이 완료되어 인터럽트를 통해 그 사실이 전달된 후에 CPU의 제어권이 다시 그 프로그램에게 넘어가서 다음 명령을 수행할 수 있게 된다. 따라서, 동기식 입출력에서는 입출력 연산이 끝날 때까지 CPU는 아무 일을 수행할 수 없다.

위의 경우 자원이 낭비되기 때문에 일반적으로 프로그램이 입출력을 수행중인 경우 CPU를 다른 프로그램에게 이양해 CPU가 쉬지 않고 계속 일할 수 있도록 관리한다.

예를들어 프로그램 A가 CPU를 할당받고 명령을 수행하다가 입출력 요청을 하게 되면 입출력 작업이 완료될 때까지 CPU의 낭비가 초래되므로 CPU를 프로그램 A에게서 선점해 다른 프로그램 B에게 할당한다. 그러면 프로그램 B가 CPU를 할당받아 명령을 수행하고 프로그램 A의 입출력이 완료될 때까지 A에게는 CPU를 다시 할당하지 않는다.

이는 입출력이 완료될 때까지 그 프로그램에 CPU를 할당하더라도 명령을 수행하지 못하기 때문인데, 이것을 프로그램 봉쇄 상태(blocked state)로 전환시킨다고 말한다. 봉쇄 상태의 프로그램에게는 CPU를 할당하지 않고, CPU를 할당하면 곧바로 작업을 수행할 수 있는 프로그램들에게만 CPU를 할당한다.

입출력 구조 : 비동기식 입출력

비동기식 입출력에서는 CPU의 제어권이 입출력을 요청한 프로세스에게 곧바로 다시 주어지게 되며, 입출력 연산이 완료되는 것과 무관하게 처리 가능한 작업부터 처리한다. 비동기식 입출력에서도 입출력 연산이 완료되면 동기식과 마찬가지로 인터럽트를 통해 이를 CPU에게 알려주게 된다. 그러면 그 시점부터 읽어온 데이터를 필요로 하는 명령을 수행할 수 있게 된다.

DMA(Direct Memory Access)

원칙적으로 메모리는 CPU에 의해서만 접근 가능한 장치이다. 따라서 주변 장치들이 메모리에 접근하기 위해서는 CPU에 인터럽트를 발생시켜 CPU가 일을 처리하는 식으로만 가능하다. 컨트롤러가 CPU에게 인터럽트를 발생시키면 CPU는 컨트롤러의 로컬 버퍼와 메모리 사이에서 데이터를 옮겨주는 역할을 하게 된다. 하지만 모든 메모리 접근 연산이 CPU에 의해서만 이루어질 경우 주변 장치가 메모리 접근을 원할 때마다 CPU의 업무가 방해되어 CPU 사용의 효율성이 떨어진다.

이를 방지하기 위해 CPU 이외에 메모리 접근이 가능한 장치를 하나 더 두는 경우가 많은데 이와 같은 장치를 DMA라고 한다. DMA를 사용하게 되면 로컬 버퍼에서 메모리로 데이터를 읽어오는 작업을 CPU가 담당하는 것이 아니라, DMA가 대행함으로써 CPU는 원래 하던 작업을 멈추고 인터럽트를 처리할 필요가 없어지는 것이다.

저장 장치의 구조

컴퓨터 시스템을 구성하는 저장 장치는 주 기억 장치보조 기억 장치로 나누어 볼 수 있다. 주 기억 장치는 보통 메모리라고 부르며 전원이 나가면 저장되었던 내용이 모두 사라져 버리는 휘발성의 RAM을 사용하는 경우가 대부분이다. 반면에 보조 기억 장치는 전원이 나가도 저장된 내용을 기억할 수 있는 비휘발성의 마그네틱 디스크를 흔히 사용한다.

보조 기억 장치의 용도는 크게 두 가지로 구분된다. 첫번째는 파일 시스템용이다. 전원이 나가도 유지해야 할 정보가 있으면 그것을 파일 형태로 보조 기억장치에 저장하게 된다. 두번째는 메모리의 연장 공간인 스왑 영역(swap area)용이다. 메모리는 크기가 한정되고 가격이 상대적으로 비싸고 용량이 적다. 따라서 메모리 공간이 부족할 경우가 흔히 발생한다.

이 경우 운영체제는 프로그램 수행에 당장 필요한 부분만 메모리에 올려놓고 그렇지 않은 부분은 디스크의 스왑 영역에 내려놓게 된다. 디스크에 내려놓는 일을 스왑 아웃(swap out)시킨다고 말하며, 스왑 아웃된 부분이 필요할 때에는 다시 메모리 영역으로 올리게 된다.

저장 장치의 계층 구조

os-storage-device-hierarchy-structure

컴퓨터 시스템을 구성하는 저장 장치는 빠른 저장 장치부터 느린 저장 장치까지 단계적인 계층 구조로 이루어진다. CPU에서 직접 접근할 수 있는 메모리 스토리지 매체를 Primary(Executable)이라고 하고 CPU가 직접 접근 할 수 없는 메모리를 Secondary라고 한다.

CPU가 직접 접근하기 위해서는 바이트 단위로 접근이 가능한 매체여야 한다. DRAM 메모리는 바이트 단위로 주소를 매겨서 바이트 단위 접근이 가능하기 때문에 Executable 하다. 그에 비해 하드 디스크같은 경우는 바이트 단위 접근이 아니라 섹터 단위 접근이기 때문에 Executable 하지 않다.

CPU는 빠르게는 1 클락 당 인스트럭션 하나씩 처리하게 되는데 DRAM 메모리를 접근하려면 적게는 10에서 많게는 100클락 싸이클까지 걸리기 때문에 오래 걸린다. 이러한 속도 차이를 완충하기 위해 중간에 캐시 메모리라는 것을 두고 레지스터로 읽어들이고 이러한 작업을 수행하게 된다. 캐싱은 주로 재사용을 위해 사용한다.

캐슁 기법은 상대적으로 느린 저장 장치에 있는 내용 중에 당장 사용되거나 빈번히 사용될 정보를 빠른 저장 장치에 선별적으로 저장해 두 저장 장치 사이의 속도를 완충시키는 기법을 의미한다. 빠른 저장장치에 빈번히 사용될 정보를 저장하면 필요한 정보를 빠른 저장 장치에서 곧바로 찾을 수 있는 경우가 많아서 전체적인 성능이 향상된다.

하드웨어의 보안

우리가 흔히 사용하는 운영 체제는 여러 프로그램이 동시에 실행될 수 있는 다중 프로그래밍 환경에서 동작한다. 그러므로 각 프로그램들이 다른 프로그램의 실행을 방해하거나 프로그램 간에 서로 충돌을 일으키는 문제를 막기 위해 하드웨어에 대한 각종 보안 기법이 필요하다. 하드웨어적인 보안을 유지하기 위해 운영체제는 기본적으로 두 가지 모드의 오퍼레이션을 지원한다. 커널 모드사용자 모드가 이에 해당한다.

커널 모드는 운영체제가 CPU의 제어권을 가지고 운영체제 코드를 실행하는 모드로서, 이 모드에서는 모든 종류의 명령을 다 실행할 수 있다. 반면, 사용자 모드에서는 일반 사용자 프로그램이 실행되며 제한적인 명령만을 수행할 수 있다. 시스템에 중요한 영향을 미치는 연산은 커널 모드에서만 실행 가능하도록 함으로써 하드웨어의 보안을 유지하는 것이다.

컴퓨터 시스템은 CPU 내부에 모드 비트(mode bit)를 두어 사용자 프로그램을 감시하게 된다. 모드 비트가 0으로 셋팅되어 있으면 커널 모드로서 모든 명령을 수행할 수 있고, 모드 비트가 1로 셋팅되어 있으면 사용자 모드로서 제한된 명령만을 수행할 수 있다. CPU는 보안과 관련된 명령을 수행하기 전에는 항상 모드 비트가 0으로 셋팅된 경우에만 그 명령을 수행한다. 운영체제가 CPU를 점유해 자신의 코드를 수행하다가 사용자 프로그램에게 CPU의 제어권을 넘길 때 모드 비트를 1로 셋팅해 넘기게 된다.

시스템의 보안과 관련된 명령들을 특권 명령이라 지칭하며, 특권 명령은 모드 비트가 0일 때에만 수행할 수 있게 된다. 즉, 특권 명령은 커널 모드에서 운영 체제에 의해서만 수행이 가능한 것이다.

메모리 보안

메모리에도 보안이 필요하다. 여러 프로그램이 메모리에 동시에 올라가서 실행되기 때문에 하나의 사용자 프로그램이 다른 사용자 프로그램이나 운영 체제가 위치한 메모리 영역을 침범할 수 있기 때문이다. 이러한 문제를 해결하기 위해 두 개의 레지스터를 사용해서 프로그램이 접근하려는 메모리 부분이 합법적인지를 체크함으로써 메모리 보호가 이루어진다. 이 때 사용되는 두 개의 레지스터는 기준 레지스터(base register)한계 레지스터(limit register)이다.

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

즉 어떤 프로그램이 실제 메모리에 올라가 있는 부분의 시작주소와 그 프로그램의 길이를 각각 기준 레지스터와 한계 레지스터에 보관해 메모리 접근 연산이 있을 때마다 하드웨어적으로 현재 접근하려는 위치가 합법적인 범위인지를 체크한다.

합법적인 범위 외의 주소에 접근하면 불법적인 메모리 접근이므로 예외 상황이라는 일종의 소프트웨어적인 인터럽트가 발생하게 된다. 예외 상황에는 운영체제에게 소프트웨어적인 인터럽트를 발생시켜 CPU의 제어권을 해당 프로그램으로부터 운영체제로 이양시키고 운영체제는 예외 상황을 발생시킨 프로그램을 강제로 종료시킨다.

위의 메모리 보호 기법은 하나의 프로그램 메모리의 한 영역에 연속적으로 위치한 경우에 대한 설명이다. 추후에 살펴볼 메모리 관리 기법에서는 하나의 프로그램이 메모리 여러 영역에 나뉘어 위치하는 페이징(paging) 기법 등에 대해서 다룬다. 이와 같은 좀 더 현실적인 시스템에서의 메모리 보호에 대해 살펴보게 될 것이다.

CPU 보호

CPU가 하나의 프로그램에 의해 독점되는 것을 막기 위해 운영체제는 타이머라는 하드웨어를 사용한다. 타이머는 정해진 시간이 지나면 인터럽트를 발생시켜 운영체제에게 CPU의 제어권을 이양시키는 역할을 한다. 타이머는 일정한 시간 단위로 셋팅될 수 있으며 매 클럭 틱 때마다 1씩 감소한다. 타이머가 0이 되는 순간에 인터럽트가 발생하게 된다.

profile
소프트웨어 엔지니어

0개의 댓글