컴퓨터 구조 운영체제 네트워크 자료구조 알고리즘 데이터베이스 내 학습
arch · 01

폰 노이만 구조

메모리CPU구조

폰 노이만 구조는 프로그램 내장 방식(Stored Program)을 기반으로 한 컴퓨터 설계 원칙입니다. 데이터와 명령어를 동일한 메모리에 저장하고, CPU가 순차적으로 명령어를 꺼내 실행하는 방식으로 현대 컴퓨터의 근간입니다.

핵심 구성

CPU

ALU(연산장치)와 CU(제어장치)로 구성. 명령어를 해석하고 연산을 수행합니다.

핵심 구성

메모리

데이터와 프로그램(명령어)을 동일한 공간에 저장합니다. 주소로 접근합니다.

핵심 구성

입출력 장치

사용자와 컴퓨터 간 데이터를 주고받는 인터페이스입니다.

핵심 구성

버스

CPU, 메모리, 입출력 장치를 연결하는 데이터 통로입니다. 데이터/주소/제어 버스로 구분됩니다.

프로그램 내장 방식 — 명령어를 메모리에 저장해두고 순서대로 실행합니다. 하드웨어 변경 없이 소프트웨어만 바꿔 다른 작업을 수행할 수 있습니다.
순차 실행 (Sequential Execution) — 명령어는 기본적으로 메모리 주소 순서대로 실행됩니다. 분기(Branch) 명령어가 있을 때만 순서를 바꿉니다.
폰 노이만 병목 (Von Neumann Bottleneck) — CPU와 메모리 사이의 단일 버스가 성능 제약이 됩니다. 캐시 메모리, 파이프라이닝이 이 문제를 완화합니다.
하버드 구조와의 차이 — 하버드 구조는 명령어 메모리와 데이터 메모리를 분리합니다. 현대 CPU는 내부적으로 하버드 구조를 쓰고 외부적으로 폰 노이만 구조를 씁니다.

폰 노이만 구조가 나온 배경

1945년 6월 존 폰 노이만이 정리한 「EDVAC 보고서 초안(First Draft of a Report on the EDVAC)」이 명령어와 데이터를 같은 메모리에 두는 프로그램 내장 방식 컴퓨터의 논리 설계를 처음으로 문서로 남겼습니다. 초기 전자식 계산기인 ENIAC은 작업을 바꾸려면 배선과 스위치를 다시 설정해야 했지만, 프로그램을 메모리에 넣어 두면 프로그램을 바꾸는 일이 메모리 내용을 바꾸는 일이 됩니다. 이 설계에는 J. 프레스퍼 에커트와 존 모클리도 기여해서, '폰 노이만 구조'라는 이름이 공정한지에 대한 논쟁이 있습니다.

자주 하는 오해

폰 노이만 구조는 이제 안 쓰는 옛날 구조다 — 오늘날 범용 컴퓨터도 대부분 프로그램 내장 방식입니다. 달라진 점은 그 위에 캐시, 파이프라이닝, 비순차 실행 같은 장치를 얹어 병목을 줄였다는 것입니다.
명령어와 데이터가 같은 메모리에 있으니 데이터도 실행될 수 있다 — 구조상 가능하지만 실제 시스템은 막습니다. 메모리 페이지마다 실행 권한을 따로 두는 NX 비트(Windows에서는 DEP)로 데이터 영역에서 코드가 실행되지 않게 하며, 버퍼 오버플로 공격을 어렵게 만드는 데 쓰입니다.
폰 노이만 병목은 메모리가 느려서 생기는 문제일 뿐이다 — 병목은 CPU와 메모리를 잇는 통로 하나로 명령어와 데이터가 모두 오가면서 생기는 전송량(대역폭)과 지연의 한계입니다. 존 배커스가 1977년 튜링상 강연에서 이 통로를 '폰 노이만 병목'이라고 부르면서 널리 알려졌습니다.
폰 노이만 구조와 하버드 구조 중 하나만 골라 쓴다 — 현대 CPU는 L1 캐시를 명령어용과 데이터용으로 나누되 메인 메모리는 하나로 두는 '수정된 하버드 구조'를 씁니다. 두 구조의 장점을 나눠 가진 셈입니다.

