본문 바로가기

[운영체제] 02-1. Computer Systems Architecture

 

출처 서울대학교 홍성수 교수님, [K-MOOC] 운영체제의 기초, 2주차 Review of Computer Hardware
2주차 강의 목차
2-1. 컴퓨터 시스템 구조(하드웨어)
컴퓨터 시스템을 구성하는 컴포넌트를 연결해주는 system bus의 관점에서 하드웨어를 다룬다.
2-2. Interrupt Mechanism
CPU와 I/O 디바이스의 수행을 병렬화하기 위해 컴퓨터 시스템에 추가된 I/O Controller circuit, 즉 I/O Controller는 Interrupt Mechanism을 사용하여 부여받은 I/O 미션의 수행 결과를 CPU에게 알려준다.
Interrupt Mechanism은 I/O processing에서만 중요한 것이 아니라 운영체제가 동작하는 데 있어서 필수 불가결한 메커니즘이다. 운영체제를 Interrupt driven software라고 해도 될 정도이다. 
2-3. Hardware Protection
운영체제의 관점에서 볼 때, 메모리 그리고 CPU와 I/O 디바이스를 보호해야 한다.  

2-1. 컴퓨터 시스템 구조 Computer Systems Architecture

우리가 현재 사용하는 컴퓨터는 거의 예외 없이 폰 노이먼 머신이라는 구조를 가지고 있다. 이 구조는 세 가지 요소-CPU, 메모리, 입출력 장치-를 요구한다. 

* 존 폰 노이먼은 현대 컴퓨터 시스템의 개념적인 모델을 가장 처음 정리해서 알린 사람으로 알려져 있다. 앨런 튜닝이라는 컴퓨터 과학자도 언급되곤 한다.

폰 노이만 머신의 특징

폰 노이만 머신의 특징은 Stored program concept을 구현하고 있다는 점이다.

과거 주판의 경우를 생각해보면 주판은 데이터만 가지고 있고, 계산의 알고리즘은 사람의 머릿속에 있었다. 그러나 Stored program concept에서는 계산기가 데이터뿐만 아니라 이 데이터를 조작하는 코드까지 포함한다. 사람이 개입하지 않고 계산기에게 계산하라는 명령만 주면 계산기가 자동적으로 계산을 하는 것이다.

폰 노이만 머신의 구조

폰 노이만 머신에는 반드시 메모리가 필요하다. 이 메모리에는 데이터가 있고, 코드가 있다. 

*코드는 Instruction의 sequence이다. 

한편 메모리에 저장된 instruction들을 알고리즘의 순서에 맞게 시퀀싱해서 하나씩 가져다가 수행시켜주는 장치가 필요한데 이것이 바로 CPU이다. 그리고 메모리로부터 instruction과 데이터를 가져오고 가끔은 데이터를 메모리에 write하기 위해서 data path가 필요하다. 입출력 장치도 당연히 필요하다.

 

CPU

CPU는 온갖 연산과 제어 동작을 한다. CPU에는 세부적으로 어떤 하드웨어 circuit이 들어 있을까?

  • Arithmatic and logic unit(ALU)
    산술 연산과 로직 연산을 할 수 있는 circuit. 두 가지는 보통 한 개로 묶여 있다.
  • Register
    : CPU 안에 있는 작은 저장 공간.
    • 데이터 레지스터: 메인 메모리에서 가져온 데이터를 가져온 값을 저장하거나, 중간 계산 결과값을 을 저장하거나,  메인 메모리로 write할 데이터를 저장하는 레지스터.
    • Address 레지스터: 수행해야 할 instruction 또는 읽어 오거나 write할 데이터의 주소를 담고 있는 레지스터.
    • Instruction 레지스터: 메인 메모리에서 instruction을 가져오면, 그 instruction을 처리하기 위해 잠시 담아두는 레지스터.
    • 프로그램 카운터 레지스터: 메인 메모리에 있는 instruction을 알고리즘이 지정한 순서대로 수행시키려면 다음 instruction이 무엇인지를 sequencing해주는 circuit이 필요하다. 이런 circuit이 프로그램 카운터 레지스터이다. 다음에 수행해야 할 instruction의 주소를 담고 있다. 
