본문 바로가기
AI & CS 지식/컴퓨터구조

[혼공컴운] 4. CPU의 작동 원리

by rahites 2024. 7. 30.

CPU의 작동 원리

1. ALU와 제어장치

(1장에서 봤듯이)

CPU는 메모리에 저장된 명령어를 읽고 해석하고 실행하는 장치이다. 

계산을 하는 ALU(Arithmetic and Logical Unit, 산술논리장치), 명령어를 읽고 해석하는 제어장치, 임시저장장치의 역할을 하는 레지스터가 존재한다.

 

ALU

ALU는 계산기이다. 이렇게 생각하면 우리는 계산기에 들어오는 정보연산, 내보내는 정보가 무엇인지로 ALU를 이해할 수 있다.

 

<입력 정보>

일반적으로 계산기를 생각해보면 피연산자수행할 연산이 필요한데, ALU는 레지스터를 통해 피연산자를 받아들이고 제어장치를 통해 수행할 연산을 알려주는 제어 신호를 받아들인다.

 

이후 ALU는 얻게된 정보를 바탕으로 연산을 진행한다.

 

<내보내는 정보>

연산을 수행한 결과 특정 문자나 숫자, 메모리 주소가 생성 될 수도 있으며 이 결과값은 메모리에 가기 전 레지스터에 저장된다. 그 이유는 CPU가 메모리에 접근하는 속도가 레지스터에 접근하는 속도보다 느리기 때문이다.

 

ALU는 연산을 수행할 때 일반적인 결과뿐만 아니라 플래그를 같이 내보낸다. 플래그는 연산 결과에 대한 추가적인 정보를 의미하며 플래그가 발생하는 예시는 다음과 같다.

(1) 이진수만 보고 음수인지 양수인지 판단하기 어려워 이를 구분하는 플래그를 사용하는 경우

(2) 방금 연산한 결과가 연산 결과를 담을 레지스터보다 클 때 이를 알리기 위한 정보(오버플로우)를 내보낼 경우

 

대표적인 플래그 종류

플래그 종류 의미
부호 플래그 연산한 부호(1: 음수, 0: 양수)
제로 플래그 연산 결과가 0인지 여부(1: 0임, 0: 0아님)
캐리 플래그 올림수나 빌림수가 발생했는지 여부(1: 발생, 0: 미발생)
오버플로우 플래그 오버플로우 발생 여부(1: 발생, 0: 미발생)
인터럽트 플래그 인터럽트가 가능한지 여부(1: 가능, 0: 불가능)
슈퍼바이저 플래그 커널모드/사용자 모드 실행 여부(1: 커널, 0: 사용자)

 

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

 

제어장치

제어장치는 제어 신호를 내보내고 명령어를 해석하는 부품이다. CPU 제조사마다 제어장치의 구현 방식이나 명령어를 해석하는 방식이 다르기 때문에 여기서는 대표적인 방법만을 다뤘다.

 

<입력 정보>

ALU가 피연산자와 수행할 연산 2가지 정보를 받아들인다면, 제어장치는 4가지의 정보를 받아들인다.

 

첫번째는 클럭 신호이다. 여기서 클럭(clock)이란 컴퓨터 부품의 시간 단위를 의미하며 컴퓨터의 모든 부품은 클럭 신호에 맞추어 작동한다. 클럭 주기에 맞추어 데이터의 이동, 연산 수행, 명령어 실행 등이 이루어진다. 

컴퓨터 속도를 빠르게 만들기 위한 오버클럭(Overclocking)의 클럭이 이것이다!!

 

두번째는 해석해야 할 명령어이다. 명령어 레지스터에는 CPU가 해석해야 하는 명령어가 저장되는데 제어장치에서 이 명령어를 받아 해석한 뒤 제어 신호를 발생시킨다. 

 

세번째는 플래그이다. ALU 결과 생성되어 플래그 레지스터에 저장된 플래그를 받아들인다.

 

네번째는 제어 신호이다. 제어장치는 CPU 외부에서 발생하는 제어 신호를 받아들인다. 이 때의 제어 신호는 제어 버스로 전달된다.

 

<내보내는 정보>

제어장치가 내보내는 정보는 크게 CPU 외부로 보내는 제어 신호내부로 보내는 제어신호가 있다.

 

각각의 장소로 제어 신호를 보내는 목적은 다음과 같다. 

  • 외부로 보내는 제어 신호
    • 메모리 : 값을 읽거나 쓰고 싶을 때
    • 입출력장치 : 값을 읽거나 쓰고 싶을 때
  • 내부로 보내는 제어 신호
    • ALU : 수행할 연산 지시
    • 레지스터 : 데이터 이동 / 명령어 해석

 

