brunch

You can make anything
by writing

C.S.Lewis

by 정주홍 Aug 16. 2017

개념 정리 - (6) 컴퓨터 구조 편

우리가 배운 개념이 어디서 어떻게 쓰이는지 알아보자

프로세서, 특히 CPU에서 실행되는 것은 그 CPU에 정의된 일련의 명령어 집합이다. 고수준 언어로 작성된 프로그램은 컴파일되어 목적하는 CPU 아키텍처 상의 명령어 집합으로 만들어진 프로그램을 만들도록 되어 있다. 따라서, CPU가 명령어를 어떻게 읽어 들여 실행하는지 알아보는 것이 의미가 있을 것이다. 이 글에서는 RISC(Reduced Instruction Set Computer, MIPS 등의 아키텍처에서 사용됨) 방식의 CPU 아키텍처와 폰 노이만 구조를 기반으로 설명하겠다. 워드는 32bit로 가정한다. 아래 사진은 추상적으로 나타낸 MIPS 구조이다.

출처 : 김병기 교수님 강의 노트



개요

폰 노이만 구조에서 명령어 메모리와 데이터 메모리는 나뉘어 있지 않다. 따라서 CPU는 메모리에서 PC(Program Counter)가 가리키는 주소의 워드를 읽어와서 정해진 규칙대로 명령어를 디코딩한 뒤 op 코드를 확인하여 명령어 별로 다르게 실행한다. 명령어에서 특정 주소의 워드를 레지스터에 로드하겠다거나(Load Word), 레지스터의 값을 메모리에 저장하겠다는(Store Word) 것이 있을 뿐이다. 소스 코드를 컴파일하여 만들어지는 목적 코드에는 정해진 규칙대로 코드 영역, 데이터 영역이 있고 OS에서 프로그램을 실행해 줄 때 엔트리 함수(C언어에서는 main 함수)의 시작 위치를 PC로 설정해주기만 하면 순차적으로 실행되는 것이다. (이는 간략하게 설명을 위해 세부 내용을 뺀 설명임을 밝힌다.)

대략적인 명령어 실행 순서

CPU는 항상 PC에 위치한 명령어를 실행하도록 되어 있으므로 분기 처리는 PC를 바꿔주는 명령어인 셈이다. 브랜치나 점프 명령어가 이에 해당한다. 하지만 한 워드에서 이미 op 코드를 위해 6bit를 사용했기 때문에 점프 명령일지라도 32bit 전체에 대한 주소를 설정할 수가 없다. 심지어 어떤 브랜치 명령어들은 레지스터 번호 2개를 포함하기 때문에 두 레지스터의 값이 같거나 틀리면 브랜치 하도록 하는 명령어인 beq, bne 명령어는 16bit만을 주소로 가질 수 있다. 이 때문에 절대 주소 지정, 상대 주소 지정 모드가 있다. 또한, 항상 명령어는 4byte(32bit) 단위임이 약속되어 있기 때문에 주어진 주소에 4를 곱한 값을 사용함으로써 bit 2개를 절약한다.

시간당 명령어 처리량을 더 올리기 위한 방법으로 파이프라이닝(Pipelining) 기법을 사용한다. 한 명령어 실행이 완전히 끝날 때까지 기다리지 않고 명령어 실행 단계를 나누어 단계별로 명령어를 실행하는 것으로 여러 명령어를 동시에 실행하는 방법이다. 단, 파이프라이닝을 하면서 발생하는 구조적 해저드, 데이터 해저드, 컨트롤 해저드를 해결해야 한다. 

메모리는 보조 기억 장치에 비해 훨씬 빠르게 접근 가능하지만 CPU 입장에서는 메모리를 접근하는 시간도 너무 크다. 게다가 명령어 같이 매번 접근해야 하는 데이터가 메모리에 있다는 것은 성능에 큰 저하를 일으킨다. 그렇기 때문에 CPU 내부에는 캐시(Cache)가 존재하여 메모리에 접근하는 횟수를 줄인다. 레지스터, 캐시, 메모리, 보조 기억 장치 순으로 접근 속도가 지수적으로 느려진다는 이야기를 들어봤을 것이다. 

