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

버스 구조

버스구조

버스(Bus)는 CPU, 메모리, I/O 장치가 데이터를 주고받는 공유 통신 경로입니다. 하나의 버스를 여러 장치가 공유하기 때문에 누가, 언제 버스를 사용할지 조율하는 중재(Arbitration) 메커니즘이 핵심입니다.

핵심 개념

Address Bus

CPU가 접근할 메모리 주소나 I/O 포트를 전달합니다. CPU에서 메모리·I/O 방향의 단방향 버스입니다(DMA 같은 다른 버스 마스터도 주소를 보냅니다). 주소선의 수가 주소 공간의 크기를 결정합니다.

핵심 개념

Data Bus

실제 데이터를 주고받는 통로입니다. CPU↔메모리, CPU↔I/O 모두 이 버스를 사용합니다. 양방향이며 버스 폭(8/16/32/64비트)이 한 번에 전송 가능한 데이터 크기를 결정합니다.

핵심 개념

Control Bus

Read/Write 신호, 인터럽트 요청(IRQ), 버스 요청(BR)/승인(BG) 등 제어 신호를 전달합니다. 양방향이며 버스 동작 전체를 지휘합니다.

핵심 개념

버스 중재 (Arbitration)

여러 장치가 동시에 버스를 요청할 때 우선순위를 결정합니다. 중앙집중식(Centralized)과 분산식(Distributed) 방식이 있으며, 중앙집중식은 하나의 Arbiter가 순서를 정하고, 분산식은 장치들이 스스로 정합니다.

핵심 개념

Bus Master / Slave

버스 사용권을 주도적으로 요청하는 Master(CPU, DMA)와 요청을 받아 응답하는 Slave(메모리, I/O)로 역할이 나뉩니다.

핵심 개념

DMA (Direct Memory Access)

I/O 장치가 CPU를 거치지 않고 메모리에 직접 접근하는 방식입니다. 데이터를 옮기는 동안 CPU가 다른 작업을 할 수 있습니다.

버스 대역폭 (Bandwidth) — 버스 폭(비트 수) × 클록 주파수 × 클럭당 전송 횟수로 결정됩니다. SDR(Single Data Rate)은 × 1, DDR(Double Data Rate)은 클럭 상승·하강 엣지 양쪽에서 전송해 × 2가 됩니다. 현대 메모리(DDR4/DDR5)는 모두 DDR 방식입니다. 병목이 되면 CPU가 빨라도 전체 성능이 제한될 수 있습니다.
동기식 vs 비동기식 버스 — 동기식은 공통 클록에 맞춰 동작해 단순하지만 느린 장치에 발목이 잡힙니다. 비동기식은 핸드셰이킹으로 각 장치 속도에 맞춥니다.
단일 버스 vs 다중 버스 — 단일 버스는 구조가 단순하지만 동시 전송이 불가합니다. 현대 시스템은 고속(CPU-메모리)과 저속(I/O) 버스를 분리해 병목을 줄입니다.
데이지 체인 중재 — Bus Grant 신호를 장치 체인으로 순서대로 전달합니다. 구조는 단순하지만 앞 장치가 우선순위를 독점할 수 있습니다.
PCIe / USB — PCIe, SATA 같은 현대 연결은 직렬 점대점 방식이고, USB도 직렬 연결입니다. 병렬 공유 버스와 달리 장치마다 전용 경로를 갖는 구조가 많습니다.

버스가 공유 경로인 이유

장치마다 서로 전용선을 깔면 장치가 늘 때마다 배선이 급격히 늘어납니다. 그래서 하나의 경로를 여러 장치가 나눠 쓰는 버스가 쓰였고, 그 대신 한 번에 한 장치만 보낼 수 있어서 누가 쓸지 정하는 중재가 필요해졌습니다. CPU와 메모리 사이의 병목은 폰 노이만 구조에서 다룬 문제와 이어집니다.

숫자로 확인하기

주소선 수와 주소 공간 — 주소선이 n개면 2ⁿ개의 주소를 구분합니다. 20개면 1MiB(2²⁰ 바이트), 32개면 4GiB(2³² 바이트)입니다. 바이트 단위로 주소를 매기는 경우의 계산입니다.
DDR4-3200의 이론 대역폭 — 초당 32억 번(3200 MT/s) 전송하고 한 번에 64비트(8바이트)를 보내므로 3200 × 8 = 25,600 MB/s, 즉 채널 하나당 이론상 최대 25.6 GB/s입니다. 실제 지속 대역폭은 이보다 낮습니다.
PCIe 3.0 x16의 이론 대역폭 — 레인 하나가 초당 80억 번(8 GT/s) 전송하고 128b/130b 인코딩을 쓰므로 레인당 약 0.985 GB/s, 레인 16개면 한 방향 약 15.75 GB/s입니다.

자주 하는 오해

