Aesthetic Algorithm

[혼공학습단] Week 2: CPU의 작동원리와 성능 향상 기법 본문

Computer Science/컴퓨터구조&운영체제

[혼공학습단] Week 2: CPU의 작동원리와 성능 향상 기법

미니튜링 2025. 7. 18. 02:07

🟦 이번 주 범위 🟦

Chapter 04. CPU의 작동 원리

04-1 ALU와 제어장치

04-2 레지스터

04-3 명령어 사이클과 인터럽트

Chapter 05. CPU 성능 향상 기법

05-1 빠른 CPU를 위한 설계 기법

05-2 명령어 병렬 처리 기법

05-3 CISC와 RISC

 

이번 주는 CPU의 구조와 동작 원리 및 효율적인 처리를 위한 설계 및 명령어 차이를 배운다.

학습하면서, 특히 input과 output이 내용의 절반이라고 해도 과언이 아닐 정도라고 느꼈다.

코딩이든 컴퓨터 구조든.. 무엇을 받아들여서 어떤 결과를 내보내는지가 본질인가보다. 컴퓨터 사이언스를 학습할 때는 인간적 자아보다는 컴퓨터적인 자아로 갈아끼우는 것이 효율이 좋다.

 

Chapter 04. CPU의 작동 원리

04-1 ALU와 제어장치

CPU의 구성 요소 중, 먼저 ALU와 제어장치에 대해 알아본다. (레지스터는 다음 장)

간단히 복습을 하자면 ALU(Arithmetic Logic Unit, ALU)는 연산을 수행하는 계산기, 제어장치는 명령어를 읽고 해석하는 장치라고 생각해 두자.

출처: https://limeup.io/glossary/alu/

계산기를 떠올려 보자. 계산을 위해서는 우선 계산할 값(피연산자)과 그 계산 방식(연산자)이 필요할 것이다. ALU의 작동 방식도 마찬가지다. 레지스터를 통해 피연산자를 받아들이고, 제어장치로부터 어떤 연산을 수행할지에 대한 제어 신호를 전달받아 연산을 수행한다. 연산의 결과값은 숫자, 문자, 메모리 주소 등이 될 수 있다. 이 값은 메모리로 직행하는 것이 아니라 레지스터에 일시적으로 저장된다. CPU는 메모리보다 레지스터에 접근하는 속도가 훨씬 빠르기 때문이다.

 

ALU는 플래그(flag) 정보도 내보낸다. 

이는 명령어 수행 후 결과의 상태를 나타내는 비트인데, 연산 결과에 대한 추가적인 정보를 붙이는 것이라고 이해하자.

예시를 들면, 255+1이라는 산술 연산을 수행한다고 하자. 8비트의 부호 없는 정수는 0부터 255까지 표현이 가능하다. 즉 255가 최댓값이다. 256을 표현하려면 9비트가 필요하기 때문에 오버플로우(overflow, 연산 결과가 연산 결과를 담을 레지스터보다 큰 상황)가 발생한다. 이진 덧셈 연산으로 11111111(255) + 00000001(1) -> 1 00000000라는 결과가 나오게 되는데, 보다시피 8자리는 전부 0, 왼쪽에 1로 자리올림이 발생한다. 그러나, 8비트 시스템에서는 상위 1비트를 버리고 00000000만 저장된다. 그렇다면 플래그의 상태는 다음과 같다:

플래그 의미
Carry Flag (CF) 자리올림 발생 1
Zero Flag (ZF) 결과가 0 1
Overflow Flag (OF) 부호 있는 오버플로우 사용 안 함 (부호가 없다면 사용하지 않음)

 

컴퓨터 시스템은 이 플래그들을 통해 연산의 이상 여부를 파악할 수 있는 것이다.

이와 관련하여 내가 생각한 비유가 있다. 한 학교에 해린이라는 이름을 가진 학생이 1학년에 1명, 2학년에 1명 있다고 해보자. 

둘은 이름은 같지만 완전히 다른 사람이다. 그러면 해린A, 해린B라고 구분해서 부를 수 있을 것이다.

마찬가지로 이진수 10000000 라는 같은 비트 패턴도 부호가 없다면 128, 부호가 있다면 -128로 다른 값으로 구분할 수 있다. 비트가 같으나 의미가 전혀 다를 수 있다는 것이다. 따라서 플래그는 컴퓨터의 오해를 방지하기 위해 꼭 필요하다고 할 수 있다.

 

