Шина -- «система автомагистралей» внутри компьютера
Различные компоненты внутри компьютера должны постоянно обмениваться информацией. CPU должен извлекать команды из памяти и читать/записывать данные. Как передаётся эта информация? Ответ —шина。
Шина — это набор общих линий связи, соединяющих CPU, память, жёсткий диск, видеокарту и другие устройства, точно как дорожная система города — с магистралями и второстепенными дорогами, каждая из которых имеет чёткое назначение.
Эта лекция поможет вам понять разделение труда между тремя типами шин и с помощью Python смоделировать полную операцию чтения памяти.
Аналогия с системой автомагистралей: три вида дорог, три вида использования
Представьте, что вы доставляете посылки в большом городе. Курьеру нужно знать три вещи:куда доставить、Что отправить?、Нужна ли получателю подпись?。
Внутри компьютера также есть три совершенно аналогичные «магистрали»:
| Тип шины | Особенности | Направление передачи | аналогия из жизни |
|---|---|---|---|
| Шина адреса | Адрес памяти, к которому необходимо обратиться для передачи | CPU → Память (односторонняя) | «Номер дома» в накладной доставки — сообщающий получателю «я ищу этот адрес» |
| Шина данных | Передача фактического содержимого данных | двунаправленная | «Посылка» в руках курьера — сам предмет, который можно отправить или получить |
| Шина управления | Передача управляющих сигналов, таких как чтение/запись и прерывания | CPU ↔ Компоненты (двунаправленная) | «Квитанция» — сообщающая другой стороне, является ли это в данный момент получением или доставкой, и завершена ли передача |
Ширина шины адреса определяет, сколько памяти может адресовать CPU. 32-битная шина адреса может адресовать 2^32 = 4GB памяти, а 64-битная шина адреса теоретически может адресовать 2^64 байт (около 16EB). Именно поэтому 32-битная операционная система может использовать не более 4GB памяти.
Полный процесс операции чтения памяти
Предположим, CPU нужно прочитать фрагмент данных из адреса памяти 100. Весь процесс делится на 4 шага, при этом три шины работают совместно:
Разбор шагов
- CPU выставляет адрес: CPU помещает значение адреса 100 нашину адреса. Теперь память получила адрес.
- CPU посылает сигнал: CPU нашине управленияподает сигнал "чтение" (READ). Память понимает, что другая сторона хочет что-то получить.
- Память выдает данные: После получения адреса и сигнала чтения память находит данные, хранящиеся по адресу 100, и помещает их нашину данных.
- 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 KB | Intel 8086 (1978) |
| 20-битная | 2^20 = 1 MB | Intel 8088 (IBM PC, 1981) |
| 24-битная | 2^24 = 16 MB | Intel 80286 (1982) |
| 32-битный | 2^32 = 4 GB | Intel 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: сигнал сброса — возвращение процессора в исходное состояние
Итоги и проверка
Резюме в одном предложении:Шина адреса отвечает на вопрос «куда идти», шина данных — на вопрос «что передавать», шина управления — на вопрос «что делать» — три шины выполняют каждая свою задачу и вместе завершают каждую передачу данных.
Вопросы для самопроверки
- Когда процессор хочет прочитать фрагмент данных из памяти, кто помещает информацию на шину адреса? А кто помещает информацию на шину данных?
- Если шина адреса компьютера имеет только 16 линий, какой максимальный объём памяти он может адресовать?
- В чём главное различие между шиной управления и шиной адреса и шиной данных? (Подсказка: подумайте о «составе» шины управления)
Эталонный ответ: 1. Адрес выставляет процессор, а данные — память. 2. 2^16 = 65536 байт = 64KB. 3. Шины адреса и данных передают однородные цифровые сигналы (значения адреса/значения данных), тогда как шина управления представляет собой набор отдельных дискретных сигнальных линий, каждая из которых имеет свою независимую функцию.
Другие расширения