면접에서 이렇게 물어요

면접 질문

폰 노이만 구조의 핵심 특징은 무엇인가요?

프로그램 내장 방식(명령어와 데이터를 같은 메모리에 저장), 명령어의 순차 실행(분기 명령어만 순서를 바꿈), CPU·메모리·입출력 장치·버스로 이루어진 구성입니다.

면접 질문

폰 노이만 병목이란 무엇이고 어떻게 줄이나요?

CPU와 메모리 사이의 단일 통로가 처리량을 제한하는 현상입니다. 자주 쓰는 데이터를 CPU 가까이 두는 캐시 메모리, L1 명령어·데이터 캐시 분리, 파이프라이닝 등으로 완화합니다.

면접 질문

하버드 구조와는 무엇이 다른가요?

하버드 구조는 명령어 메모리와 데이터 메모리(그리고 버스)를 물리적으로 분리해 명령어와 데이터를 동시에 가져올 수 있습니다. 대신 구조가 복잡해져 마이크로컨트롤러 같은 곳에서 주로 쓰입니다.

면접 질문

프로그램 내장 방식의 장단점은 무엇인가요?

메모리 내용만 바꾸면 다른 프로그램을 실행할 수 있고, 운영체제가 프로그램을 데이터처럼 메모리에 적재할 수 있습니다. 반대로 데이터가 코드로 실행될 수 있어 실행 권한 제어(NX)가 필요합니다.

실제 시스템에서는

마이크로컨트롤러 — Microchip의 8비트 AVR 마이크로컨트롤러는 프로그램을 저장하는 플래시 메모리와 데이터를 저장하는 SRAM이 서로 다른 주소 공간에 있는 수정된 하버드 구조입니다.
범용 CPU — 리눅스의 lscpu 같은 도구는 L1 캐시를 명령어용(L1i)과 데이터용(L1d)으로 나누어 보여줍니다. CPU 안쪽은 분리하고 바깥의 메인 메모리는 하나로 쓰는 구성입니다.
운영체제 — Windows의 DEP를 비롯한 운영체제의 실행 방지 기능은 CPU가 NX(No-eXecute) 비트를 지원하면 이를 이용해 데이터 영역의 코드 실행을 막습니다.

함께 보면 좋은 토픽

CPU 구성 요소 — CPU 구성 요소 — 폰 노이만 구조의 CPU 안에서 ALU, CU, 레지스터가 어떻게 협력하는지 봅니다.
명령어 사이클 — 명령어 사이클 — 메모리에서 명령어를 꺼내 실행하는 반복 과정을 단계별로 봅니다.
캐시 메모리 — 캐시 메모리 — 폰 노이만 병목을 줄이는 대표적인 방법입니다.
메모리 계층 구조 — 메모리 계층 구조 — 레지스터부터 SSD까지 속도와 용량이 어떻게 나뉘는지 봅니다.
엔디언(빅/리틀) — 여러 바이트로 된 값을 메모리에 놓는 바이트 순서를 봅니다.

참고 자료

C — 명령어 패치·디코드·실행 시뮬레이션

// 폰 노이만 구조의 기본 동작 — Fetch → Decode → Execute

typedef struct {
int memory[256];   // 명령어 + 데이터 동일 메모리
int pc;           // Program Counter (다음 명령어 주소)
int acc;          // Accumulator (누산기)
} CPU;

void run(CPU *cpu) {
    while (1) {
        /* 1. FETCH — PC가 가리키는 메모리에서 명령어를 가져옴 */
        int instr = cpu->memory[cpu->pc++];

        /* 2. DECODE — 명령어 상위 비트로 연산 종류 판별 */
        int opcode  = (instr >> 8) & 0xFF;
        int operand = instr & 0xFF;

        /* 3. EXECUTE — 연산 수행 */
        if      (opcode == 0x01) cpu->acc  = cpu->memory[operand]; // LOAD
        else if (opcode == 0x02) cpu->memory[operand] = cpu->acc;  // STORE
        else if (opcode == 0x03) cpu->acc += cpu->memory[operand]; // ADD
        else if (opcode == 0xFF) break;                            // HALT
    }
}