2. 레지스터

프로그램 속 명령어와 데이터는 실행 과정에서 반드시 레지스터에 저장된다. 이말은 즉 우리는 레지스터에 저장된 값만 확인하더라도 지금 프로그램이 어떻게 진행되고 있는지를 알 수 있다는 의미이다. 

 

이러한 레지스터는 한 가지 종류만 있는 것은 아니고 CPU마다 그 종류가 다양하지만, 대표적으로 사용되는 레지스터를 정리해 보자

 

1) 프로그램 카운터

프로그램 카운터(Program Counter)메모리에서 가져올 명령어의 주소를 의미한다. 명령어 포인터(Instruction Pointer)라고 부르기도 하는데 자료구조에서 포인터가 수행하는 역할을 생각하면 이해하기 편하다.

 

2) 명령어 레지스터

명령어 레지스터(Instruction Register)메모리에서 읽어 들인 명령어를 저장하는 레지스터이다. 명령어를 받아들여 해석한 뒤 이에 맞는 제어신호를 내보낸다.

 

3) 메모리 주소 레지스터

메모리 주소 레지스터(Memory Address Register)메모리의 주소를 저장하는 레지스터이다. 예를 들어 CPU가 읽어 들이고자 하는 주소 값을 주소 버스로 보낼 때 메모리 주소 레지스터를 거친다.

 

4) 메모리 버퍼 레지스터

메모리 버퍼 레지스터(Memory Buffer(Data) Register)메모리와 주고받을 데이터와 명령어를 저장하는 레지스터이다. 예를 들어 CPU가 데이터 버스로 주고 받을 값은 메모리 버퍼 레지스터를 거친다.

 

※ 이해를 돕기 위한 예시
1. CPU로 실행할 프로그램이 메모리의 "1번지 ~ 10번지"까지 저장되어 있다. 메모리의 각 주소에는 $10_2$, $11_2$와 같은 이진수가 저장되어 있을 것이다(데이터 or 명령어). 
2. 프로그램을 순서대로 실행하기 위해 우선 프로그램 카운터에 "1번지"를 저장한다.
3. 1번지를 읽기 위해 주소 버스에 "1번지"라는 정보를 보내야 한다("읽겠다"는 제어신호). 따라서 메모리 주소 레지스터에 "1번지"를 저장한다.
4. 제어장치에서는 "메모리 읽기" 제어 신호, 메모리 주소 레지스터에서는 "1번지" 라는 값이 각각 제어 버스주소 버스를 거쳐 메모리로 보내진다.
5. "1번지"에 저장되어 있던 $10_2$라는 값은 데이터 버스를 타고 메모리 버퍼 레지스터로 전달된다. 이 때 프로그램 카운터는 +1 되어 다음 명령어를 읽을 준비를 한다.
6. 메모리 버퍼 레지스터에 저장된 값은 명령어 레지스터로 이동한다.
제어장치는 명령어 레지스터의 값을 해석하고 제어 신호를 발생한다.

 

이와 같이 프로그램 카운터가 꾸준히 증가하기 때문에 프로그램은 순차적으로 작동할 수 있다.

 

5) 범용 레지스터

범용 레지스터(General Purpose Register)는 일반적인 상황에서 자유롭게 사용할 수 있는 레지스터이다. 데이터든 주소든 모두 저장할 수 있으며 CPU 안에는 여러 범용 레지스터가 존재한다.

 

6) 플래그 레지스터

플래그 레지스터(Flag Register)는 ALU의 연산 결과가 발생하는 플래그가 저장되는 레지스터이다. 연산 결과 또는 CPU 상태에 대한 부가적인 정보를 저장한다. 

 

7) 스택 포인터

스택 포인터(Stack Pointer)는 스택 주소 지정 방식에 사용되는 레지스터로 스택의 꼭대기를 나타낸다.

 

8) 베이스 레지스터

베이스 레지스터(Base Register)는 기준 주소를 나타내는 레지스터이다.

 

스택 포인터베이스 레지스터는 주소 지정 방식에 사용되는 특별한 레지스터이다. 주소 지정 방식3장에서 공부했던 내용으로 연산에 사용할 데이터의 위치를 찾는 방법을 의미한다. 즉시 주소 지정 방식, 직접 주소 지정 방식, 간접 주소 지정 방식, 레지스터 주소 지정 방식, 레지스터 간접 주소 지정 방식을 알아 봤었는데 위와 같이 특별한 레지스터의 개념을 이해해야만 알 수 있는 주소 지정 방식이 또한 존재한다.

 

