포스트

[CS 기초 #2] 컴퓨터 구조: CPU, 메모리, 저장장치의 동작 원리 완벽 이해

[CS 기초 #2] 컴퓨터 구조: CPU, 메모리, 저장장치의 동작 원리 완벽 이해

컴퓨터 속을 들여다봅시다! CPU, 메모리, 저장장치가 어떻게 협력하여 여러분의 코드를 실행하는지 알면 성능 최적화의 길이 보입니다.

🎯 이 글을 읽고 나면

  • 폰 노이만 구조와 현대 컴퓨터의 기본 설계를 이해합니다
  • CPU의 Fetch-Decode-Execute 사이클을 설명할 수 있습니다
  • 메모리 계층구조와 캐시의 동작 원리를 알게 됩니다
  • HDD와 SSD의 차이점과 성능 특성을 비교할 수 있습니다
  • 캐시 친화적인 코드를 작성할 수 있습니다

📚 사전 지식

  • 기본 프로그래밍 경험: 변수, 배열 등 기본 개념 이해
  • CS 기초 개념: 이전 글: CS 입문을 읽으면 더 좋습니다
  • 이진수는 다음 글에서! 지금은 몰라도 괜찮습니다

💡 핵심 개념 미리보기

컴퓨터는 CPU(연산), 메모리(임시 저장), 저장장치(영구 저장)라는 세 가지 핵심 구성요소로 이루어져 있습니다. CPU는 메모리에서 명령어를 가져와(Fetch) 해석하고(Decode) 실행(Execute)하는 사이클을 반복합니다. 메모리는 속도와 용량의 트레이드오프를 고려한 계층 구조로 설계되어 있으며, 이를 이해하면 더 빠른 코드를 작성할 수 있습니다!


🏗️ 들어가며: 컴퓨터는 어떻게 동작하는가?

우리가 작성한 코드 한 줄이 실제로 어떻게 실행되는지 궁금하신가요? 오늘은 컴퓨터의 핵심 구성요소인 CPU, 메모리, 저장장치가 어떻게 동작하고 서로 협력하는지 알아보겠습니다.

이 지식은 단순한 이론이 아닙니다. 성능 최적화, 메모리 관리, 시스템 설계에 직접적으로 활용됩니다!

📐 폰 노이만 구조: 현대 컴퓨터의 기본 설계

1945년 폰 노이만이 제안한 이 구조는 현재까지도 대부분의 컴퓨터가 따르는 기본 설계입니다.

graph TB
    subgraph "폰 노이만 구조"
        CPU[CPU<br/>중앙처리장치]
        Memory[Memory<br/>주기억장치]
        Input[Input<br/>입력장치]
        Output[Output<br/>출력장치]
        
        CPU <--> Memory
        Input --> CPU
        CPU --> Output
    end
    
    subgraph "CPU 내부"
        CU[Control Unit<br/>제어장치]
        ALU[ALU<br/>산술논리장치]
        REG[Registers<br/>레지스터]
        
        CU --> ALU
        CU --> REG
        ALU <--> REG
    end

핵심 특징

  1. 프로그램 내장 방식: 프로그램과 데이터를 같은 메모리에 저장
  2. 순차적 실행: 명령어를 하나씩 순서대로 실행
  3. 이진 시스템: 모든 정보를 0과 1로 표현
🔰 초보자를 위한 비유

컴퓨터를 공장에 비유해봅시다!

  • CPU (중앙처리장치): 공장의 작업자. 설계도(프로그램)를 보고 실제로 일을 합니다
  • 메모리 (RAM): 작업대. 지금 당장 사용할 재료와 도구를 올려놓습니다 (전원이 꺼지면 사라짐!)
  • 저장장치 (HDD/SSD): 창고. 모든 자료를 영구적으로 보관합니다
  • 캐시: 손이 닿는 곳의 소형 작업대. 자주 쓰는 것만 올려놓아 빠르게 접근

작업자(CPU)가 창고(저장장치)까지 가려면 시간이 오래 걸리지만, 작업대(메모리)나 손 닿는 곳(캐시)에 있으면 금방 가져올 수 있습니다!

🧠 CPU (Central Processing Unit)

CPU는 컴퓨터의 두뇌입니다. 모든 연산과 제어를 담당합니다.

CPU의 구성 요소

graph LR
    subgraph "CPU 구조"
        subgraph "제어 장치"
            PC[Program Counter]
            IR[Instruction Register]
            Decoder[명령어 해석기]
        end
        
        subgraph "산술논리장치"
            Adder[덧셈기]
            Logic[논리 연산기]
            Comparator[비교기]
        end
        
        subgraph "레지스터"
            GPR[범용 레지스터]
            SP[스택 포인터]
            ACC[누산기]
        end
        
        subgraph "캐시"
            L1[L1 캐시<br/>32KB]
            L2[L2 캐시<br/>256KB]
            L3[L3 캐시<br/>8MB]
        end
    end

CPU 명령어 실행 사이클 (Fetch-Decode-Execute)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# CPU 동작을 시뮬레이션하는 간단한 예제
class SimpleCPU:
    def __init__(self):
        self.pc = 0  # Program Counter
        self.registers = [0] * 8  # 8개의 레지스터
        self.memory = [0] * 256  # 256바이트 메모리
        
    def fetch(self):
        """메모리에서 명령어를 가져옴"""
        instruction = self.memory[self.pc]
        self.pc += 1
        return instruction
    
    def decode(self, instruction):
        """명령어를 해석"""
        opcode = instruction >> 4  # 상위 4비트: 명령 코드
        operand = instruction & 0xF  # 하위 4비트: 피연산자
        return opcode, operand
    
    def execute(self, opcode, operand):
        """명령어를 실행"""
        if opcode == 0x1:  # LOAD
            self.registers[0] = self.memory[operand]
        elif opcode == 0x2:  # ADD
            self.registers[0] += self.registers[operand]
        elif opcode == 0x3:  # STORE
            self.memory[operand] = self.registers[0]
        # ... 더 많은 명령어들
    
    def run_cycle(self):
        """한 사이클 실행"""
        instruction = self.fetch()
        opcode, operand = self.decode(instruction)
        self.execute(opcode, operand)

# 사용 예시
cpu = SimpleCPU()
# 메모리에 프로그램 로드
cpu.memory[0] = 0x10  # LOAD 0 (메모리 0번지 값을 레지스터에 로드)
cpu.memory[1] = 0x21  # ADD 1 (레지스터 1번과 더하기)
cpu.memory[2] = 0x30  # STORE 0 (결과를 메모리 0번지에 저장)

현대 CPU의 고급 기능

1. 파이프라이닝 (Pipelining)

여러 명령어를 동시에 처리하여 성능 향상

1
2
3
4
시간 →
명령어1: [Fetch][Decode][Execute][Memory][WriteBack]
명령어2:        [Fetch][Decode][Execute][Memory][WriteBack]
명령어3:               [Fetch][Decode][Execute][Memory][WriteBack]

2. 분기 예측 (Branch Prediction)

조건문의 결과를 미리 예측하여 파이프라인 효율성 증대

3. 멀티코어 (Multi-core)

여러 개의 CPU 코어로 병렬 처리

💾 메모리 (Memory)

메모리는 데이터와 프로그램을 임시로 저장하는 공간입니다.

메모리 계층 구조

graph TD
    subgraph "메모리 계층구조"
        REG[레지스터<br/>1ns, 수십 바이트]
        L1[L1 캐시<br/>1-2ns, 32KB]
        L2[L2 캐시<br/>3-10ns, 256KB]
        L3[L3 캐시<br/>10-20ns, 8MB]
        RAM[주 메모리 RAM<br/>50-100ns, 16GB]
        SSD[SSD<br/>10-100μs, 512GB]
        HDD[HDD<br/>5-10ms, 2TB]
        
        REG --> L1
        L1 --> L2
        L2 --> L3
        L3 --> RAM
        RAM --> SSD
        SSD --> HDD
    end
    
    style REG fill:#ff9999
    style L1 fill:#ffcc99
    style L2 fill:#ffff99
    style L3 fill:#ccff99
    style RAM fill:#99ffcc
    style SSD fill:#99ccff
    style HDD fill:#cc99ff

메모리 접근 속도와 용량의 트레이드오프

메모리 종류 접근 시간 용량 가격/GB
레지스터 1ns 수십 바이트 -
L1 캐시 1-2ns 32KB $$$
L2 캐시 3-10ns 256KB $$
L3 캐시 10-20ns 8MB $
RAM 50-100ns 16GB $
SSD 10-100μs 512GB ¢
HDD 5-10ms 2TB ¢

캐시의 동작 원리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class CacheSimulator:
    def __init__(self, cache_size=4):
        self.cache = {}  # 캐시 메모리
        self.cache_size = cache_size
        self.hits = 0
        self.misses = 0
        self.access_order = []  # LRU를 위한 접근 순서
    
    def read(self, address):
        """메모리 읽기 시뮬레이션"""
        if address in self.cache:
            # 캐시 히트
            self.hits += 1
            # LRU 업데이트
            self.access_order.remove(address)
            self.access_order.append(address)
            return self.cache[address], "HIT"
        else:
            # 캐시 미스
            self.misses += 1
            # 메모리에서 데이터 로드 (시뮬레이션)
            data = f"Data at {address}"
            
            # 캐시가 가득 찬 경우 LRU 제거
            if len(self.cache) >= self.cache_size:
                lru_address = self.access_order.pop(0)
                del self.cache[lru_address]
            
            # 새 데이터를 캐시에 추가
            self.cache[address] = data
            self.access_order.append(address)
            return data, "MISS"
    
    def get_hit_rate(self):
        total = self.hits + self.misses
        return (self.hits / total * 100) if total > 0 else 0

# 캐시 동작 테스트
cache = CacheSimulator(cache_size=3)
addresses = [1, 2, 3, 1, 4, 1, 2, 5, 1]

for addr in addresses:
    data, status = cache.read(addr)
    print(f"주소 {addr}: {status}")

print(f"\n캐시 적중률: {cache.get_hit_rate():.1f}%")

가상 메모리 (Virtual Memory)

물리적 RAM보다 큰 프로그램을 실행할 수 있게 해주는 기술입니다.

graph LR
    subgraph "가상 메모리"
        VP1[가상 페이지 1]
        VP2[가상 페이지 2]
        VP3[가상 페이지 3]
        VP4[가상 페이지 4]
    end
    
    subgraph "물리 메모리"
        PP1[물리 페이지 1]
        PP2[물리 페이지 2]
        PP3[물리 페이지 3]
    end
    
    subgraph "디스크"
        DP1[스왑 영역]
    end
    
    VP1 --> PP1
    VP2 --> PP3
    VP3 --> PP2
    VP4 --> DP1

🗄️ 저장장치 (Storage)

저장장치는 데이터를 영구적으로 보관하는 장치입니다.

HDD vs SSD 비교

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 저장장치 성능 비교 시뮬레이션
import time
import random

class StorageDevice:
    def __init__(self, name, seek_time, transfer_rate):
        self.name = name
        self.seek_time = seek_time  # ms
        self.transfer_rate = transfer_rate  # MB/s
    
    def read_sequential(self, size_mb):
        """순차 읽기 시간 계산"""
        return self.seek_time + (size_mb / self.transfer_rate * 1000)
    
    def read_random(self, count, size_kb):
        """랜덤 읽기 시간 계산"""
        total_time = 0
        for _ in range(count):
            total_time += self.seek_time
            total_time += (size_kb / 1024 / self.transfer_rate * 1000)
        return total_time

# 저장장치 정의
hdd = StorageDevice("HDD", seek_time=10, transfer_rate=150)
ssd = StorageDevice("SSD", seek_time=0.1, transfer_rate=550)

# 성능 비교
print("100MB 순차 읽기:")
print(f"HDD: {hdd.read_sequential(100):.1f}ms")
print(f"SSD: {ssd.read_sequential(100):.1f}ms")

print("\n1000개의 4KB 랜덤 읽기:")
print(f"HDD: {hdd.read_random(1000, 4):.1f}ms")
print(f"SSD: {ssd.read_random(1000, 4):.1f}ms")

RAID (Redundant Array of Independent Disks)

여러 디스크를 조합하여 성능과 안정성을 향상시키는 기술

graph TB
    subgraph "RAID 0 - Striping"
        D1A[디스크1<br/>A C E]
        D2A[디스크2<br/>B D F]
    end
    
    subgraph "RAID 1 - Mirroring"
        D1B[디스크1<br/>A B C]
        D2B[디스크2<br/>A B C]
    end
    
    subgraph "RAID 5 - Distributed Parity"
        D1C[디스크1<br/>A D P]
        D2C[디스크2<br/>B P E]
        D3C[디스크3<br/>P C F]
    end

🔄 시스템 버스: 구성요소들의 고속도로

graph LR
    subgraph "시스템 버스"
        CPU[CPU]
        Memory[메모리]
        IO[I/O 장치]
        
        CPU <--> DataBus[데이터 버스<br/>데이터 전송]
        CPU <--> AddressBus[주소 버스<br/>위치 지정]
        CPU <--> ControlBus[제어 버스<br/>명령 전달]
        
        DataBus <--> Memory
        AddressBus --> Memory
        ControlBus --> Memory
        
        DataBus <--> IO
        AddressBus --> IO
        ControlBus --> IO
    end

💡 실무에서의 활용

1. 성능 최적화

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 캐시 친화적인 코드 vs 캐시 비친화적인 코드

# 나쁜 예: 캐시 미스가 많이 발생
def matrix_sum_bad(matrix):
    total = 0
    rows = len(matrix)
    cols = len(matrix[0])
    # 열 우선 순회 - 캐시 비효율적
    for j in range(cols):
        for i in range(rows):
            total += matrix[i][j]
    return total

# 좋은 예: 캐시 히트율이 높음
def matrix_sum_good(matrix):
    total = 0
    # 행 우선 순회 - 캐시 효율적
    for row in matrix:
        for value in row:
            total += value
    return total

# 메모리는 행 단위로 연속 저장되므로
# 행 우선 순회가 캐시 지역성을 활용

2. 메모리 관리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 메모리 풀링으로 할당/해제 오버헤드 감소
class MemoryPool:
    def __init__(self, object_class, pool_size=100):
        self.object_class = object_class
        self.pool = [object_class() for _ in range(pool_size)]
        self.available = list(self.pool)
        self.in_use = []
    
    def acquire(self):
        if self.available:
            obj = self.available.pop()
            self.in_use.append(obj)
            return obj
        else:
            # 풀이 비어있으면 새로 생성
            obj = self.object_class()
            self.in_use.append(obj)
            return obj
    
    def release(self, obj):
        if obj in self.in_use:
            self.in_use.remove(obj)
            self.available.append(obj)
            # 객체 초기화
            obj.reset()

3. 병렬 처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import multiprocessing

def cpu_bound_task(n):
    """CPU 집약적 작업"""
    total = 0
    for i in range(n):
        total += i * i
    return total

# 멀티코어 활용
def parallel_processing():
    # CPU 코어 수 확인
    num_cores = multiprocessing.cpu_count()
    print(f"사용 가능한 CPU 코어: {num_cores}")
    
    # 작업을 코어 수만큼 분할
    with multiprocessing.Pool(num_cores) as pool:
        tasks = [10000000] * num_cores
        results = pool.map(cpu_bound_task, tasks)
    
    return sum(results)

🎯 핵심 정리

기억해야 할 5가지

  1. CPU는 Fetch-Decode-Execute 사이클로 동작
  2. 메모리 계층구조는 속도와 용량의 트레이드오프
  3. 캐시는 자주 사용하는 데이터를 빠르게 접근 가능하게 함
  4. 가상 메모리로 물리 메모리보다 큰 프로그램 실행 가능
  5. 병렬 처리로 멀티코어 CPU 성능 활용

성능 개선 팁

  • 데이터 지역성을 고려한 코드 작성
  • 캐시 친화적인 알고리즘 선택
  • 메모리 할당/해제 최소화
  • CPU 집약적 작업은 병렬 처리

📚 더 깊이 공부하기

추천 자료

  • “Computer Organization and Design” - Patterson & Hennessy
  • “What Every Programmer Should Know About Memory” - Ulrich Drepper
  • Intel/AMD CPU 매뉴얼 (고급)

실습 프로젝트

  1. 간단한 CPU 에뮬레이터 만들기
  2. 캐시 시뮬레이터 구현
  3. 메모리 관리자 작성

🚀 다음 시간 예고

다음 포스트에서는 “이진수와 논리 게이트”를 다룹니다. 컴퓨터가 0과 1만으로 어떻게 복잡한 연산을 수행하는지, 논리 게이트가 어떻게 동작하는지 알아보겠습니다!

✅ 이 글에서 배운 것

스스로 확인해보세요! 각 항목을 설명할 수 있다면 체크하세요.

개념 이해

  • 폰 노이만 구조의 핵심 특징 3가지 (프로그램 내장, 순차 실행, 이진 시스템)
  • CPU의 Fetch-Decode-Execute 사이클 동작 방식
  • 메모리 계층구조: 레지스터 → 캐시 → RAM → SSD → HDD
  • 캐시 히트(Hit)와 미스(Miss)의 개념
  • 가상 메모리가 필요한 이유

실용적 이해

  • 왜 행 우선 순회가 캐시 친화적인지 설명할 수 있다
  • SSD가 HDD보다 랜덤 접근에서 훨씬 빠른 이유를 안다
  • 멀티코어 CPU를 활용하는 방법 (병렬 처리)
  • 메모리 풀링이 성능을 개선하는 원리

실습 완료

  • SimpleCPU 시뮬레이터 코드를 읽고 이해했다
  • CacheSimulator로 LRU 캐시 동작을 확인했다
  • 캐시 친화적 코드 vs 비친화적 코드의 차이를 이해했다
  • HDD와 SSD 성능 비교 코드를 실행해봤다

📌 시리즈 네비게이션

순서 제목 링크
1 CS 입문: 컴퓨터 사이언스가 무엇인가? ← 이전 글
2 컴퓨터 구조: CPU, 메모리, 저장장치의 동작 원리 현재 글
3 이진수와 논리 게이트: 컴퓨터가 생각하는 방법 다음 글 →
4 운영체제 기초: 프로세스, 스레드, 메모리 관리 준비중
5 네트워크 기초: OSI 7계층, TCP/IP 이해하기 준비중
📋 전체 시리즈 목차 보기

기초 이론편

  1. CS 입문 ✅
  2. 컴퓨터 구조 ✅
  3. 이진수와 논리 게이트
  4. 운영체제 기초
  5. 네트워크 기초

자료구조편

  1. 배열과 연결 리스트
  2. 스택과 큐
  3. 트리 구조
  4. 그래프
  5. 해시 테이블

알고리즘편

  1. 복잡도 분석
  2. 정렬 알고리즘
  3. 탐색 알고리즘
  4. 동적 계획법
  5. 그리디와 분할정복

실무 응용편

  1. 데이터베이스 이론
  2. 소프트웨어 공학
  3. 보안 기초
  4. 분산 시스템
  5. CS 종합과 면접 준비
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.