Шина -- «система автомагистралей» внутри компьютера

Различные компоненты внутри компьютера должны постоянно обмениваться информацией. CPU должен извлекать команды из памяти и читать/записывать данные. Как передаётся эта информация? Ответ —шина。

Шина — это набор общих линий связи, соединяющих CPU, память, жёсткий диск, видеокарту и другие устройства, точно как дорожная система города — с магистралями и второстепенными дорогами, каждая из которых имеет чёткое назначение.

Эта лекция поможет вам понять разделение труда между тремя типами шин и с помощью Python смоделировать полную операцию чтения памяти.


Аналогия с системой автомагистралей: три вида дорог, три вида использования

Представьте, что вы доставляете посылки в большом городе. Курьеру нужно знать три вещи:куда доставить、Что отправить?、Нужна ли получателю подпись?。

Внутри компьютера также есть три совершенно аналогичные «магистрали»:

Тип шиныОсобенностиНаправление передачианалогия из жизни
Шина адресаАдрес памяти, к которому необходимо обратиться для передачиCPU → Память (односторонняя)«Номер дома» в накладной доставки — сообщающий получателю «я ищу этот адрес»
Шина данныхПередача фактического содержимого данныхдвунаправленная«Посылка» в руках курьера — сам предмет, который можно отправить или получить
Шина управленияПередача управляющих сигналов, таких как чтение/запись и прерыванияCPU ↔ Компоненты (двунаправленная)«Квитанция» — сообщающая другой стороне, является ли это в данный момент получением или доставкой, и завершена ли передача

Ширина шины адреса определяет, сколько памяти может адресовать CPU. 32-битная шина адреса может адресовать 2^32 = 4GB памяти, а 64-битная шина адреса теоретически может адресовать 2^64 байт (около 16EB). Именно поэтому 32-битная операционная система может использовать не более 4GB памяти.


Полный процесс операции чтения памяти

Предположим, CPU нужно прочитать фрагмент данных из адреса памяти 100. Весь процесс делится на 4 шага, при этом три шины работают совместно:

Разбор шагов

  1. CPU выставляет адрес: CPU помещает значение адреса 100 нашину адреса. Теперь память получила адрес.
  2. CPU посылает сигнал: CPU нашине управленияподает сигнал "чтение" (READ). Память понимает, что другая сторона хочет что-то получить.
  3. Память выдает данные: После получения адреса и сигнала чтения память находит данные, хранящиеся по адресу 100, и помещает их нашину данных.
  4. CPU принимает данные: CPU считывает данные с шины данных и сохраняет их во внутренний регистр. Передача завершена.

диаграмма последовательности

Следующая временная диаграмма в ASCII показывает изменения состояния шин во время выполнения этой инструкции:

时间  |   地址总线       控制总线        数据总线
------|------------------------------------------
T0    |   100           IDLE            XXXX       ← CPU 放上地址
T1    |   100           READ            XXXX       ← CPU 发出读信号
T2    |   100           READ            0x2A       ← 内存放上数据
T3    |   XXXX          IDLE            0x2A       ← CPU 取走数据
------|------------------------------------------
结果: CPU 从地址 100 读到了数据 0x2A

Демонстрация кода: моделирование процесса взаимодействия шин с помощью Python

Следующий код на Python полностью моделирует процесс совместной работы шины адреса, шины данных и шины управления во время операции чтения памяти.

Пример

"""
Симуляция взаимодействия шин — полная операция чтения памяти (демонстрационный пример)

Эта программа моделирует полный процесс чтения данных из памяти процессором через три шины:
1. Шина адреса: процессор выдаёт адрес памяти для доступа
2. Шина управления: процессор выдаёт сигналы управления чтением/записью
3. Шина данных: память возвращает/принимает данные

При каждом изменении состояния шины выводится подробный журнал, что позволяет легко наблюдать совместную работу трёх шин.
"""


import time


class AddressBus:
    """Шина адреса — однонаправленная: процессор отправляет адрес в память"""
    def __init__(self):
        self.value = None  # Текущее значение адреса на шине
        self.width = 16    # 16-битная шина адреса, адресация до 64KB

    def set_address(self, addr):
        """Процессор выставляет адрес на шину адреса"""
        self.value = addr
        print(f" [Шина адресов] Процессор выставляет адрес: {addr} (0x{addr:04X})")
        return addr

    def clear(self):
        """Очистить шину"""
        self.value = None
        print(f[Шина адресов] Шина очищена, переход в состояние простоя)


