CPU
최종 수정 : 2025.04.21
CPU
컴퓨터에서 가장 중요한 부품 중 하나
■ 레지스터
CPU 안에 있는 작은 임시 저장장치이며, 다양한 레지스터들이 있고, 각기 다른 이름과 역할이 있다. 프로그램을 이루는 데이터와 명령어가 프로그램의 실행 전후로 레지스터에 저장되기 때문에, 레지스터에 저장된 값만 잘 관찰해도 비교적 낮은 수준의 프로그램이 어떻게 작동하는지를 파악할 수 있다.
WinDbg(윈도우 운영체제), hdb(리눅스, 맥OS 운영체제) 등의 도구를 이용해 관찰할 수 있다.
1. 프로그램 카운터(PC, Program Counter)
메모리에서 다음으로 읽어 들일 명령어의 주소를 저장한다. 간혹, 명령어 포인터(IP, Instruction Pointer)라고 부르는 CPU도 있다. 일반적으로 프로그램 카운터는 1씩 증거하는데, 이는 곧 다음으로 읽어 들일 메모리 주소가 1씩 증가하는 것과 같다.
다만, 언제나 증가만 하는 것은 아니다. 조건이 참이 되거나 리턴문을 실행하는 경우에는 프로그램이 순차적으로 실행되지 않는다. 이럴 때는, 프로그램 카운터 값이 임의의 위치로 변경된다.
2. 명령어 레지스터(IR, Instruction Register)
해석할 명령어, 즉 메모리에서 방금 읽어 들인 명령어를 저장하는 레지스터이다. CPU 내의 제어장치는 명령어 레지스터 속 명령어를 해석한 뒤 ALU로 하여금 연산하도록 시키거나 다른 부품으로 제어 신호를 보내 해당 부품을 작동시킨다.
3. 범용 레지스터(general purpose register)
일반적인 상황에서 자유롭게 사용할 수 있는 레지스터이다. 데이터와 명령어, 주소 모두를 저장할 수 있다. 일반적으로 CPU 안에는 여러 개의 범용 레지스터들이 존재한다.
4. 플래그 레지스터(flag register)
연산의 결과 혹은 CPU 상태에 대한 부가 정보인 플래그(flag) 값을 저장하는 레지스터이다. 플래그는 CPU가 명령어를 처리하는 과정에서 반드시 참조해야 할 상태 정보를 의미하는 비트이다.
종류 | 설명 | 사용 예시 |
부호 플래그 | 연산 결과의 부호 | 1 : 음수 0 : 양수 |
제로 플래그 | 연산 결과가 0인지의 여부 | 1 : 연산 결과가 0 0 : 연산 결과가 0이 아님 |
캐리 플래그 | 연산 결과에 올림수나 빌림수가 발생했는지의 여부 | 1 : 발생 0 : 미발생 |
오버플로우 플래그 | 오버플로우가 발생했는지의 여부 | 1 : 발생 0 : 미발생 |
인터럽트 플래그 | 인터럽트가 가능한지의 여부 | 1 : 인터럽트가 가능 0 : 불가능 |
슈퍼바이저 플래그 | 커널 모드로 실행 중인지, 사용자 모드로 실행 중인지의 여부 | 1 : 커널 모드 0 : 사용자 모드 |
5. 스택 포인터(stack pointer)
메모리에는 실행 중인 프로그램들이 적재되어 있으며, 실행 중인 각 프로그램들은 스택과 같은 형태로 사용 가능한 주소 공간을 하나 이상 가지고 있다. 암묵적으로 스택처럼 사용하자고 약속된 이 메모리 영역을 스택 영역이라고 하며, 스택 포인터는 메모리 내 스택 영역의 최상단 스택 데이터 위치를 가리키는 특별한 레지스터를 말한다.
즉, 마지막으로 스택에 저장된 데이터의 위치를 가리키는 레지스터이자. 스택이 채워진 정도를 나타내는 레지스터인 셈이다.
■ 인터럽트(Interrupt)
'방해하다, 중단시키다'라는 의미를 갖고 있으며, CPU의 작업을 방해하는 신호를 인터럽트라고 한다.
- 동기 인터럽트(synchronouse interrupts)
- CPU에 의해 발생
- 가령 CPU가 프로그램 오류와 같은 예외적인 상황을 마주할 때 발생한다.
- 이런 점에서 예외(exception)이라고도 부른다. - 비동기 인터럽트 (asynchronouse interrupts)
- 주로 입출력장치에 의해 발생
- ex) 세탁기의 세탁 완료 알림, 전자레인지의 조리 완료 알림
- 하드웨어 인터럽트라고도 한다.
1. 하드웨어 인터럽트
CPU는 효율적으로 명령어를 처리하기 위해 하드웨어 인터럽트를 사용한다. 일반적으로 입출력장치의 속도는 CPU에 비해 현저히 느리다. 그래서 CPU는 입출력 작업의 결과를 바로 받아볼 수 없다. 만약 이를 사용하지 않는다면, 주기적으로 완료 여부를 확인(폴링, polling)해야 한다.
이렇듯 하드웨어 인터럽트는 입출력 완료 여부를 확인하기 위한 CPU 사이클 낭비를 최소화하고, CPU가 다른 일을 수행할 수 있는 시간을 벌어 줌으로써 효율적으로 명령어를 처리할 수 있도록 돕는다.
CPU가 인터럽트를 처리하는 방식은 인터럽트의 종류를 막론하고 비슷하다.
- 입출력장치는 CPU에게 인터럽트 요청 신호를 보낸다.
- CPU는 실행 사이클이 끝나고 명령어를 인출하기 전에 항상 인터럽트 여부를 확인한다.
- CPU는 인터럽트 요청을 확인하고, 인터럽트 플래그를 통해 현재 인터럽트를 받아들일 수 있는지 여부를 확인한다.
- 인터럽트를 받아들일 수 있다면 CPU가 지금까지의 작업을 백업한다.
- CPU는 인터럽트 벡터를 참조하여 인터럽트 서비스 루틴을 실행한다.
- 인터럽트 서비스 루틴 실행이 끝나면, 백업해 둔 작업을 복구하며 실행을 재개한다.
인터럽트는 CPU의 정상적인 실행 흐름을 끊는 것이기 때문에 인터럽트하기 전에 CPU에게 인터럽트의 가능 여부를 확인하기 위해 인터럽트 요청(Interruput request) 신호를 보낸다.
막을 수 있는 인터럽트(MI) / 막을 수 없는 인터럽트(NMI)
이를 위해서는 플래그 레지스터의 인터럽트 플래그(interruput flag)가 활성화되어 있어야 한다. 그렇지 않으면 무시된다. 다만, 모든 하드웨어 인터럽트를 막을 수 있는 것은 아니다. 즉, 막을 수 있는 인터럽트(maskable interupt)와 막을 수 없는 인터럽트(non maskable interruput)가 존재한다.
인터럽트 서비스 루틴(ISR)
인터럽트 요청을 받아들이기도 했다면, CPU는 인터럽트 서비스 루틴이라는 프로그램을 실행한다. 인터럽트 서비스 루틴(ISR, Interruput Service Routine)은 인터럽트를 처리하기 위한 프로그램으로 인터럽트 핸들러(interrupt handler)라고도 부르며, 어떤 인터럽트가 발생했을 때 해당 인터럽트를 어떻게 처리하고 작동해야 할지에 대한 정보로 이루어진 프로그램이다. 이는 본직적으로 함수를 실행하는 것과 비슷한 작동방식이다. 실행의 조건이 충족되면 실행되는 함수와 같기 때문이다.
인터럽트 벡터
입출력장치마다 각기 다른 인터럽트 서비스 루틴을 갖고 있다. CPU는 수많은 인터럽트 서비스 루틴들을 구분하기 위해 인터럽트 벡터(interrupt vector)을 이용한다. CPU는 하드웨어 인터럽트 요청을 보낸 대상으로부터 버스를 통해 인터럽트 벡터를 전달받는다. 이 벡터에 인터럽트 서비스 루틴의 시작 주소가 포함되어 있기에, CPU는 인터럽트 벡터를 통해 처음부터 특정 인터럽트 서비스 루틴을 실행할 있다.
인터럽트 서비스 루틴 또한 데이터와 명령어로 이루어져 있기 때문에 인터럽트 서비스 루틴도 프로그램 카운터를 비롯해 레지스터들을 사용하면서 실행된다. 이때 인터럽트 요청을 받기 직전까지 CPU가 수행하고 있었던 일은 인터럽트 서비스 루틴이 끝나면 되돌아와서 마저 수행되어야 하므로 지금까지의 작업들을 어딘가에 백업해 둬야 한다. 따라서 CPU는 인터럽트 서비스 루틴을 실행하기 전에 프로그램 카운터 값 등을 현재 프로그램을 재개하기 위해 필요한 모든 내용을 메모리 내 스택에 백업한다.
그리고 나서 인터럽트 서비스 루틴의 시작 주소가 위치한 곳으로 프로그램 카운터 값을 갱신하고, 인터럽트 서비스 루틴을 실행한다. 인터럽트 서비스 루틴을 모두 실행하면, 스택에 저장해 둔 프로그램 카운터 등을 다시 불러온 뒤 이전까지 수행하던 작업을 재개한다.
2. 동기 인터럽트 (예외)
CPU가 본래 하던 작업으로 되돌아왔을 때, 예외가 발생한 명령어부터 실행하느냐, 예외가 발생한 명령어의 다음 명령어부터 실행하느냐에 따라 폴트와 트랩으로 나눌 수 있다.
- 폴트(fault)
- 예외를 처리한 직후에 예외가 발생한 명령어부터 실행을 재개한다.
- CPU는 필요한 데이터를 보조기억장치로부터 메모리로 가지고 왔으므로 다시 실행을 재개해 폴트가 발생한 그 명령어부터 실행해 나가는데, 이를 '페이지 폴트'라고 한다. - 트랩(trap)
- 예외를 처리한 직후에 예외가 발생항 명령어의 다음 명령어부터 실행을 재개한다.
- ex) 디버깅의 브레이크 포인트 : 특정 코드가 실행되는 순간에 프로그램을 멈추게 한다. 프로그램을 중단시키고 디버깅이 끝나면 트랩이 발생한 그 다음 명령어부터 실행해 나간다. - 중단(abort)
- CPU가 실행 중인 프로그램을 강제로 중단시킬 수 밖에 없는 심각한 오류를 발견했을 때 발생하는 예외 - 소프트웨어 인터럽트(software interrupt)
- 시스템 콜이 발생했을 때 발생하는 예외
■ CPU 성능 향상을 위한 설계
CPU 클럭 속도
클럭(clock) : 컴퓨터 부품을 일사불란하게 움직일 수 있게 하는 시간의 단위
클럭 속도 : 헤르츠(Hz) 단위로 측정되는데, 클럭이 1초에 몇 번 반복되는지를 나타낸다.
클럭 속도가 높아지면 CPU는 명령어 사이클을 더 빠르게 반복하고, 다른 부품들도 그에 따라 더 빠르게 작동한다. 이런 점에서 클럭 속도는 CPU의 속도 단위로 간주되기도 한다. 하지만 클럭 속도를 필요 이상으로 높이면 컴퓨터의 발열이 심해질 수 있기 때문에 클럭 속도를 높이는 것만으로 CPU의 성능을 높이는 데에는 한계가 있다.
멀티코어와 멀티스레드
코어(core) : CPU 내에서 명령어를 읽어 들이고, 해석하고, 실행하는 부품
코어 수나 스레드 수를 늘리는 방법으로도 CPU의 성능을 높일 수 있다. CPU 안에는 코어들이 여러 개가 존재할 수 있다. 이처럼, 여러 개의 코어를 포함하고 있는 CPU를 멀티코어 CPU, 혹은 멀티코어 프로세서라고 부른다.
스레드(thread) : 실행 흐름의 단위
- 하드웨어 스레드 (물리적인 실행 단위)
- 하나의 코어가 동시에 처리하는 명령어의 단위
- 멀티스레드(multithread) 프로세서, 혹은 멀티스레드 CPU라고 한다.
- '한 번에 하나의 명령어를 처리하는 1개의 CPU'
- 그래서, 논리 프로세서(logical processor)라고 부르기도 한다.
- 작업을 물리적으로 동시에 처리하는 병렬성(parallelism) - 소프트웨어 스레드 (논리적인 실행 단위)
- 하나의 프로그램에서 독립적으로 실행되는 단위
- 메모리에 적재된 해당 프로그램을 구성하는 여러 부분이 동시에 실행될 수 있다.
- 그러나 하드웨어 스레드가 하나인 CPU로도 이러한 프로그램을 충분히 실행할 수 있다.
- 동시에 작업을 처리하는 것처럼 보이는 동시성(concurrency)
- 사용자의 눈에는 마치 여러 작업이 동시에 처리되는 것처럼 보이지만, 물리적으로 같은 시점에서는 하나씩 번갈아가며 처리되고 있다.
■ 파이프라이닝을 통한 명령어 병렬 처리
명령어 병렬 처리 기법
- ILP, Instruction-Level Parallelism)
- 여러 명령어를 동시에 처리하여 CPU를 한시도 쉬지 않고 작동시킴으로써 CPU의 성능을 높이는 기법
명령어 파이프라이닝(Instruction Pipelining)
- 명령어 인출(Instruction Fetch)
- 명령어 해석(Instruction Decode)
- 명령어 실행(Execute Instruction)
- 결과 저장(Write Back)
같은 단계만 겹치지만 않는다면, CPU가 각각의 단계를 동시에 처리할 수 있다. CPU는 하나의 명령어가 인출되는 동안 다른 명령어를 실행할 수 있고, 하나의 명령어가 실행되는 동안 연산의 결과를 저장할 수 있다. 이처럼 공장의 생산 라인과 같이 명령어 파이프라인(Instruction pipeline)에 넣고 동시에 처리하는 기법
슈퍼스칼라(superscalar)
- 오늘날 대부분의 CPU는 여러 개의 파이프라인을 이용한다. 이처럼 CPU 내부에 여러 명령어 파이프라인을 포함하는 구조를 슈퍼스칼라라고 한다.마치 공장의 생산 라인을 여러 개 두는 것과 같다. 이런 구조를 사용하여 명령어 처리가 가능한 CPU는 슈퍼스칼라 프로세서, 혹은 슈퍼스칼라 CPU라고 부른다.
명령어 집합 유형
- CISC (Complex Instruction Set Computer)
- ex) CISC 기반 CPU : 인텔의 x86 혹은 x86-64 CPU
- 다채로운 기능을 지원하는 복잡한 명령어들로 구성된 명령어 집합
- 덕분에, 적은 수의 명령어로도 프로그램을 실행할 수 있다.
- 활용하는 명령어가 복잡하고 다양한 기능을 제공하는 탓에
- 명령어의 크기 및 실행되기까지의 시간이 일정하지 않고,
- 하나의 명령어 실행에 여러 클럭 주기가 필요하다.
- 그렇기에 파이프라이닝에 비효율적 - RISC (Reduuced Instruction Set Computer)
- ex) RISC 기반 CPU : 애플의 M1 CPU
- CISC에 비해 활용 가능한 명령어의 종류가 적다.
- 짧고 규격화된 명령어,
- 되도록이면 1클럭 내외로 실행되는 명령어를 지향
- 그렇기에, 같은 프로그램이더라도 CISC보다 더 많은 명령어가 필요하다.
- 또한 덕분에, 파이프라이닝에 최적화
파이프라인 위험(pipeline bazard)
파이프라이닝이 실패하여 성능 향상이 이루어지지 않은 상황을 뜻한다.
- 데이터 위험(data hazard)
- 명령어 간의 데이터 의존성에 의해 발생
- 어떤 명령어들은 동시에 처리할 수 없고,
- 이전 명령어를 끝까지 실행해야만 비로소 실행할 수 있다. - 제어 위험(control hazard)
- 프로그램 카운터의 갑작스러운 변화에 의해 발생
- PC는 기본적으로 1씩 증가하며, '현재 실행 중인 명령어의 다음 주소'로 갱신된다.
- 하지만 프로그램 실행의 흐름이 바뀌어 명령어가 실행되면서 PC 값에 갑작스러운 변화가 생기면, 미리 인출하거나 해석 중인 명령어들은 아무 쓸모가 없어진다. - 구조적 위험(structual hazard)
- 명령어들을 겹쳐 실행하는 과정에서 서로 다른 명령어가 동시에 ALU, 레지스터 등 같은 CPU 부품을 사용하려고 할 때 발생
- 자원 위험(resource hazard)라고도 한다.
참고 도서
이것이 컴퓨터 과학이다