폰 노이만 머신에서 다음 instruction을 sequencing하는 방법
1. OS가 메인 메모리에 프로그램을 적재한다.
2. OS는 그 프로그램 첫 번째 instruction의 시작 주소를 알고 있다. 그 주소를 프로그램 카운터에 넣어 놓는다.
3. 그러면 CPU가 initiation하면 프로그램 카운터에 있는 주소를 가지고 instruction 하나를 fetch한다.
4. 그러면 프로그램 레지스터는 다음 instruction 주소를 시퀀싱하기 위해서 다음 instruction의 주소를 담아 놓는다. 만약 프로그램에 브랜치가 있다면 브랜치 타겟에 그 다음 instruction의 주소가 담겨 있기 때문에 그 값을 담는다.

메모리

메모리에는 그 프로그램이 수행하는 데 필요한 코드들이 저장된다.

*Instruction: CPU가 수행을 하는 기본 단위.

*코드: Instruction의 순서.

Data path

CPU, 메모리, I/O 디바이스를 서로 연결해주는 것을 흔히 Data Path라고 한다. 이 Path를 따라 데이터가 흘러다니기 때문이다. 이때의 데이터의 단위는 word이다.

*word는 컴퓨터 하드웨어가 주고받는 데이터의 유닛이다. CPU에서 일어나는 연산의 단위이다.

데이터 단위
bit
(binary digit)
컴퓨터가 표현할 수 있는 가장 작은 단위, 이진수의 한 digit을 의미한다.
byte
컴퓨터가 본격적으로 저장하는 데이터의 가장 작은 단위는 bit가 8개 모인 byte이다. 컴퓨터의 메인 메모리를 놓고 볼 때, 메인 메모리의 가장 element unit은 바이트이다. 메인 메모리에서 주소가 부여되는 단위가 바로 1바이트이다.
word
word는 변동하는 단위이다.
- 7~80년대 PC) 1word =  1byte
- 인텔이 마이크로 프로세서를 개발해 PC를 처음 만들었을 때) 1word = 2byte
- 그 이후) 1word =  4바이트
- 요즘) 1word = 8바이트(64bit)
word란 무엇이 결정하는가? 그 컴퓨터의 CPU를 설계하는 사람이 결정한다. 이 word에 맞게 다른 하드웨어 로직들이 함께 디자인된다. word 정수나 실수를 표현하는 수단이 된다.

 

System Bus의 3가지 sub bus

 

Data path를 컴퓨터 아키텍처에서 system bus라고 한다. System bus는 무슨 역할을 하는가?

폰 노이만 머신에서는 데이터와 instruction이 모두 메인 메모리에 저장되어 있다. 그래서 CPU가 어떤 프로그램을 수행시키려면 제일 먼저 메인 메모리에 저장되어 있는 instruction 하나를 fetch해 와야 한다.

instruction이 메인 메모리에서 CPU로 전달이 되려면 data path, 즉 system bus가 필요하게 된다.

system bus는 내부가 3개의 sub bus로 나누어져 있다. 

  1. Address Bus: CPU가 메모리에서 읽어오거나 메모리에 write하고자 하는 데이터 또는 instruction의 주소가 전달되는 경로. CPU로부터 메모리로 향하는 단방향 버스. 
  2. Control Bus: CPU가 메모리에게 instruction 하나를 fetch해오겠다는 명령을 보내는 경로.
  3. Data Bus: CPU의 명령에 따라 메모리가 데이터 또는 instruction을 CPU에게 보내는 경로.

Bus transaction

Bus transaction은 CPU가 메모리에서 데이터를 읽거나 메모리를 write하는 동작을 말한다. 두 개의 트랜잭션이 존재한다.

  • load, read: CPU가 메모리에서 데이터를 읽어오는 동작.
  • store, write: CPU가 레지스터에 있는 데이터를 메모리로 보내는 동작.