class DataBus:
    """Шина данных — двунаправленная: и процессор, и память могут отправлять и принимать данные"""
    def __init__(self):
        self.value = None  # Текущее значение данных на шине
        self.width = 8     # 8-битная шина данных, передаёт один байт за раз
        self.direction = None  # 'TO_CPU' или 'TO_MEMORY'

    def send_from_cpu(self, data):
        """ЦП отправляет данные на шину"""
        self.value = data
        self.direction = 'TO_MEMORY'
        print(f" [Шина данных] ЦП → Шина, Данные: {data} (0x{data:02X})")
        return data

    def send_from_memory(self, data):
        Память отправляет данные на шину
        self.value = data
        self.direction = 'TO_CPU'
        print(f[Шина данных] Память → Шина, данные: {data} (0x{data:02X}))
        return data

    def read_to_cpu(self):
        """ЦП читает данные с шины данных"""
        data = self.value
        print(f[Шина данных] ЦП считывает данные из шины: {data} (0x{data:02X}))
        self.clear()
        return data

    def read_to_memory(self):
        """Память считывает данные с шины данных (используется во время операций записи)"""
        data = self.value
        print(f[Шина данных] Память считывает данные из шины: {data} (0x{data:02X}))
        self.clear()
        return data

    def clear(self):
        """Очистить шину"""
        self.value = None
        self.direction = None
        print(f[Шина данных] Шина очищена, переход в состояние простоя)


class ControlBus:
    """Шина управления — двунаправленная: передаёт сигналы чтения/записи, сигналы прерываний и т. д."""
    # Тип управляющего сигнала
    READ = 'MEM_READ'        # Сигнал чтения памяти
    WRITE = 'MEM_WRITE'      # Сигнал записи в память
    IDLE = 'IDLE'            # Простой

    def __init__(self):
        self.signal = self.IDLE

    def set_signal(self, signal):
        """Установить управляющие сигналы"""
        self.signal = signal
        signal_name = {
            self.READ: 'Чтение памяти (MEM_READ)',
            self.WRITE: 'Запись в память (MEM_WRITE)',
            self.IDLE: 'Простой (IDLE)'
        }
        print(f[Шина управления] ЦП отправляет сигнал: {signal_name.get(signal, signal)})

    def clear(self):
        """Сбросить сигнал"""
        self.signal = self.IDLE
        print(f[Шина управления] Сигналы сброшены, переход в режим ожидания)


class SimpleMemory:
    """Упрощённый модуль памяти — 256 байт пространства для хранения"""
    def __init__(self, size=256):
        self.size = size
        self.data = {}
        # Инициализация демонстрационных данных
        for i in range(size):
            self.data[i] = i % 256  # Адрес i хранит значение i % 256
        # Разместить несколько специальных значений по определённым адресам для демонстрации
        self.data[100] = 42         # Адрес 100: сохранить 42 (0x2A)
        self.data[200] = 0xAB       # Адрес 200: сохранить 0xAB (171)
        self.data[50] = ord('R')    # Адрес 50: хранит ASCII-код буквы 'R'
        self.data[51] = ord('U')
        self.data[52] = ord('N')
        self.data[53] = ord('O')
        self.data[54] = ord('O')
        self.data[55] = ord('B')

    def read(self, addr):
        Чтение данных по указанному адресу
        if 0 <= addr < self.size:
            return self.data.get(addr, 0)
        else:
            raise ValueError(fАдрес {addr} выходит за пределы диапазона памяти (0-{self.size-1}))

    def write(self, addr, data):
        Запись данных по указанному адресу
        if 0 <= addr < self.size:
            self.data[addr] = data
        else:
            raise ValueError(fАдрес {addr} выходит за пределы диапазона памяти (0-{self.size-1}))