실제로는 물리 메모리 주소로 바로 접근하지 않고 가상 메모리(Virtual Memory) 기법을 이용하여 논리 주소를 기반으로 메모리에 접근한다. 논리 주소와 물리 주소를 맵핑하는 것을 소프트웨어 수준에서 처리하는 것은 큰 성능 문제를 일으키므로, 하드웨어 지원을 통해 빠르게 처리할 수 있도록 해야 한다. 빠르게 페이지를 찾기 위해 변환 색인 버퍼(Translation Lookaside Buffer, TLB)라는 별도의 메모리를 둔다. 실제로는 MMU라는 메모리 관리 장치를 별도로 둔다고 하지만, 여기서는 간략하게 TLB 개념을 소개하는 차원에서 TLB만을 설명한다.



명령어 집합

명령어는 크게 R-type, I-type, J-type으로 나뉜다. R-type에는 보통 2개 레지스터에 대한 연산을 1개 레지스터에 저장하는 명령어들로 예를 들어 add 명령어와 같은 것으로 구성되어 있다. I-type은 두 개의 레지스터 번호와 상수를 담고 있어 어떤 메모리 주소의 값을 레지스터로 불러오거나 저장하는 lw, sw 명령어, 브랜치 명령어 들로 구성되어 있다. 마지막으로 J-type은 점프 명령어들로 더 많은 비트를 주소로 사용할 수 있다.

각 명령어들은 6bit의 op 코드로 이루어져 있다. 명령어를 가져온다는 것은 어떤 주소로부터 32bit(한 워드)를 읽어 들여 해석하는 것과 같다. 이 작업을 디코딩 작업이라고 한다. R-type을 예로 들면 워드의 가장 왼쪽 6bit를 op 코드로, 그다음 5bit를 source register로, 그다음 5bit를 target register으로, 그다음 5bit를 destination register으로, 그다음 5bit와 6bit를 shift amount, function code로 인식하는 것이다. 이것을 32bit 숫자 표현인 것으로 인식할 수도 있으나 디코딩하면서 의미를 분석함으로써 명령어로 인식할 수 있는 것이다. 아래 사진은 명령어 타입별 인코딩 규칙이다.

출처 : https://www.cise.ufl.edu/~mssz/CompOrg/CDA-lang.html

각 레지스터 지정을 위해 5bit를 쓴 것을 보면 알 수 있듯이 레지스터는 5bit로 표현 가능한 총 32개가 존재한다. MIPS 아키텍처에서 어떤 레지스터가 어떻게 쓰이는지는 MIPS Calling Convention을 참고. 그렇기 때문에 어셈블러가 어셈블리어를 목적 코드로 변환할 때 목적 코드가 실행될 CPU의 아키텍처에 정의된 명령어 집합과 인코딩 방식을 참고하여 값을 인코딩해야만 한다.



주소 지정 모드

MIPS에는 대략 5가지(강의 자료에 따라 조금씩 차이가 있다)의 주소 지정 모드가 존재한다. 크게 절대 주소와 상대 주소, 값 기반 상대 주소로 나눌 수 있을 것 같다. 위에서 명령어가 어떻게 인코딩 되어 있는지 보면 알겠지만 주소 표현을 위해 사용할 수 있는 bit가 충분히 많지 않기 때문에 주소 지정 모드가 있는 것이다. 16bit를 주소로 표현할 수 있다고 해도 262144(2의 18승)까지의 값 밖에 표현할 수 없다. 크기가 1MB 밖에 안 되는 정수 배열도 그 배열 전체를 주소로 지정할 수 없게 되는 것이다. 그 한계를 극복하기 위해 레지스터에 있는 값에 상대적인 주소를 지정하는 등의 여러 가지 주소 지정 모드를 제공한다.

각 주소 지정 모드의 자세한 작동 방식은 다음 문서를 참고. 



데이터 경로 설계(Data Path Design)

메모리부터 명령어를 가져오고, 명령어를 디코딩하고, 산술 논리 장치로 연산을 하고, 메모리로부터 값을 읽어 오거나 쓰는 작업 뒤에 다시 레지스터에 그 값을 쓰는 작업 등을 위해서는 회로를 설계해야 한다. 각 작업을 수행하는 물리적인 장치에는 전기 신호를 보내서 어떤 작업을 수행해야 하는지도 알려줘야 한다. 이러한 회로 설계 작업을 데이터 경로 설계라고 한다.

예를 들어, PC는 다음에 실행할 명령어 주소를 뜻하므로 한 번 명령어를 실행할 때마다 4를 더해준 값을 다음 PC로 설정한다. 만약 명령어를 디코딩한 결과 명령어가 브랜치 명령어이고 조건을 만족하여 PC를 변경해야 될 수도 있다. 이때, 어떤 값을 PC로 설정하게 할 것인지를 컨트롤 장치(Control unit)를 통해 Mux(Multiplexer)로 지정할 수 있다. 