ALU가 내보내는 대표적인 플래그는 다음과 같다:

플래그 종류 의미
부호 플래그 연산한 결과의 부호를 나타낸다.
제로 플래그 연산 결과가 0인지 여부를 나타낸다.
캐리 플래그 연산 결과 올림수나 빌림수가 발생했는지를 나타낸다.
오버플로우 플래그 오버플로우가 발생했는지를 나타낸다.
인터럽트 플래그 인터럽트가 가능한지를 나타낸다. 
슈퍼바이저 플래그 커널 모드로 실행 중인지, 사용자 모드로 실행 중인지를 나타낸다.

 

플래그들은 플래그 레지스터에 저장된다.

input 피연산자(<-레지스터), 제어신호(<-제어장치)
output 계산 결과값(->레지스터), 플래그(->플래그 레지스터)

 

다음으로 제어장치에 대해 알아보자.

제어장치는 정교하게 만들어져 여러 제어 신호들을 내보내 컴퓨터 부품들을 관리한다.

input 의미 및 동작
클럭(clock) 컴퓨터의 모든 부품을 일사불란하게 움직일 수 있게 하는 시간 단위
시계 똑딱소리처럼 일정한 주기에 맞춰 명령어가 작동하게 된다.
명령어 명령어 레지스터로부터 해석할 명령어를 받아들이고 해석한 뒤 제어 신호를 발생시킴.
플래그 값 플래그 값을 받아들여 참고하고 제어 신호를 발생시킨다.
제어 신호 제어 버스를 통해 외부로부터 전달된 제어 신호를 받아들인다.

 

 

output 의미 및 동작
CPU 외부 전달 제어 신호 제어 버스로 제어 신호를 내보낸다
전달되는 곳에 따라 메모리/입출력장치 제어 신호가 있음
CPU 내부 전달 제어 신호 ALU에 전달하는 제어 신호: 수행할 연산을 지시하기 위함
레지스터에 전달하는 제어 신호: 레지스터 간 데이터 이동 혹은 레지스터에 저장된 명령어를 해석하기 위함

 

 

04-2. 레지스터

아까 다루지 못했던 레지스터에 대해 다룰 차례다.

간단히 복습하면, 프로그램 속 명령어와 데이터가 실행 전후 반드시 저장되는 곳이 레지스터라고 했다.

핵심적인 요소이므로, 이 레지스터의 값에 주목하다보면 CPU 내부 흐름도 파악할 수 있다.

 

레지스터의 종류는 다음과 같다:

레지스터 종류 설명
프로그램 카운터 매모리에서 읽어 들일 명령어의 주소를 저장한다.
명령어 레지스터 방금 메모리에서 읽어 들인 명령어를 저장한다. 
메모리 주소 레지스터 메모리의 주소를 저장한다. CPU가 읽어 들이고자 하는 주소 값을 주소 버스로 보낼 때 거치게 된다.
메모리 버퍼 레지스터 메모리와 주고받을 값(데이터, 명령어)을 저장한다. 
플래그 레지스터 연산 결과 또는 CPU 상태에 대한 부가적인 정보
범용 레지스터 다양하고 일반적인 상황에서 자유롭게 사용
스택 포인터 스택의 꼭대기(스택에 마지막으로 저장한 값의 위치 저장)를 가리킨다.
베이스 레지스터 이곳에 저장된 주소는 기준 주소로서의 역할을 한다.

 

특정 주소를 이용한 주소 지정 방식이 2가지가 있는데, 1) 스택 주소 지정 방식과 2)변위 주소 지정 방식이 있다.

 

우선, 스택 주소 지정 방식은 스택스택 포인터를 이용하는 주소 지정 방식이다.

스택이란 통 같은 저장 공간이다. (개인적으로 일정한 눈금이 그려진 비커를 연상했다.)

메모리 안에 사용할 영역이 정해져 있으며, 이를 스택 영역이라고 한다.

스택 포인터란 스택의 꼭대기를 가리키는 레지스터다.

https://www.elprocus.com/what-is-stack-stack-pointer-types-operations-its-application/

 

따라서 스택에서 데이터를 꺼낼 때에는 위에서부터 꺼내게 된다.

 

다음으로, 변위 주소 지정 방식을 살펴보겠다.

'변위' 라는 단어부터 보면, displacement, 즉 명령어에 포함된 상수 값을 의미한다.