* read&write 트랜잭션: read&write을 하나의 트랜잭션으로 만드는 일은 일반적으로 하지 않는다. 그런데 multi process synchronization에서는 하드웨어적으로 read&write 트랜잭션을 만들어서 사용하기도 한다. 

 

시나리오

 

CPU가 메모리로부터 데이터를 load하거나 메모리에 데이터를 store하는 연산을 수행해야 하는 경우

 

1. CPU가 Address Bus를 통해 메모리에게 자신이 읽거나 write할 데이터 주소를 보낸다.

2. 그 다음 CPU가 Control Bus를 통해 메모리에게 자신이 할 동작이 load인지 store인지 알려준다.

3. 메모리는 그에 대한 대응으로 Data Bus를 통해 데이터를 받거나 보낸다.

 

Bus Arbitration
Bus를 주체적으로 사용하는 것은 CPU이다. CPU가 bus transcation을 initiation을 하고 control bus를 통해 명령을 하달하면 그것에 따라 메모리가 동작하는 것이다. 그런데 CPU가 4개, 8개 있을 경우, bus를 주체적으로 사용할 수 있는 entity가 여러 개 된다. 이렇게 여러 CPU들이 동시에 bus를 장악해서 사용한다면 bus에 여러 cpu의 시그널들이 동시에 전송되니 시그널들이 gobble되어서 값이 엉터리가 될 것이다.
따라서 system bus는 어느 한 시점에 하나의 entity만 exclusive하게 사용해야 한다. 이러한 조정을 해주는 기법을 Bus Arbitration, 버스 조정이라고 한다. 그 방법으로는 여러 가지가 있는데 여기서는 Centralized Bus Arbitration만 소개하겠다.

Centralized Bus Arbitration
Centralized Bus Arbitration 방식에서는 세 개의 컴포넌트 외에 별도의 circuit인 bus arbiter가 system bus에 붙어 있다.
1. 컴포넌트가 bus 사용을 원하면 bus arbiter에게 시그널을 보낸다. 이러한 시그널을 bus request signal이라고 한다.
2. bus arbiter는 request 중 하나만 선택해서 하나의 컴포넌트에게만 bus를 허용한다. bus grant 시그널을 보내게 되는 것이다.
3. 그러다가 bus grant를 받은 CPU가 버스를 다 사용하고 나면 bus arbiter에게 bus release 신호를 보내게 된다.
Bus master와 Bus slave
CPU처럼 주체적으로 bus transaction을 initiate할 수 있는 컴포넌트를 특별히 bus mater라고 부른다.
메모리(메모리 컨트롤러)처럼 Bus master가 bus transaction을 initiate했을 때 그에 따라 response만 할 수 있는 entity를 bus slave라고 한다.

 

I/O 컨트롤러

 

각종 I/O 디바이스나 메모리 앞에 추가적인 circuit이 있다. 이들이 바로 IBM 엔지니어들이 개발했던 I/O 채널, 현대적인 말로 I/O 컨트롤러-circuit-disk controller, printer controller, tape-drive controller-이다. I/O 컨트롤러가 하는 역할은 CPU가 직접 I/O 디바이스를 통제하고 관리하지 않아도 되도록 CPU의 미션을 대신 수행하는 것이다. 그리고 그 수행이 끝나면 CPU로 interrupt를 보내주는 것이다.

*메모리 컨트롤러: 메모리 접근의 효율성을 하드웨어적으로 지원하는 circuit이다. 옛날에는 메모리가 단순했으나 요즘에는 메모리 기술이 많이 발전했다. 멀티뱅크로 메모리를 구성하고 메모리를 접근할 때 병렬성을 줘서 메모리 접근의 throughput을 높이는 기능을 제공한다.

 

레지스터

I/O 컨트롤러는 CPU와 I/O 디바이스 사이에 존재하는 circuit이다. 이 circuit 안에는 CPU처럼 레지스터들이 존재한다.