class CPU:
    """Упрощённый CPU - взаимодействует с памятью через шину"""
    def __init__(self, addr_bus, data_bus, ctrl_bus, memory):
        self.addr_bus = addr_bus   # Шина адреса
        self.data_bus = data_bus   # Шина данных
        self.ctrl_bus = ctrl_bus   # Шина управления
        self.memory = memory       # Память
        # Внутренние регистры процессора
        self.mdr = 0               # Регистр данных (Регистр данных памяти)
        self.mar = 0               # Регистр адреса (Регистр адреса памяти)

    def memory_read(self, addr):
        """
Выполнить полную операцию чтения памяти

Процесс:
T0: MAR ← addr (внутренняя подготовка)
T1: шина адреса ← addr, шина управления ← READ
T2: Память читает данные, шина данных ← data
T3: MDR ← шина данных, очистить все шины

Возврат: значение считанных данных
        """

        print(f"\n{'='*55}")
        print(fНачало операции: CPU читает данные из адреса памяти {addr})
        print(f"{'='*55}")

        # T0: внутренняя подготовка CPU — загрузить адрес в MAR
        self.mar = addr
        print(f"\n[T0 - внутренняя подготовка CPU]")
        print(f[Внутренние операции CPU] MAR (регистр адреса памяти) ← {addr})

        # T1: CPU помещает адрес на шину адреса и одновременно выдает сигнал чтения на шину управления.
        print(f"\n[T1 - Выдача адреса и сигнала чтения]")
        self.addr_bus.set_address(addr)
        self.ctrl_bus.set_signal(ControlBus.READ)

        # Имитировать задержку передачи сигнала
        time.sleep(0.001)

        # T2: ответ памяти — по адресу на шине адреса найти данные и поместить их на шину данных
        print(f"\n[T2 - Ответ памяти]")
        data = self.memory.read(addr)
        self.data_bus.send_from_memory(data)

        # Имитировать задержку передачи сигнала
        time.sleep(0.001)

        # T3: ЦП считывает данные с шины данных в MDR. Шины очищаются.
        print(f"\n[T3 - ЦП получает данные]")
        self.mdr = self.data_bus.read_to_cpu()
        self.ctrl_bus.clear()
        self.addr_bus.clear()

        # Вывести результат
        print(f"\n{'='*55}")
        print(f"Операция завершена: fromAddress {addr} (0x{addr:04X}) чтение toData {self.mdr} (0x{self.mdr:02X})")
        print(f"{'='*55}")

        return self.mdr

    def memory_write(self, addr, data):
        """
Выполнить полную операцию записи в память

Процесс:
        T0: MAR ← addr, MDR ← data
T1: Address Bus ← addr, Data Bus ← data, Control Bus ← WRITE
T2: Память принимает данные из шины данных и записывает по адресу addr
T3: Очистить все шины
        """

        print(f"\n{'='*55}")
        print(fНачало операции: CPU записывает данные {data} в адрес памяти {addr})
        print(f"{'='*55}")

        # T0: внутренняя подготовка CPU
        self.mar = addr
        self.mdr = data
        print(f"\n[T0 - внутренняя подготовка CPU]")
        print(f" [внутренний CPU] MAR ← {addr}, MDR ← {data}")

        # T1: CPU помещает адрес и данные на шину и выдаёт сигнал записи
        print(f"\n[T1 - выдать адрес, данные и сигнал записи]")
        self.addr_bus.set_address(addr)
        self.data_bus.send_from_cpu(data)
        self.ctrl_bus.set_signal(ControlBus.WRITE)

        time.sleep(0.001)

        # T2: Память берёт данные с шины данных и записывает их
        print(f"\n[T2 - Запись в память]")
        received = self.data_bus.read_to_memory()
        self.memory.write(addr, received)
        print(f" [Внутри памяти] Адрес {addr} (0x{addr:04X}) ← {received} (0x{received:02X})")

        time.sleep(0.001)

        # T3: Очистить все шины
        print(f"\n[T3 - Очистить шину]")
        self.ctrl_bus.clear()
        self.addr_bus.clear()

        print(f"\n{'='*55}")
        print(f"Операция завершена: {data} записано по адресу {addr}")
        print(f"{'='*55}")