PC(Program Counter)가 메모리 주소를 가리키고, 명령어를 꺼내(Fetch) → 해석하고(Decode) → 실행(Execute)하는 과정이 반복됩니다. 명령어와 데이터가 같은 memory[] 배열에 들어있는 것이 폰 노이만 구조의 핵심입니다.

Java — CPU-메모리 대역폭 병목 (폰 노이만 병목) 체감

// 폰 노이만 병목(Von Neumann Bottleneck): CPU와 메모리를 잇는 단일 버스의
// 처리량(bandwidth)이 CPU 연산 속도를 따라가지 못하는 현상.
// — John Backus, 1978 ACM Turing Award 강연에서 정의
//
// 아래 코드는 메모리 계층(캐시) 효과를 통해 병목을 간접 체감합니다.
// 캐시 미스가 잦을수록 CPU가 메인 메모리를 기다리는 시간이 늘어나
// CPU-메모리 간 대역폭 병목이 전체 성능을 제한하는 상황이 재현됩니다.

int[] data = new int[1024 * 1024];

// 순차 접근 — 공간 지역성 높음 → 캐시 히트율 높음 → 버스 대역폭 부담 적음 (빠름)
for (int i = 0; i < data.length; i++) {
    data[i] *= 2;
}

// 랜덤 접근 — 공간 지역성 없음 → 캐시 미스 빈발 → 매 연산마다 메인 메모리 접근
// → CPU가 버스를 통해 메모리를 기다리는 상태 반복 = 폰 노이만 병목 간접 체감
Random rand = new Random();
for (int i = 0; i < data.length; i++) {
    data[rand.nextInt(data.length)] *= 2;
}

폰 노이만 병목은 Backus(1978 ACM Turing Award)가 정의한 개념으로, CPU와 메모리를 잇는 단일 버스의 처리량이 CPU 연산 속도보다 훨씬 낮아 CPU가 데이터를 기다리며 낭비되는 현상을 가리킵니다. 이 코드는 메모리 계층(캐시) 효과를 통해 병목을 간접적으로 체감합니다. 순차 접근은 캐시가 데이터를 미리 가져와 버스 부하가 낮지만, 랜덤 접근은 캐시 미스가 빈발해 CPU가 매번 버스를 통해 메인 메모리를 기다리는 시간이 길어집니다. 캐시 미스 자체는 메모리 계층의 문제이며, 폰 노이만 병목(단일 버스 대역폭 한계)과 같은 개념은 아니지만 병목이 실제 성능에 미치는 영향을 체감하기 위한 예시입니다.

Q1

폰 노이만 구조에서 CPU, 메모리, 입출력장치가 공유하는 것은?

명령어와 데이터가 같은 통로를 지나갑니다.
폰 노이만 구조는 CPU, 메모리, I/O가 하나의 버스를 공유해 명령어와 데이터를 주고받습니다. 이 때문에 동시에 둘 다 전송할 수 없는 "폰 노이만 병목"이 발생합니다.

Q2

폰 노이만 구조의 대표적 한계로 꼽히는 것은?

버스가 하나뿐이라 생기는 문제입니다.
명령어 인출과 데이터 접근이 같은 버스를 다퉈야 해서 폰 노이만 병목(bottleneck)이라 부릅니다.

Q3

프로그램 내장 방식(stored-program concept)의 핵심은?

폰 노이만 구조 이전엔 프로그램을 배선으로 바꿔야 했습니다.
프로그램 내장 방식은 명령어(프로그램)를 데이터와 동일하게 메모리에 저장해두고 CPU가 순차적으로 읽어 실행합니다. 이 덕분에 프로그램 변경이 배선 재구성 없이 가능해졌습니다.