변위 주소 지정 방식이란 오퍼랜드 필드의 값(변위)과 특정 레지스터의 값을 더하여 유효 주소(최종적으로 접근할 실제 메모리 주소)를 얻어내는 방식이다.

오퍼랜드 필드(Operand Field)도 잠깐 복습하면 명령어에서 무엇을 갖고 연산할지를 나타내는 부분이다. 즉 연산 대상(데이터나 주소, 예를 들면 레지스터나 메모리 주소)을 명시한다. 

 

추상적이라 이해가 쉽지 않다. 예시를 들어 보자.

scores = [85, 90, 78, 92, 88] # 메모리 주소 1000번지부터 순차적으로 저장됨, 1001, 1002,...
print(scores[2])

scores[2]는 scores 리스트의 시작 주소(base address)를 기준으로 인덱스 2(변위, displacement)에 있는 값을 가져오는 것이다. 여기서 유효 주소는 scores[0]에서 2칸 떨어진 메모리 위치가 되겠다. 그렇다면 scores[2]는 메모리 1002번지의 값을 가져오는 것이다. 오퍼랜드 필드는 인덱스 2값이 명령어 안에 존재하는 상수다.

명령어 형식으로 다시 보자.

책에서는 [연산 코드 | 레지스터 | 오퍼랜드] 형태로 되어 있다.

BX 레지스터가 1000을 가리키고 있고, 그로부터 2만큼 떨어진 곳인 1002번지의 데이터를 가져오고 싶다면,

[MOV | AL | [BX + 2]]처럼 써 볼 수 있겠다. 

MOV: 연산 코드(데이터 복사)

AL: 레지스터(결과 저장 위치)

[BX + 2]: 오퍼랜드(유효 주소 계산 대상)

 

이 변위 주소 지정 방식은 오퍼랜드 필드의 주소와 어떤 레지스터를 더하는지에 따라 2가지로 구분된다.

편의를 위해 프로그램 카운터는 PC라고 표기하겠다.

 

1) 상대 주소 지정 방식

오퍼랜드와 PC값을 더하여 유효 주소를 얻는다.

PC에는 불러들일 명령어의 주소가 저장되어 있다. 따라서 PC+10이라고 한다면 현재 위치에서 10만큼 이동하라는 것이다.

양수라면 실행할 명령어의 10번째 건너뛴 번지(통의 밑으로 내려간다)를 실행하는 것이다.

IF문처럼, 분기하여 특정 주소의 코드를 실행할 때 사용한다고 한다.

 

2) 베이스 레지스터 주소 지정 방식

오퍼랜드와 베이스 레지스터의 값을 더하여 유효 주소를 얻는다.

기준 주소가 베이스 레지스터이다. 따라서 오퍼랜드는 기준 주소로부터 떨어진 거리를 의미한다.

명령어 예시로 보면 MOV AL, [BX+2]일 때 BX 기준 2만큼 떨어진 곳을 말한다. 

 

요약하면, 유효 주소는 1번 방식일 때 명령어 위치 기준으로 상대적이고 2번 방식일 때 기준점이 레지스터인 것이다.

 

04-3. 명령어 사이클과 인터럽트

이전 장까지는 CPU의 구성 요소들에 대해 더 자세히 다루었다면, 이번엔 CPU 안에서의 흐름을 살펴보자.CPU가 하나의 명령어를 처리하는 과정에는 정해진 흐름이 있다. 이러한 흐름을 명령어 사이클이라고 한다. 그러나 간혹 그 흐름이 끊어지는 상황이 발생하며, 이를 인터럽트라고 부른다.

 

명령어 사이클에 대해 배워보자.프로그램 속 각각의 명령어들은 명령어 사이클이 반복되며 실행된다. 그 사이클은 생각보다 단순하다.먼저, 메모리에 저장된 명령어 1개를 실행한다고 생각해보자.단계는 다음과 같을 것이다.

 

1) 인출 사이클

메모리에서 CPU로 명령어를 가져온다.

 

2) 실행 사이클

CPU로 인출된 명령어를 실행한다.

구체적으로 제어장치가 명령어 레지스터에 담긴 값을 해석하고, 제어 신호를 발생시킨다.

 

인출-실행-인출-실행.. 프로그램을 이루는 명령어들은 모두 이 인출과 실행 사이클을 반복하며 실행된다. 

 