우선은 스택 주소 지정 방식이다.

 

스택 주소 지정 방식은 스택과 스택 포인터를 이용한 주소 지정 방식이다. 스택은 우리가 자료구조를 통해 배웠던 것 처럼 가장 최근에 저장하는 값부터 꺼낼 수 있다는 특징을 가지는데, 프로그램 카운터가 리스트 구조에서 다음에 어떤 명령어를 실행할지를 저장하고 있다면 스택 포인터는 스택 구조에서 다음에 어떤 명령어를 실행할지를 저장하고 있다고 이해하면 편하다.

 

메모리 속에는 스택처럼 사용할 영역이 정해져 있어 다른 주소 공간과는 다르게 스택 형태로 사용한다고 한다(스택 영역).

 

다음은 변위 주소 지정 방식이다.

우리는 3장에서 명령어는 연산 코드와 오퍼랜드(피연산자가 저장)로 이루어져 있으며 오퍼랜드에는 메모리의 주소가 담길 때도 있다고 공부했다. 변위 주소 지정 방식오퍼랜드 필드 값(변위)특정 레지스터의 값을 더하여 유효 주소를 얻어내는 주소 지정 방식이다.

 

따라서 이 주소 지정 방식을 사용하는 명령어는 (1) 어떤 명령을 수행할지 나타내는 연산 코드 (2) 더할 레지스터의 값 (3) 더할 오퍼랜드 값 으로 이루어져 있다. 변위 주소 방식은 오퍼랜드 값과 어떤 레지스터를 더하는지에 따라 상대 주소 지정 방식베이스 레지스터 주소 지정 방식으로 나뉜다.

 

상대 주소 지정 방식

오퍼랜드와 프로그램 카운터의 값을 더하여 유효 주소를 얻는 방식이다. 프로그램 카운터(PC)에는 읽어 들일 명령어의 주소가 있기 때문에 오퍼랜드에는 숫자가 들어가 +1일시 PC+1번지에 있는 명령어를, -1일시 PC-1번지에 있는 명령어를 실행한다.

 

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

베이스 레지스터에는 기준 주소가, 오퍼랜드에는 기준 주소로부터 떨어진 거리가 저장되어 두 값을 더한 주소에 존재하는 명령어를 실행하는 방식이다.

 

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

CPU는 지금까지 알아본 제어장치와 ALU, 레지스터를 돌며 진행되는 흐름을 반복하며 명령어를 처리한다. 

 

이러한 하나의 명령어를 처리하는 흐름을 명령어 사이클이라고 하며, 이 흐름이 끊어지는 상황을 인터럽트라고 한다.

 

명령어 사이클

프로그램은 수많은 명령어로 이루어져 있다. 명령어들은 일정한 명령어 사이클에 따라 반복되며 실행되고 크게는 메모리에서 명령어를 가져오는 인출 사이클(Fetch Cycle)과 CPU로 가져온 명령어를 실행하는 실행 사이클(Execution Cycle)로 나뉜다.

 

인출 사이클은 <2. 레지스터> 파트 중 이해를 돕기 위한 예시에서 확인할 수 있고, 실행 사이클은 제어장치가 명령어 레지스터에 담긴 값을 해석하고 제어 신호를 발생시키는 단계이다.

 

인출 사이클과 실행 사이클만을 반복하는 것으로 모든 명령어가 실행되면 좋겠지만, 간접 주소 지정 방식과 같이 명령어 속에서 메모리 접근이 필요한 경우가 존재한다(유효 주소 값을 알아내기 위해). 이러한 경우에는 간접 사이클(Indirect Cycle)이 진행된다.

 

인터럽트

인터럽트는 우리가 코딩을 하며 쉽게 볼 수 있는 키워드로 CPU의 작업을 방해하는 신호를 의미한다. 인터럽트에는 크게 동기 인터럽트와 비동기 인터럽트가 존재한다.

 

1) 동기 인터럽트

CPU에 의해 발생하는 인터럽트로 CPU가 명령어를 수행하는 도중 문제가 발생하였을 때 인터럽트를 발생시킨다. 대표적인 예로는 프로그래밍 오류가 있으며 따라서 동기 인터럽트는 예외(Exception)이라고도 부른다.

 

2) 비동기 인터럽트

비동기 인터럽트는 주로 입출력 장치에 의해 발생하는 인터럽트이다. CPU가 프린터와 같은 입출력 장치에 작업을 부탁하면, 작업을 끝낸 입출력 장치가 CPU에 완료되었다는 알림을 인터럽트로써 보내는 것이다. 일반적으로 인터럽트라 하면 비동기 인터럽트를 의미하고 하드웨어 인터럽트라고 부르기도 한다.

 