크게 두 가지 부류의 레지스터가 있다.

  1. 데이터 레지스터
    • output 데이터 레지스터: output을 할 때 CPU가 출력하길 원하는 데이터를 잠깐 저장해 놓는 곳. 
    • input 데이터 레지스터: 입력을 할 때 input 디바이스가 생성해놓은 데이터를 CPU에 전달하기 전에 잠시 저장해 놓는 곳.
  2. 컨트롤 레지스터
    • 컨트롤 레지스터: CPU가 부여한 명령을 기억하는 공간. 이 레지스터에는 지금 하고자 하는 연산이 input operation인지, output operation인지를 표현하는 명령어들이 저장되어 있게 된다.
    • status 레지스터: I/O 디바이스 또는 I/O 컨트롤러의 현재 상황을 표현하는 레지스터.
시나리오

OS의 통제를 받아서 CPU가 출력하는 사례

 

1. CPU는 출력하고자 하는 byte를 interconnect를 이용해서 I/O 컨트롤러에 있는 output 데이터 레지스터에 저장한다.

2. 그리고 CPU는 I/O 컨트롤러 레지스터에 출력 명령을 저장한다.

3. 이렇게 하면 I/O 컨트롤러는 delegation을 다 받은 것이다. 자체적으로 출력 연산을 수행한다. 출력 연산이 완료되면 CPU로 거꾸로 interrupt를 걸게 된다.

 

I/O 컨트롤러 레지스터의 주소를 구별하는 방식

I/O 컨트롤러 하나 안에는 여러 레지스터가 있다. 여러 레지스터들을 구별해주는 주소가 필요하다. 그 방법으로는 두 가지가 있다. 

 

1. Memory-mapped I/O

물리적 메모리가 사용하는 주소 공간에 ROM이나 RAM이 꼽혀 있지 않은, 즉 사용되지 않는 address들이 많이 존재할 수 있다. 그런 것들과 I/O 레지스터들을 서로 mapping을 시키는 기법이다.

그러면 CPU의 입장에서는 메모리 location의 데이터를 읽고 쓰는 것이나 I/O 레지스터를 읽고 쓰는 것이나 똑같은 형태의 주소를 사용하게 된다. 그리고 I/O 수행을 할 때 CPU는 특별히 다른 instruction을 사용할 필요 없이, 메모리의 데이터를 읽거나 쓸 때 사용하는 load/store instruction을 사용하면 I/O 연산을 사용할 수 있다.

이런 Memory-mapped I/O 기법은 주로 모토롤라 프로세서들, 옛날 프로세서에서 많이 제공되고 있다.

 

2. Port-mapped I/O

I/O 디바이스에서 Port는 I/O 컨트롤러 안에 있는 I/O 레지스터의 별칭이다. Port-mapped I/O는 물리적 메모리의 주소 공간과 완전히 별개의 I/O 주소 공간을 만들어서 별개의 주소를 가지고 I/O 레지스터들에게 addressing하는 방식이다.

예를 들어 32-bit word 머신이라면 address의 사이즈가 32bit라는 뜻인데, 그런 경우 메인 메모리가 가질 수 있는 메모리 공간은 2의 32제곱인 4기가 개의 주소가 존재하게 된다.

그런데 Port-mapped I/O인 경우에는, I/O 디바이스 레지스터의 개수가 그렇게 많지 않으니까 32bit까지 필요 없고 16bit 64K개 정도의 개별 주소를 가지고 다 커버할 수 있다. 16비트 별도 주소 공간을 부여해서 port 레지스터들의 주소를 부여할 수 있다.

Port-mapped I/O기법을 사용할 때에는, load/store 방식으로는 I/O 연산으로 수행할 수 없다. 반드시 port address를 받아들이는 별도의 I/O 연산이 필요하게 된다.

인텔 프로세서는 Port-mapped I/O를 제공하고 있다.

 

입출력 연산을 하는 방식

입출력 연산을 하는 방식을 크게 두 가지 종류로 나누어 볼 수 있다.

 

1. Polling I/O

