우리는 시스템 동작을 자세히 배우기 전에 컴퓨터 시스템 구조에 대한 일반적인 지식을 가질 필요가 있다. 이 절에서는 컴퓨터 시스템 구조의 여러부분을 살펴본다. 이 절의 대부분은 컴퓨터 시스템 구조와 관련되어 있으므로, 독자가 이미 개념을 이해하고 있다면 대강 살펴보거나 생략해도 된다.
현대의 범용 컴퓨터 시스템은 공유 메모리에 대한 접근을 제공하는 공통 버스를 통해 연결된 여러 개의 장치 제어기와 하나 이상의 CPU로 구성되어 있다. 각 장치 제어기는 특정 장치(예를 들어, 디스크 드라이브, 오디오 장치, 비디오 디스플레이)를 관리한다. CPU와 장치 제어기는 메모리 사이클을 얻기 위해 경쟁하면서 병행 실행될 수 있다. 공유 메모리에 대한 질서 있는 접근을 보장하기 위해 메모리 제어기가 제공되며, 그 기능은 메모리 접근을 동기화시키는 일이다.
컴퓨터가 전원이 켜지거나 리부팅할 때 실행할 초기 프로그램(또는 부트스트랩 프로그램) 을 가지고 있어야 한다. 이 초기 프로그램은 매우 단순한 형태를 가지는 것이 일반적이다. 전형적으로 이것을 보통 펌웨어라고 알려져 있는 컴퓨터 내의 읽기 전용 메모리(ROM, Read-Only Memory)나 EEPROM에 저장된다.
ROM은 비휘발성 메모리이다. 고정 기억 장치 또는 롬은 반도체 기억 장치의 하나로 사람의 본능에 비유할수 있으며,
컴퓨터를 구동하기 위한 기본적인 정보가 담겨있다.
EFPROM 또한 비휘발성 메모리이며, EEPROM은 모뎀이나 비디오 카드, 메인보드, SCSI 컨트롤러 등에서 사용된다.
모뎀의 경우에는 내부에 사용자가 AT명령을 통해서 설정한 상태가 전원을 껐다 켠 후에도 그대로 유지되는데.
이것은 EEPROM에서 그 상태를 저장해 놓기 때문이다. 대부분의 비디오카드, 메인보드, SCSI 컨트롤러 등에서
EEPROM을 사용해 점퍼없이 설정상태를 저장한다.
이 초기 프로그램(또는 부트스트랩 프로그램)은 CPU 레지스터로부터 장치 제어기, 메모리 내용등을 포함한 시스템의 모든 면을 초기화한다. 또한 부트스트랩 프로그램은 운영체제를 적재하는 방법 및 실행을 시작하는 방법을 알아야 한다. 이러한 목적을 달성하기 위해서 부트스트랩 프로그램은 운영체제의 커널을 찾아 메모리에 적재해야 한다.
그런 다음 운영체제는 "init"과 같은 첫 번째 프로세스를 실행하고, 어떤 이벤트가 발생하기를 기다린다.
이벤트가 발생하면 하드웨어나 또는 소프트웨어로부터 발생한 인터럽트(interrupt)에 의해 신호가 보내어진다. 하드웨어는 어느 순간이든 시스템 버스를 통해 CPU에 신호를 보내 인터럽트를 발생시킬 수 있다. 소프트웨어는 시스템 호출이라 불리는 특별한 연산을 실행하여 인터럽트를 발생시킬 수 있다.
CPU가 인터럽트되면, CPU는 하던 일을 중단하고, 즉시 고정된 위치로 실행을 옮긴다. 이러한 고정된 위치는 일반적으로 인터럽트를 위한 서비스 루틴이 위치한 시작 주소를 가지고 있다. 그리고 인터럽트 서비스 루틴이 실행된다. 인터럽트 서비스 루틴의 실행이 완료되면, CPU는 인터럽트 되었던 연산을 재개한다.
인터럽트는 컴퓨터 구조의 중요한 부분이다. 각 컴퓨터 설계는 자신의 인터럽트 메커니즘을 가지고 있으며, 몇 가지 기능은 공통적이다. 인터럽트는 적절한 서비스 루틴으로 제어를 전달한다. 이러한 전달을 처리하는 간단한 방법은 인터럽트 정보를 조사하는 일반적인 루틴을 호출하는 방법이다. 이 루틴은 이어서 인터럽트 고유의 핸들러(handler)를 호출한다.
그러나 인터럽트는 매우 빠르게 처리되어야 하고, 사용 가능한 인터럽트의 수가 미리 정의되어 있으므로 대신 인터럽트 루틴에 대한 포인터들의 테이블을 이용할 수 있다. 이 경우 중간 루틴을 둘 필요 없이 테이블을 통하여 간접적으로 인터럽트 루틴이 호출될 수 있다. 일반적으로 포인터들의 테이블은 하위 메모리에 저장된다.
인터럽트가 요청되면, 이들 위치에는 여러 장치에 대한 인터럽트 서비스 루틴의 주소가 들어 있다. 인터럽트를 유발한 장치를 위한 인터럽트 서비스 루틴의 주소를 제공하기 위해 이 주소의 배열 즉, 인터럽트 벡터가 인터럽트 요청과 함께 주어진 고유의 유일한 장치 번호로 색인된다.
또한, 인터럽트 구조는 인터럽트된 명령의 주소를 반드시 저장해야 한다. 과거의 많은 설계는 고정된 위치에 인터럽트 주소를 저장하거나 장치 번호에 의해 색인되는 위치에 저장하였지만, 최근의 구조들은 시스템 스택에 복귀 주소를 저장한다. 만약 인터럽트 루틴이 처리기의 상태를 변경할 필요(예를 들어, 레지스터의 값이 변경되어)가 있다면, 인터럽트 루틴은 반드시 명시적으로 현재의 상태를 저장하여야 하며, 복귀하기 전에 상태를 복원해야 한다.
인터럽트를 서비스 한 후 저장되어 있던 복귀 주소를 프로그램 카운터에 적재하고, 인터럽트에 의해 중단되었던 연산이 인터럽트가 발생되지 않았던 것처럼 다시 시작한다.
초기 프로그램 실행
-> 이벤트 발생
-> 하드웨어나 소프트웨어로 부터의 interrupt에 의해 신호가 보내짐
-> CPU가 인터럽트 됨
-> 고정된 위치로 이동 및 처리
-> 처리 끝
-> CPU 다시 하던일 하러감
CPU는 오직 메모리로부터 명령을 적재할 수 있으며, 모두 메모리에 저장되어야 한다. 범용 컴퓨터는 대부분의 프로그램을 읽기-쓰기 가능한 메모리인 주 메모리(Random-Access Memory, RAM)에서 실행시킨다. 또한, 컴퓨터는 다른 형태의 메모리로 Read-Only memory(ROM)을 사용하는데 ROM은 갱신될 수 없기 때문에 오직 정적인 프로그램만이 저장된다. (예를 들어, 스마트폰은 공장 설치 프로그램을 저장하는 데 EEPROM을 사용한다.)
모든 형태의 메모리는 워드의 배열을 제공하며, 각 워드는 고유의 주소를 가지고 있다. 상호작용은 특정 메모리 주소들에 대한 일련의 적재(load) 또는 저장(store) 명령을 통하여 이루어진다. 적재 명령은 주 메모리로부터 CPU 내부의 레지스터로 한 워드를 옮기는 것이다. 반대로 저장 명령은 레지스터의 내용을 주 메모리로 옮긴다. 명시적인 적재, 저장 명령 외에 CPU는 실행을 위해 자동적으로 주 메모리로부터 명령을 적재한다.
적재 명령 : RAM -> CPU
저장 명령 : CPU -> RAM
폰 노이만 구조 시스템에서 실행되는 전형적인 명령-실행 사이클은 먼저 메모리로부터 명령을 인출해 그 명령을 명령 레지스터(instruction register)에 저장한다. 이어서 명령을 해독하고, 명령은 메모리로부터 피연산자를 인출하여 내부 레지스터에 저장하도록 유발할 수 있다. 피연산자에 대해 명령을 실행한 후에 결과가 메모리에 다시 저장될 수 있다.
메모리 장치는 단지 연속적인 메모리 주소만을 인식한다는 사실에 유의하라.
메모리는 이들 주소(명령 카운터(instruction counter), 색인(indexing), 간접 주소(indirection), 리터럴 주소(literal addresses) 등)가 어떻게 생성되었는지 알지 못하며, 관심도 없다. (명령인지 데이터인지 알지 못한다.) 그러므로 우리는 프로그램이 어떻게 생겼는지는 무시하고 단지 실행 중인 프로그램에 의해 생성된 일련의 메모리 주소에만 흥미가 있다.
이상적으로는, 프로그램과 데이터가 주 메모리에 영구히 존재하기를 원한다. 그러나 이는 다음 두 가지 이유 때문에 불가능하다.
1. 주 메모리는 모든 필요한 프로그램과 데이터를 영구히 저장하기에는 너무 작다.
2. 주 메모리는 전원이 공급되지 않으면 그 내용을 잃어버리는 휘발성 저장장치이다.
그러므로 대부분의 컴퓨터 시스템은 주 메모리의 확장으로 보조 저장장치를 제공한다. 보조 저장장치의 주요 요건은 대량의 데이터를 영구히 보존할 수 있어야 한다는 것이다. 가장 일반적인 보조 저장장치는 프로그램과 데이터 모두를 저장할 수 있는 자기디스크(하드디스크)이다. 대부분의 프로그램은 메모리에 적재될 때까지 디스크에 저장된다.
여러 저장시스템은 휘발성이거나 비휘발성이다. 휘발성(volatile) 저장장치는 전원이 제거되면 저장하고 있는 내용을 잃게 된다. 값비싼 배터리와 발전기 예비(backup)시스템이 없다면, 데이터를 보관하기위해 반드시 비휘발성 저장장치를 사용해야 한다. 밑에 그림에 보인 계층에서 전자디스크 이상의 저장 시스템은 휘발성이며, 반면에 전자디스크 아래의 장치들은 비휘발성이다.
하드디스크 아래로 광 디스크나 자기 테이프가 있다고 볼 수 있는데 이는 과거에 많이 사용했던
CD나 테이프같은 것들이므로 생략된 그림으로 가져왔습니다.
저장장치는 컴퓨터 내의 여러 형태의 입출력장치 중 하나이다. 시스템의 신뢰성과 성능에 미치는 중요성 그리고 장치들의 다양한 특징 때문에 운영체제 코드의 많은 부분들이 입출력을 관리하는 데 할애된다. 다음으로 입출력에 대한 전체적인 모습을 살펴보겠다.
범용 컴퓨터 시스템은 공통 버스에 의해 연결된 여러 개의 장치 제어기와 CPU들로 구성되어있다. 각 장치 제어기가 특정 타입의 장치를 담당한다. 장치 제어기는 약간의 로컬 버퍼 저장장치와 특수 목적용 레지스터 집합을 유지한다. 장치제어기는 자신이 제어하는 주변장치와 자신의 로컬 버퍼 저장장치 사이의 데이터 전송을 담당한다.
통상적으로 운영체제는 각 장치 제어기마다 디바이스 드라이버를 가지고 있다. 이 디바이스 드라이버는 장치 제어기의 동작을 이해하고 운영체제의 다른 부분들에게 장치에 대한 일관된 인터페이스를 제공한다.
입출력 연산을 시작하기 위해 디바이스 드라이버는 장치 제어기의 적절한 레지스터에 필요한 값을 적재한다. 장치 제어기는 이어 취할 동작(예를들어, "키보드에서 한 문자를 읽어라")을 결정하기 위해 이들 레지스터의 내용을 조사한다. 제어기는 장치로부터 자신의 로컬 버퍼로 데이터 전송을 시작한다.
일단 데이터 전송이 완료되면, 장치 제어기는 자신이 연산을 완료했음을 인터럽트를 이용하여 디바이스 드라이버에게 통보한다. 그러면 디바이스 드라이버는 제어를 운영체제에게 반환하고 이때 입력 완료인 경우에는 데이터 또는 데이터에 대한 포인터를 같이 반환할 수 있다. 다른 동작에 대해서는 디바이스 드라이버는 상태 정보를 반환한다.
이 인터럽트 구동 방식은 입출력은 적은 양의 데이터를 전송하는 데에는 문제가 없으나 디스크 입출력과 같은 대량의 데이터를 전송하는 데에는 높은 오버헤드를 초래한다.
이 문제를 해결하기 위해서 직접 메모리 접근(Direct Memory Access, DMA) 장치가 사용된다. 장치에 대한 버퍼 및 포인터, 입출력 카운트를 세팅한 후 장치 제어기는 CPU의 개입 없이 메모리로부터 자신의 버퍼 장치로 또는 버퍼로부터 메모리로 데이터 블록 전체를 전송한다. 속도가 느린 장치처럼 한 바이트마다 인터럽트가 발생하는 것이 아니라 블록 전송이 완료될 때마다 인터럽트가 발생한다. 장치 제어기가 전송 작업을 실행하고 있는 동안 CPU는 다른 작업을 실행할 수 있어서 효율성이 높아진다.
디스크 드라이브 컨트롤러, 그래픽 카드, 네트워크 카드, 사운드 카드를 포함한 많은 하드웨어 시스템이 DMA를 사용한다. DMA는 멀티 코어 프로세서의 칩 내부 데이터 전송에도 쓰인다. DMA는 메모리 간 복사 또는 데이터 이동에도 쓰일 수 있다.
DMA가 없을 때
DMA가 있을 때