사이클이 너무 단순해 보인다고? 그렇다, 세상은 그렇게 호락호락하지 않다.

명령어를 CPU로 인출해서 잘 데려온다해도, 바로 실행할 수 없는 경우가 있다. 

몇 번 더 메모리에 접근해야 하는 상황이 생기기 때문이다. 이 단계를 간접 사이클이라고 한다. 

 

당연히 여기서 끝나지도 않는다.

프로그래밍을 하다보면 필연적으로 마주치게 되는 인터럽트가 등장하기 때문이다.

 

CPU가 수행 중인 작업이 어떠한 방해공작에 의해 멈춰질 수 있다. 이 방해공작을 인터럽트라고 한다.마치 내가 유튜브 영상을 보고 있는데, 갑자기 전화가 와서 받은 후, 다시 영상을 본다고 생각해보자.인터럽트는 이처럼 일시적인 작업 전환에 가깝다. 원래 하던 작업을 잠시 멈추고 다른 일을 처리하는 것이다.다른 예시로, 유튜브 영상을 보다가 갑자기 와이파이 신호가 끊겨버린 상황을 생각해보자. 이건 인터럽트라기보다는 비정상적인 종료나 예외처리에 가깝다.

 

아무튼 인터럽트는 CPU의 정상적인 작업을 방해하여, CPU의 주목을 끌어오는 역할을 한다.종류는 다음과 같다:

종류 설명
동기 인터럽트(예외) CPU에 의해 발생하는 인터럽트다. 
프로그래밍상 오류처럼, 예외적인 상황에 마주쳤을 때 발생한다.
비동기 인터럽트(하드웨어 인터럽트) 입출력장치에 의해 발생한다.
CPU에게 작업이 끝났다고 알림을 날려준다. 이로 인해 CPU는 좀 더 효율적으로 명령어를 처리할 수 있다.

 

비동기 인터럽트(하드웨어 인터럽트)에 대해 좀 더 살펴보자. 이것이 인터럽트에 대해 좀 더 일반적인 내용이다.

하드웨어 인터럽트의 처리 순서는 다음과 같다:

 

1. 입출력장치는 CPU에 인터럽트 요청 신호를 보낸다.

2. CPU는 실행 사이클이 끝나고 명령어 인출 전 항상 인터럽트 여부를 확인한다.

3. CPU는 인터럽트 요청을 확인하고 인터럽트 플래그를 통해 현재 인터럽트를 받아들일 수 있는지 여부를 확인한다.

4. 인터럽트를 받아들일 수 있다면 CPU는 지금까지의 작업을 백업한다.

5. CPU는 인터럽트 벡터를 참조하여 인터럽트 서비스 루틴을 실행한다.

6. 인터럽트 서비스 루틴 실행이 끝나면 4에서 백업한 작업을 복구하여 실행을 재개한다.

인터럽트 요청 신호: CPU의 정상적 작업 흐름을 끊기 위해 지금 끼어들어도 되냐고 물어보기

 

CPU가 요청 신호를 OK하기 위해서는, 플래그 레지스터의 인터럽트 플래그가 활성화되어 있어야 함. 

 

○  인터럽트 플래그: 하드웨어 인터럽트를 받을지 무시할지 결정하는 플래그

무조건 OK하는 것이 아님, CPU가 중요한 일 하고 있으면 불가능 띄워도 됨. 

그런데 꼭 받아줘야 하는 인터럽트 요청도 있다! 정전 혹은 고장인 경우가 그러하다.

 

○  인터럽트 벡터: 각 인터럽트 구분을 위한 정보, 이를 통해 루틴의 시작점을 알 수 있다.

 

○  인터럽트 서비스 루틴: 인터럽트 처리 프로그램

인터럽트를 보낼 수 있는 주체(키보드, 마우스 등)에 따라 각기 다른 인터럽트 서비스 주소를 갖는다.

 

결국 CPU는 인터럽트 서비스 루틴이 끝나면 원래 하던 작업으로 되돌아와야 하기에, 그 작업 내역을 백업해두어야 한다. 

서비스 루틴이 끝나고(=인터럽트 처리 완료)나면 스택에 저장해 둔 값을 불러와서 재개한다.

 


2주차 숙제

1. 125p 확인문제 2번

1) 플래그 레지스터

2) 프로그램 카운터

3) 범용 레지스터

4) 명령어 레지스터

 

2. 155p 확인문제 4번

코어