아래 사진은 데이터 경로 설계를 어느 정도 마친 결과이다. RegDst 신호를 설정하지 않으면 계산 결과를 레지스터에 쓰지 않으며, MemWrite 신호를 설정하지 않으면 메모리에 쓰지 않게 된다. 즉, 조건을 만족하지 않을 때 어떤 작업을 수행하지 않아야 한다면 컨트롤 신호만 설정하지 않으면 된다는 말이 된다. 특히, 쓰기 작업만 하지 않으면 실제 데이터에 수정이 일어나지 않게 된다.

출처 : 김병기 교수님 강의 노트

데이터 경로 설계에 대한 더 자세한 내용은 다음 를 참고.



파이프라이닝(Pipelining)

빨래를 한다고 생각해보자. 세탁기를 돌리고, 건조기를 돌리고, 다 말려진 빨래를 개고, 옷장에 정리하는 네 단계로 나눌 수 있다. 빨래를 여러 번 해야 할 때 이전의 빨래를 세탁한 뒤 옷장에 정리할 때까지 기다린 뒤에야 다음 빨래를 세탁하도록 한다면 너무 비효율적일 것이다. 그보다는 이전의 빨랫감을 건조기로 옮겼을 때 새로운 빨래를 세탁기로 돌리도록 하는 것이 같은 시간 내에 더 많은 빨래를 할 수 있다.

명령어 실행도 비슷하다. 명령어를 가져와서 디코딩하고 실행한 뒤 메모리 접근을 끝낼 때까지 기다린 뒤 레지스터에 값을 쓰고 나서야 다음 명령어를 실행하기보다, 앞의 명령어를 실행하는 동안 다음 명령어를 디코딩한다면 같은 시간 동안 더 많은 명령어를 실행할 수 있다. 이렇게 명령어를 여러 단계로 나누어 동시에 실행하는 기법을 파이프라이닝 기법이라고 한다. 

명령어 실행은 IF(Instruction Fetch), ID(Instruction Decode), EX(Execution), MEM(Memory), WB(Write Back) 5단계로 나뉜다. IF 단계에서는 명령어를 가져오고, ID 단계에서 명령어를 디코딩하고, EX 단계에서 명령어를 실행하고, MEM 단계에서 메모리에 접근하고, WB 단계에서 ALU 결과 혹은 메모리 접근 결괏값을 레지스터에 쓴다. ID 단계에 있던 명령어가 EX로 이동하고 나면 다음 명령어가 디코딩되므로 쓰기 대상인 레지스터 번호가 소실된다. 이 문제를 해결하기 위해 각 단계 사이에 파이프라인 레지스터를 둬서 값을 저장해둘 수 있도록 한다. 저장하는 값에는 컨트롤 장치의 신호 값도 포함된다.

파이프라이닝 기법은 명령어 실행량을 늘릴 수 있는 장점이 있지만 3가지 해저드 때문에 정상적으로 작동할 수 없다. 구조적 해저드, 데이터 해저드, 컨트롤 해저드가 그 3가지 해저드인데, 이 해저드를 해결하기 위한 몇 가지 추가적인 장치와 기법이 필요하다. 아래 항목에서 해저드를 더 자세히 다루겠다.



해저드(Hazard)

해저드는 구조적 문제 혹은 명령어 간 의존성에 의해서 파이프라이닝 하여 실행하는 도중 잘못된 실행이 될 수 있는 것을 말한다. 각 해저드를 간략하게 설명하면 다음과 같다.

구조적 해저드 : 장치를 함께 사용하면서 발생하는 해저드. lw 명령어가 메모리에 접근하고 있는 상태에서 다른 명령어를 메모리에서 가져오려 한다면 둘이 충돌된다.

데이터 해저드 : 다음 명령어가 이전 명령어의 쓰기 대상 레지스터를 참조하는 경우 이전 명령어가 WB 단계를 거쳐 레지스터 값을 수정하지 않으면 수정되기 전의 값을 참조하게 된다.

컨트롤 해저드 : 브랜치 명령어로 인해서 다른 명령어를 실행하게 된다면 다른 주소의 명령어를 실행하기 전에 브랜치 명령어의 다음 명령어가 이미 디코딩 단계에 와있으므로 잘못된 실행을 하게 된다.