# ===== Главная программа: запуск демонстрации =====
if __name__ == "__main__":
    print("=" * 60)
    print(Моделирование взаимодействия шин — ПРИМЕР Демонстрация принципов организации компьютера)
    print("=" * 60)
    print()

    # 1. Инициализация шины
    print("[Инициализация системы] Созданы шина адреса, шина данных, шина управления, модуль памяти")
    addr_bus = AddressBus()
    data_bus = DataBus()
    ctrl_bus = ControlBus()
    memory = SimpleMemory()
    print()

    # 2. Просмотр начального состояния памяти
    print("[Начальное состояние памяти] Предварительный просмотр данных по ключевым адресам:")
    for addr in [50, 51, 52, 53, 54, 55, 100, 200]:
        ch = chr(memory.data[addr]) if 32 <= memory.data[addr] < 127 else '?'
        print(f" Адрес {addr} (0x{addr:04X}): {memory.data[addr]:3d} (0x{memory.data[addr]:02X}) '{ch}'")
    print()

    # 3. Инициализация CPU
    cpu = CPU(addr_bus, data_bus, ctrl_bus, memory)

    # 4. Демо: Чтение данных из памяти
    print("\n" + "▼" * 60)
    print(Демо 1: Чтение данных из адреса памяти 100)
    print("▼" * 60)
    val1 = cpu.memory_read(100)

    print("\n" + "▼" * 60)
    print(Демо 2: Чтение данных из адреса памяти 200)
    print("▼" * 60)
    val2 = cpu.memory_read(200)

    print("\n" + "▼" * 60)
    print(Демо 3: Последовательное чтение из адресов памяти 50-55 для сборки символов (ПРИМЕР))
    print("▼" * 60)
    chars = []
    for addr in range(50, 56):
        val = cpu.memory_read(addr)
        chars.append(chr(val))
    print(f"\n"Собранная строка: {''.join(chars)}")

    # 5. Демонстрация: запись данных в память
    print("\n" + "▼" * 60)
    print("Демонстрация 4: запись данных 77 в адрес памяти 10")
    print("▼" * 60)
    cpu.memory_write(10, 77)

    # 6. Проверка результата записи
    print("\n" + "▼" * 60)
    print("Демонстрация 5: проверка записи — чтение данных обратно из адреса 10")
    print("▼" * 60)
    val3 = cpu.memory_read(10)
    print(f"\nПроверка: запись 77, чтение {val3}, {'успех' if val3 == 77 else 'ошибка'})

    print("\n" + "=" * 60)
    print(" Все демонстрации завершены!")
    print("=" * 60)

Разрядность шины и фактическая производительность

Разрядность шины адреса определяет, "какой объём памяти она может адресовать"

Каждая линия шины адреса передаёт один двоичный бит. N линий адреса могут представлять 2^N различных адресов.

Разрядность шины адресаАдресуемое пространствоТипичный процессор
16-битный2^16 = 64 KBIntel 8086 (1978)
20-битная2^20 = 1 MBIntel 8088 (IBM PC, 1981)
24-битная2^24 = 16 MBIntel 80286 (1982)
32-битный2^32 = 4 GBIntel 80386 (1985)
64-битный2^64 ≈ 16 EBСовременные процессоры x86-64

Примечание: современные 64-битные процессоры обычно имеют меньше 64 фактических адресных линий (например, 48), потому что 2^64 байт намного превышает фактические требования к физической памяти сегодня. 48 адресных линий уже могут адресовать 256 TB, чего достаточно для текущего использования.

Ширина шины данных определяет «сколько данных может быть передано за один раз»

8-битная шина данных передаёт 1 байт за раз, тогда как 64-битная шина данных передаёт 8 байт за раз. Именно поэтому при одной и той же задаче 64-битные системы часто быстрее 32-битных — объём данных, передаваемых за одну операцию, удваивается.

Типы сигналов шины управления

Шина управления не так однородна, как шины адреса и данных. Она представляет собой совокупность многих типов сигнальных линий, включая:

  • MEM_READ / MEM_WRITE: Сигналы разрешения чтения/записи памяти
  • IO_READ / IO_WRITE: сигналы чтения/записи порта ввода-вывода
  • INTERRUPT: сигнал запроса прерывания — способ, которым внешние устройства уведомляют процессор
  • CLOCK: тактовый сигнал — "сердцебиение", которое заставляет всю систему работать синхронно
  • RESET: сигнал сброса — возвращение процессора в исходное состояние

Итоги и проверка

Резюме в одном предложении:Шина адреса отвечает на вопрос «куда идти», шина данных — на вопрос «что передавать», шина управления — на вопрос «что делать» — три шины выполняют каждая свою задачу и вместе завершают каждую передачу данных.

Вопросы для самопроверки

  1. Когда процессор хочет прочитать фрагмент данных из памяти, кто помещает информацию на шину адреса? А кто помещает информацию на шину данных?
  2. Если шина адреса компьютера имеет только 16 линий, какой максимальный объём памяти он может адресовать?
  3. В чём главное различие между шиной управления и шиной адреса и шиной данных? (Подсказка: подумайте о «составе» шины управления)

Эталонный ответ: 1. Адрес выставляет процессор, а данные — память. 2. 2^16 = 65536 байт = 64KB. 3. Шины адреса и данных передают однородные цифровые сигналы (значения адреса/значения данных), тогда как шина управления представляет собой набор отдельных дискретных сигнальных линий, каждая из которых имеет свою независимую функцию.

Другие расширения