64비트 CPU는 주소선이 64개다 — 레지스터와 데이터 처리 폭이 64비트라는 뜻이지, 실제로 연결된 주소선이 64개라는 뜻이 아닙니다. 지원하는 물리 주소 비트 수는 CPU 구현마다 다릅니다.
버스 폭이 넓으면 항상 빠르다 — 대역폭은 폭 × 클록 × 클록당 전송 횟수로 정해지고, 지연(요청에서 응답까지 걸리는 시간)은 별개의 지표입니다. 폭을 늘려도 지연이 줄지는 않습니다.
DDR은 클록 주파수를 두 배로 올린 것이다 — DDR은 클록의 상승 엣지와 하강 엣지에서 모두 전송해 클록당 전송 횟수를 두 배로 만든 방식입니다. 클록 자체를 두 배로 올린 것과는 다릅니다.
DMA를 쓰면 버스 중재가 필요 없다 — DMA 컨트롤러도 버스 마스터이므로 CPU와 버스를 두고 경쟁합니다. 중재가 필요한 이유 중 하나가 바로 DMA입니다.

면접에서 이렇게 물어요

면접 질문

동기식 버스와 비동기식 버스는 무엇이 다른가요?

동기식은 모든 장치가 공통 클록에 맞춰 동작해 제어가 단순하지만 가장 느린 장치에 속도가 묶입니다. 비동기식은 요청과 응답 신호를 주고받는 핸드셰이킹으로 각 장치 속도에 맞추지만 제어가 복잡합니다.

면접 질문

버스 중재 방식에는 어떤 것이 있나요?

중재자가 하나인 중앙집중식과 장치들이 스스로 정하는 분산식으로 나뉩니다. 데이지 체인은 승인 신호를 장치 줄을 따라 순서대로 넘기는 중앙집중식의 한 형태로, 구조가 단순하지만 체인 앞쪽 장치가 유리합니다.

면접 질문

PCIe는 왜 병렬 공유 버스 대신 직렬 점대점 연결을 쓰나요?

병렬 버스는 선 사이의 신호 도착 시간 차이(스큐)와 간섭 때문에 클록을 높이기 어렵다고 알려져 있습니다. 직렬 점대점 연결은 장치마다 전용 레인을 가져 공유로 인한 대기가 없고, 레인 수를 늘려 대역폭을 키웁니다.

면접 질문

대역폭과 지연의 차이는 무엇인가요?

대역폭은 단위 시간에 옮길 수 있는 데이터 양이고, 지연은 요청 하나가 끝나기까지 걸리는 시간입니다. 큰 파일을 옮길 때는 대역폭이, 작은 요청을 자주 보낼 때는 지연이 성능을 좌우합니다.

실제 시스템에서는

리눅스 PCI 장치 확인 — lspci -t로 버스, 브리지, 장치가 이어진 트리 구조를 볼 수 있습니다. 리눅스 커널 문서에 PCI 서브시스템 설명이 있습니다.
메모리 속도 확인 — dmidecode -t memory로 설치된 메모리 모듈의 형식과 속도(MT/s)를 볼 수 있습니다. 읽으려면 관리자 권한이 필요합니다.

함께 보면 좋은 토픽

I/O 방식 — 프로그램 I/O, 인터럽트, DMA가 버스를 어떻게 쓰는지 봅니다.
인터럽트 — 버스의 제어 신호로 전달되는 인터럽트 요청의 처리 과정을 봅니다.
메모리 계층 구조 — 버스 대역폭과 지연이 메모리 계층에서 어떤 의미인지 봅니다.
폰 노이만 구조 — CPU와 메모리 사이의 병목이 생기는 구조를 봅니다.

참고 자료

리눅스 PCI 서브시스템 — PCI Bus Subsystem — The Linux Kernel documentation
표준 — JEDEC, DDR4 SDRAM Standard (JESD79-4)
표준 — PCI-SIG, PCI Express Base Specification

C — 버스 읽기/쓰기 시뮬레이션

// 버스 구조 시뮬레이션 — Address / Data / Control Bus
#include <stdint.h>
#include <stdbool.h>

// 메인 메모리 (256 바이트)
#define MEM_SIZE 256
static uint8_t memory[MEM_SIZE];

// 버스 레지스터 — 실제 하드웨어의 버스 라인을 모델링
typedef struct {
    uint8_t  address_bus;   // Address Bus — 단방향 (CPU → 장치)
    uint8_t  data_bus;      // Data Bus    — 양방향
    bool     read_signal;   // Control Bus — READ 신호
    bool     write_signal;  // Control Bus — WRITE 신호
    bool     bus_request;   // Control Bus — BR (Bus Request)
    bool     bus_grant;     // Control Bus — BG (Bus Grant)
} Bus;

static Bus bus = {0};