구조적 해저드는 폰 노이만 아키텍처 상으로 어쩔 수 없이 발생하는 문제이다. 해결 방법은 하버드 아키텍처를 적용하거나 lw 명령어 다음에 bubble을 추가하는 것으로 stall 하여 다음 명령어 실행을 지연시키는 방법이 있다.

데이터 해저드는 변경된 레지스터 값을 사용해야 한다는 점에서 발생하는 문제이다. 해결 방법은 구조적 해저드처럼 stall 하거나 명령어 순서를 잘 조절하여 해저드가 일어나지 않게 하거나, 포워딩(forwarding)하는 것이다. 명령어 순서를 조절하는 것은 컴파일러나 어셈블러 수준에서 처리해줄 수 있을 것이고, 현실적으로 stall 시킴으로써 성능을 저하시키는 것보다 포워딩이라는 우아한 해결방법이 있기 때문에 포워딩이 중요하다. 포워딩은 ALU 출력 결과를 가져오는 회로를 추가하여 다음 명령어에서 읽기 대상인 레지스터를 이전 명령어가 쓰기 하려고 할 때 이전 명령어의 출력 결과를 선택하게끔 하는 방법이다.

컨트롤 해저드는 브랜치 명령어로 인해 실제로 브랜치를 하게 될지, 하지 않게 될지 알 수 없다는 점에서 가장 고통스러운 해저드다. 속 편한 방법은 모든 브랜치 명령어 다음에 한 번 stall 하는 것이다. 통계에 따르면 브랜치 명령어는 평균적으로 17%의 비율로 존재하고 stall 하는 경우 한 번의 추가적인 cycle이 필요하므로 평균 CPI가 1.17로 늘어나게 된다. 당연히 이런 해결 방법은 너무 성능 저하가 커서 말이 안 된다. 더 우아한 해결 방법으로는 브랜치가 일어날지 예측하는 방법이다. 브랜치가 일어났을 때만 stall 하고자 하는 것이 목표다. 컴파일러가 휴리스틱 혹은 프로파일링을 통해 미리 목적 코드 상에 브랜치가 일어날 것인지에 따른 결과를 적용해두는 정적 예측과 브랜치 예측 버퍼(또는 브랜치 역사 테이블, Branch History Table, BHT)를 이용한 동적 예측 방법이 있다. BHT에 bit를 두고 브랜치가 일어났을 땐 1로 수정한다. 다음에 브랜치 명령어를 실행할 때는 이 테이블의 값을 참고하여 브랜치 여부를 예측한다. BHT에 2개의 bit를 둬서 예측 성공률을 더 높이기도 한다. 

해저드 해결 방법에 대한 더 자세한 설명은 다음 을 참고.



캐시(Cache)

CPU의 클럭 속도가 매우 빨라지면서 메모리와의 속도 차이가 현저하게 증가하게 된다. 레지스터에 접근하는 속도와 메모리에 접근하는데 걸리는 속도는 100배 이상 차이 난다. 즉, 메모리가 병목 현상을 일으키는 요소가 된 것이다. 이 문제를 해결할 방법은 메모리 접근 횟수를 줄이는 것이고, 그 방법으로 캐시를 추가하게 되었다. 캐시를 추가하면 메모리에 접근할 때에 비해 접근 속도를 매우 증가시킬 수 있다. 메모리의 내용을 캐시에 일정량 복사해둠으로써 캐시에 원하는 데이터가 없을 때만 메모리에 접근하도록 한다. 이때, 캐시에 원하는 내용이 있을 때 캐시 히트(Cache hit), 없을 때 캐시 미스(Cache miss)라고 한다. 

출처 : 김병기 교수님 강의 노트

캐시는 비싸다. 더 큰 용량을 갖추려면 CPU 크기를 크게 만들게 되고 전력을 더 많이 사용해야 한다. 그렇기 때문에 먼저 참조하게 하는 캐시일수록 비싸고 빠르며 용량이 작게, 나중에 참조하게 되는 캐시일수록 저렴하고 덜 빠른 대신 용량이 크게 구성한다. L1 캐시에서 미스가 발생하면 다시 L2에 원하는 데이터가 있는지 참조하고, 또 존재하지 않으면 그보다 하위 레벨의 캐시를 확인한다. 최종적으로 캐시에 데이터가 없다는 것을 알게 되면 메모리에 접근하여 데이터를 다시 복사해온다.