CPU가 전적으로 I/O 수행을 전적으로 전담한다.  Polling I/O는 CPU가 스스로 클락 사이클을 사용하면서 I/O 수행을 끝까지 다 책임지는 종류이다. 이 방법은 낭비적 요소가 많다. CPU가 계속 클락 사이클을 쓰면서 아무 유용한 연산을 하지 못하고, 루프만 돌면서 똑같은 질문을 던지기 때문이다.

시나리오

 

1. CPU가 출력할 때, output 데이터 레지스터에 데이터를 저장한다.

2. 그 다음 I/O 컨트롤러에게 '내가 쓴 데이터가 출력됐니'라고 질문하는 루프를 돈다.

= status register의 값을 확인한다.

- I/O 컨트롤러의 대답이 YES라면 수행을 종료한다.

- I/O 컨트롤러의 대답이 NO라면 다시 루프를 돌아서 물어본다.

 

*status register의 한 비트는 CPU가 기록해 준 output 데이터 값이 여전히 남아있는지를 나타낸다. 이게 출력된다면 status register의 해당 비트가 바뀐다.

 

2. Interrupt-driven I/O

CPU는 I/O 컨트롤러에게 출력할 데이터와 원하는 수행이 출력 연산이라는 명령 보낸다. 그러고는 잊어버리고 다른 연산을 한다.

I/O 컨트롤러는 output 데이터를 출력하려고 노력하고, 출력이 성공적으로 완료되면 그때서야 interrupt를 통해서 CPU를 깨운다.

 

DMA (Direct Memory Access)

 

기본적으로 CPU가 메모리에 데이터를 저장하는 것이나, CPU가 I/O 컨트롤러의 레지스터에 값을 전달하는 것이나 비슷하다. 즉, I/O 컨트롤러는 bus의 관점에서 봤을 때 수동적인 개체이다. 그러나 CPU가 어떤 I/O operation을 I/O 컨트롤러에게 부여했는지에 따라서 답은 달라진다. DMA의 경우 I/O 컨트롤러가 Bus master가 된다.

 

DMA란?

주로 디스크 드라이브와 같은 볼륨 데이터를 입출력할 때 사용하는 기법이다. DMA란 간단하게 얘기해서 CPU를 경유하지 않고 I/O 컨트롤러가 메인 메모리에 직접 접근해서, 데이터를 모아서 블록으로 만든 다음에 통째로 출력하는 연산이다.

 

Polling I/O나 Interrupt-driven I/O는 바이트 단위의 데이터를 I/O 레지스터에 보내고 읽어들이고 하는 연산이었다.

그런데 입출력의 대상이 되는 데이터가 몇 바이트가 아니고 수 K, 수십 K에 해당하는 큰 데이터 블록이라고 한다면 이것을 바이트 단위로 전송한다는 일은 오버헤드가 큰 연산이 된다. I/O 연산 성능이 저하되고 그에 따라 CPU가 다른 연산을 수행할 수 없는 문제가 발생할 수 있다. 이럴 때 효율을 높이기 위해 고안된 방법이 DMA다.

 

예를 들어, CPU가 1K짜리 메모리 블록을 디스크로 output하는 연산이 있다고 해 보자.

CPU는 디스크 컨트롤러에게 출력할 데이터 블록의 시작 주소, 데이터 블록의 사이즈, 입출력 연산의 유형(input/output)을 전달해 준다. 그러면 I/O 컨트롤러인 디스크 컨트롤러는 직접 메모리를 엑세스해서 CPU를 경유하지 않고 바이트 단위로 데이터를 얻어온 다음에 블록으로 만들어서 디스크 드라이브로 출력하는 연산을 할 수가 있다.

 

CPU가 DMA를 initiation하면, 디스크 컨트롤러는 bus master가 된다. 이와 같이 폰 노인만 머신에서 시스템 버스에는 여러 개의 다수의 bust master와 bus slave가 연결되어 있다. 그렇기 때문에 bus arbitration 동작이 중요한 기능이 된다.