// 메모리 읽기: Address Bus → READ 신호 → Data Bus로 반환
uint8_t bus_read(uint8_t addr) {
    bus.address_bus  = addr;   // 1. Address Bus에 주소 전송
    bus.read_signal  = true;  // 2. Control Bus — READ 신호 활성화
    bus.data_bus     = memory[bus.address_bus]; // 3. 메모리 → Data Bus
    bus.read_signal  = false; // 4. 신호 해제
    return bus.data_bus;
}

// 메모리 쓰기: Address Bus → Data Bus에 값 적재 → WRITE 신호
void bus_write(uint8_t addr, uint8_t val) {
    bus.address_bus  = addr;   // 1. Address Bus에 주소 전송
    bus.data_bus     = val;    // 2. Data Bus에 데이터 적재
    bus.write_signal = true;  // 3. Control Bus — WRITE 신호 활성화
    memory[bus.address_bus] = bus.data_bus; // 4. 메모리에 기록
    bus.write_signal = false; // 5. 신호 해제
}

// 버스 중재 — I/O 장치가 버스 요청 후 승인받아 DMA 수행
void dma_transfer(uint8_t dst, uint8_t src_data) {
    bus.bus_request = true;  // I/O: Bus Request (BR) 신호 전송

    // Arbiter가 버스 승인 (현재 CPU 전송 완료 후)
    bus.bus_grant   = true;  // Arbiter: Bus Grant (BG) 신호 전송
    bus.bus_request = false;

    // I/O가 버스 마스터로서 메모리에 직접 쓰기 (DMA)
    bus.address_bus  = dst;
    bus.data_bus     = src_data;
    bus.write_signal = true;
    memory[dst]      = src_data;
    bus.write_signal = false;

    bus.bus_grant = false;    // 버스 반환
}

Address Bus는 단방향이므로 항상 CPU(또는 현재 Bus Master)가 주소를 올립니다. Data Bus는 양방향이며 Read 시에는 메모리→CPU, Write 시에는 CPU→메모리 방향으로 흐릅니다. DMA 전송 시에는 I/O 장치가 Arbiter로부터 버스 사용권을 받아 직접 메모리에 접근합니다.

C — 데이지 체인 버스 중재 구현

// 데이지 체인(Daisy Chain) 버스 중재
// Bus Grant 신호를 장치 체인으로 순서대로 전달
#define NUM_DEVICES 4

typedef struct {
    int  id;
    bool requesting;  // 버스 요청 중인지 여부
    bool granted;     // 버스 사용권 획득 여부
} Device;

Device devices[NUM_DEVICES] = {
    { 0, false, false },  // 우선순위 최고
    { 1, false, false },
    { 2, false, false },
    { 3, false, false },  // 우선순위 최저
};

// Arbiter: Bus Grant를 체인 순서대로 전달
// 요청 중인 첫 번째 장치가 버스를 획득
int daisy_chain_arbitrate() {
    bool grant_available = true;  // 초기에 Grant 신호 활성화

    for (int i = 0; i < NUM_DEVICES; i++) {
        if (!grant_available) break;

        if (devices[i].requesting) {
            devices[i].granted = true;   // 버스 사용권 부여
            grant_available    = false;   // Grant 신호 차단 (이후 장치는 획득 불가)
            return i;
        }
        // 요청 없으면 Grant 신호를 다음 장치로 통과
    }
    return -1;  // 요청 장치 없음
}

// 버스 사용 완료 후 반환
void release_bus(int device_id) {
    devices[device_id].granted    = false;
    devices[device_id].requesting = false;
}

데이지 체인 중재는 Bus Grant 신호를 장치 0번부터 순서대로 통과시킵니다. 요청 중인 장치가 신호를 받으면 그 자리에서 버스를 획득하고 이후 장치로의 신호를 차단합니다. 구조가 단순한 대신 앞 번호 장치가 지속적으로 요청하면 뒷 장치는 기아(Starvation) 상태가 될 수 있습니다.

Q1

주소 버스(Address Bus)의 역할은?

'어디'를 지정하는 버스입니다.
주소 버스는 CPU가 접근하려는 메모리나 I/O 장치의 주소를 전달하는 역할을 합니다. 실제 값 전송은 데이터 버스가 담당합니다.

Q2

DMA(Direct Memory Access)를 사용하는 이유는?

CPU가 매번 데이터를 옮기면 비효율적입니다.
DMA 컨트롤러가 CPU를 거치지 않고 장치와 메모리 사이 데이터를 직접 옮겨, CPU는 그동안 다른 작업을 할 수 있습니다.

Q3

버스 중재(Bus Arbitration)가 필요한 이유는?

버스는 공유 자원이라 동시에 두 장치가 쓸 수 없습니다.
여러 장치가 동시에 버스 사용을 요청할 수 있으므로, 버스 중재기가 누가 먼저 버스를 사용할지 순서를 정해 충돌을 방지합니다.