지역성에는 시간 지역성과 공간 지역성이 있다. 시간 지역성이란 최근 접근했던 메모리 주소에 다시 접근하게 되는 현상을 말하고, 공간 지역성은 최근 접근했던 메모리 주소 근처를 다시 접근하게 되는 현상을 말한다. 명령어를 가져오는 것은 공간 지역성이 높고 데이터 참조는 시간 지역성이 높다고 알려져 있다. 캐시 미스가 일어나면 다시 cycle을 돌아 메모리에서 캐시로 복사하는 작업이 수행되어야 하므로 성능상 손해가 발생하지만, 캐시 미스는 10% 확률로 일어난다고 알려져 있으므로 캐시를 썼을 때 성능 향상이 비약적으로 뛰어나다고 볼 수 있다.

캐시가 있으면서 불일치 문제가 생긴다. sw 명령어를 실행하면서 메모리의 값을 수정하게 된다면 캐시에도, 메모리에도 적용해야 한다. 매번 메모리에 적용하는 방식을 Write through 방식이라고 한다. 하지만 이 방법은 메모리 값을 수정할 때마다 메모리에 접근하게 되기 때문에 성능 저하를 크게 일으킨다. Write back 방식은 그 대안이다. 캐시를 교체할 때만 메모리에 변경 사항을 적용하는 방식이다.



가상 메모리와 변환 색인 버퍼

현대 OS에서 가상 메모리는 필수적인 기능이다. 가상 메모리 기법을 통해 프로세스는 전체 논리 주소를 다 사용하고 있는 것처럼 느낄 수 있으며 보다 많은 물리 메모리를 가지고 있는 것 같은 효과도 줄 수 있다. 그러나 가상 메모리 기능을 지원해주는 것은 부하가 많이 든다. 페이지 테이블(Page table)을 구성하여 논리 주소를 물리 주소로 맵핑해주는 작업을 해줘야 하고, 페이지 폴트(Page fault)가 발생했을 때 희생 페이지(Victim)를 결정하는 작업도 해줘야 한다. 메모리 참조가 매우 자주 일어나는 일인 것을 생각했을 때, 이러한 작업을 하드웨어 지원을 받는다면 성능상에 큰 이점이 있기 때문에 보통 하드웨어 수준에서 가상 메모리 기능을 지원한다. 실제로는 메모리 관리 장치를 따로 두나 이 글에서는 가상 메모리 개념과 TLB를 소개하는 수준으로만 다루려 한다.

출처 : 위키백과

가상 메모리는 사용자가 논리 주소만을 이용하여 메모리에 접근하여 사용할 수 있게 하는 방법이다. 실제 메모리에 참조할 때는 페이지 테이블을 통해 물리 주소를 찾아 물리 주소로 접근한다. 이때, 페이지는 논리 주소 단위이고 물리 주소의 단위는 프레임이라고 한다. 페이지 크기는 OS에서 설정하도록 되어 있는데, 보통 4KB 정도로 구성한다. 페이지 크기가 작으면 페이지 테이블이 커지고, 페이지 크기가 커지면 페이지 폴트가 일어날 가능성과 페널티가 커지기 때문에 적당한 크기로 설정해야 한다. 예를 들어 4KB로 페이지 크기를 설정할 경우 12bit를 페이지 오프셋으로 결정하기 때문에 20bit를 가상 페이지 번호로 할당할 수 있게 되고, 2의 20승만큼의 페이지 엔트리가 존재하게 되므로 페이지 테이블의 용량 또한 무시할 수 없는 수준임을 알 수 있다.

페이지 폴트는 물리 메모리에 논리 메모리가 존재하지 않는 상황인데, 물리 메모리가 부족하여 페이지를 보조 기억 장치로 내렸거나, 애초에 물리 메모리에 로드한 적이 없는 페이지를 접근하려는 상황에 발생할 수 있다. 물리 메모리가 부족하여 새로운 페이지를 로드할 수 없다면 어쩔 수 없이 하나의 페이지를 희생 페이지로 정하고 빼내야 한다. 메모리 내용을 상실해서는 안되므로 물리 메모리에서 페이지를 내릴 때는 보조 기억 장치에 그 페이지 내용을 복사하는 방식으로 해결하는데, 보조 기억 장치에 접근한다는 것이 성능 저하를 일으키는 일이므로 페이지 폴트를 최대한 일어나지 않게끔 희생 페이지를 잘 선택하는 것 또한 매우 중요하다. 보통 LRU 알고리즘을 사용한다고 하지만 실제로는 LRU 알고리즘보다 쉬우면서 유사한 효과를 내는 유사 LRU 알고리즘들을 사용한다고 한다.