입출력 장치는 CPU보다 속도가 느리기 때문에 CPU가 주기적으로 입출력 장치의 작업이 끝났는지를 확인한다면 매우 비효율적일 것이다. 따라서 CPU는 하드웨어 인터럽트가 오기 전까지 다른 일을 처리하다가 입출력 장치의 인터럽트를 받은 후 해당 장치의 알림을 확인한다. 

 

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

  1. 입출력 장치가 CPU에 인터럽트 요청 신호를 보낸다.
  2. CPU는 실행 사이클이 끝나고 명령어를 인출하기 전 항상 인터럽트 여부를 확인한다.
  3. CPU는 인터럽트 요청을 확인하고 인터럽트 플래그를 통해 현재 인터럽트를 받아들일 수 있는지 여부를 확인한다.
  4. 인터럽트를 받아들일 수 있다면 CPU는 지금까지의 작업을 백업해둔다.
  5. CPU는 인터럽트 벡터를 참조하여 인터럽트 서비스 루틴을 실행한다.
  6. 인터럽트 서비스 루틴 실행이 끝나면 백업해 둔 작업을 복구하여 실행을 재개한다.

또한 하드웨어 인터럽트에는 정전이나 하드웨어 고장과 같이 막을 수 없는 인터럽트도 존재한다. 

Jupyter Notebook으로 머신러닝 모델을 돌리다 보면 내가 네모모양 Stop 버튼을 누르더라도 모델이 학습하던 것이 즉시 종료되지 않고 조금 뒤에 종료되는 것을 확인할 수 있다. 그 당시에는 몰랐지만 지금 생각해보면 CPU가 인터럽트 플래그를 False로 계속 주어 하나의 사이클이 마무리 되고나서야 정상 중지 되었을 것이라 추측한다. Shut Down이나 kill 명령어가 막을 수 없는 인터럽트이지 않을까... 라고도 생각한다.

 

인터럽트 서비스 루틴은 인터럽트 핸들러라고도 부르며 특정 인터럽트가 발생했을 때 해당 인터럽트를 어떻게 처리하고 작동해야 할지에 대한 정보로 이루어진 프로그램이다.

 

메모리 속에서 정상적으로 작업이 진행되다 특정 인터럽트가 발생하였을 경우 다른 메모리에 저장되어 있던 인터럽트 서비스 루틴에 따라 인터럽트를 처리하고 다시 돌아가 작업을 이어 진행하는 형식이다. 앞서 설명한 인터럽트 벡터는 인터럽트 서비스 루틴을 식별하기 위한 정보를 담고있어 이를 알면 인터럽트 서비스 루틴의 시작 주소를 알 수 있다. 

 

즉 CPU가 인터럽트를 처리한다는 말은 인터럽트 서비스 루틴을 실행하고 본래 수행하던 작업으로 다시 되돌아온다는 의미이다.

 

이 때 지금까지 순서대로 진행되어 저장된 프로그램 카운터를 비롯한 다른 레지스터의 값들은 스택 영역에 백업되어 인터럽트 서비스 루틴이 마무리 된 후 저장해 둔 값을 다시 불러와 작업을 재개한다.

 

결국 앞서 설명한 명령어 사이클의 순서는 인출 사이클 - 실행 사이클 - 간접 사이클에 인터럽트가 더해진 과정을 반복해 나가며 프로그램을 실행한다.

 

※ 예외(동기 인터럽트)의 종류

  • 폴트(Fault) : 예외를 처리한 직후 예외가 발생한 명령어부터 실행을 재개하는 예외
  • 트랩(Trap) : 예외를 처리한 직후 예외가 발생한 명령어의 다음 명령어부터 실행을 재개하는 예외(주로 디버깅시 사용)
  • 중단(Abort) : CPU가 실행중인 프로그램을 강제로 중단시켜야 하는 심각한 오류를 발견했을 때 발생하는 예외
  • 소프트웨어 인터럽트 : 시스템 호출이 발생했을 때

출처

강민철, 혼자 공부하는 컴퓨터 구조 + 운영체제, 한빛미디어, 2022

'AI & CS 지식 > 컴퓨터구조' 카테고리의 다른 글

[혼공컴운] 5. CPU 성능 향상 기법  (0) 2024.08.15
[혼공컴운] 3. 명령어  (0) 2024.07.21
[혼공컴운] 2. 데이터  (3) 2024.07.14

댓글