TLB는 논리 주소를 물리 주소로 변환하는 속도를 빠르게 하기 위해 존재하는 별도의 캐시다. 논리 주소를 물리 주소로 변환하려 할 때 먼저 TLB에 접근하여 원하는 페이지가 있는지 확인하고 존재하지 않는 경우 페이지 테이블을 탐색하여 페이지를 찾는다. 메인 메모리가 아닌 캐시 수준에서 찾아보기 때문에 주소 변환 속도를 훨씬 빠르게 해줄 것이라는 것을 알 수 있다. 페이지 폴트 때와 비슷하게 TLB에 원하는 페이지 엔트리가 없는 경우에도 페이지 테이블에서 TLB로 다시 복사해오는 것이 추후 다시 미스가 발생하지 않게 하는 방법일 것이다.



Polling과 DMA

I/O 작업은 CPU를 벗어나 별도의 장치에서 수행된다. 그런 만큼 I/O 작업이 수행되기 위해서는 별도의 처리 방법이 필요한데, 대표적으로 Polling 방식과 DMA(Direct Memory Access) 방식이 있다. 각 방식의 이름에 특징이 나타나 있는데, Polling은 주기적으로 I/O 장치의 상태 bit를 확인하여 데이터가 쌓였으면 당겨오는(polling) 방식인 Programmed I/O이고, DMA 방식은 장치가 직접 메모리에 접근하는 방식이다. Polling은 인터럽트(Interrupt)에서 인터럽트 핀이 하나일 때 어떤 장치가 인터럽트를 걸었는지 찾을때 사용하는 용어로도 쓰인다. 이 글에서는 앞서 언급한 Programmed I/O로 가정하고 설명한다.

출처 : http://www.yourdictionary.com/dma

Polling 방식은 프로세서의 시간을 많이 낭비하게 된다. 데이터가 쌓였는지 주기적으로 확인해야 하기 때문에 I/O 장치를 확인하는 작업을 수행해야 하는데, I/O 장치의 속도는 CPU만큼 빠르지 않기 때문에 많은 시간을 기다려야 한다. 여기서 좀 더 개선시키고자 고안한 방법이 인터럽트를 이용하여 CPU에게 상태 변화를 알리는 방식이다. 데이터가 쌓였을 때 프로세서에게 인터럽트를 발생시키면 CPU가 주기적으로 I/O 장치를 확인하지 않아도 되므로 많은 시간을 절약할 수 있다.

DMA 방식은 I/O 장치가 직접 메모리에 접근하여 값을 읽거나 쓰는 방식이다. DMA 장치에도 마이크로컨트롤러가 장착되어 있어 CPU처럼 명령어를 수행할 수 있는데, 마이크로컨트롤러에서 직접 메모리에 접근하여 I/O 작업을 수행한다. 그리고 Polling 방식과 비슷하게 작업이 끝났다는 것을 인터럽트로 알려줄 수 있다. 데이터 전송은 bus를 통해 일어나는데 CPU가 작동 중일 때 bus를 이용하려 하면 충돌이 일어나기 때문에 DMA가 bus를 이용하는 동안 CPU가 잠시 bus의 이용을 멈추어야 한다. 이것을 Cycle stealing이라고 한다.

모든 I/O 방식은 오버헤드가 존재한다. Polling 방식은 Busy waiting이 발생하고 인터럽트 주도 방식은 문맥 전환(Context switching)이 발생하며 DMA 방식은 bus cycle을 요구한다. 즉, 큰 비용이 드는 작업임을 알아둬야 한다.



이것으로 컴퓨터 구조에서의 주요 내용들을 살펴보았다. 컴퓨터 구조 강의를 통해 배운 것을 써먹게 되는 것이 어셈블러를 개발해보는 강의다. 어셈블리를 목적 코드로 바꾸는 어셈블러와 목적 코드를 실행해주는 시뮬레이터를 개발하면서 실제로 어떻게 명령어들을 인코딩하고 실행할 때 디코딩하는지를 느껴보는 것이다. 하지만 실제로 실습을 해보지 않고 글로만 배울 때는 큰 효과가 없다고 판단하여 개념 정리 시리즈에 추가하지 않을 계획이다.

다음 편에서는 이러한 컴퓨터 구조와 가장 가까이에 있는 OS를 다뤄보겠다.




브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari