Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Как на самом деле работают компьютеры

Практическое руководство по внутреннему устройству машины.

Автор Джастис Мэттью.

How Computers Really Work: A Hands-On Guide to the Inner Workings of the Machine

by Matthew Justice

Об авторе

Мэттью Джастис – инженер-программист. Он проработал 17 лет в компании Microsoft, где выполнял различные работы, включая отладку ядра Windows, автоматизацию исправления ошибок и руководство группой специалистов, ответственных за создание диагностических инструментов и сервисов. Он работал над низкоуровневым программным обеспечением (операционная система) и над программным обеспечением высокого уровня, удаленным от основного оборудования (например, веб-приложения). Мэттью имеет научную степень в области электротехники. Когда он не занят написанием кода или сбором схем, Мэттью любит проводить время с семьей, ходить в горы, читать, создавать музыкальные аранжировки и играть в старые видеоигры.


Принципы компьютерных вычислений

Аналоговый и цифровой

Аналоговые системы – это системы, использующие для представления данных широко варьирующиеся значения, в то время как цифровые системы представляют данные в виде последовательности символов.

Принцип работы аналоговых (механических) весов основан на фундаментальных законах физики, в частности на деформации упругих элементов и правиле рычага.

Положение иглы изменяется пропорционально в зависимости от веса, помещенного на весы. Таким образом, весы являются аналогией, позволяющей нам определить вес объекта через положение иглы на линии. Вот почему мы называем этот метод измерения аналоговым подходом. Само слово происходит от греческого analogos, что означает «соразмерный» или «соответствующий».

Другим примером аналогового измерительного прибора является жидкостный (например, ртутный или спиртовой) термометр. Объем жидкости увеличивается с ростом температуры. Производители термометров используют это свойство, помещая подкрашенную жидкость в стеклянную трубку с отметками, которые соответствуют ожидаемому объему жидкости при различных температурах. Таким образом, положение поверхности жидкости в трубке служит отображением температуры.

На протяжении почти всей истории человечества люди измеряли разные вещи, используя аналоговый подход. Но люди используют аналоговый подход не только для измерений. Они также придумали хитроумные способы хранения данных в аналоговом виде. Граммофонная пластинка использует извилистую канавку в качестве аналогового представления записанного звука. Форма канавки изменяется на всем ее протяжении в соответствии с изменениями формы звуковой волны во времени. Канавка – это не сам звук, а аналог формы волны оригинального звука. Пленочные камеры делают нечто подобное, кратковременно подвергая пленку воздействию света через объектив камеры, что приводит к химическим изменениям на пленке. Химические свойства пленки – это не само изображение, а представление запечатленного изображения, аналогия изображения.

Аналоговый звук это физический процесс, напрямую повторяющий звуковую волну (колебания воздуха). Значение сигнала существует в каждый момент времени. То есть сигнал — это реально существующее непрерывное напряжение или движение. На носителе (магнитная лента, канавки на виниле) физически хранится аналог полной звуковой волны. А цифровой звук это набор чисел, он имеет ограничение по детализации звука т.е. имеет дискретность, но его можно сжать, каждое измерение округляется (квантование). На носителе хранится не звук, а его числовая модель. Если частота дискретизации достаточная то цифровой звук можно восстановить в непрерывный сигнал. Частота дискретизации 44.1 кГц уже достаточно для человека, чтобы восстановить слышимый звук, а 48 кГц — стандарт в индустрии.

Аналоговый осциллограф напрямую отклоняет электронный луч в зависимости от входного напряжения. Это значит, что между сигналом и изображением на экране почти нет промежуточных этапов — сигнал фактически сразу превращается в линию. За счёт этого картинка получается непрерывной и «живой»: любые изменения формы сигнала отображаются мгновенно и без ступенчатости. Это хорошо ложится на интуицию, потому что ты видишь почти то же самое, что происходит в проводе. Цифровой осциллограф работает иначе. Сначала он измеряет сигнал через равные интервалы времени (дискретизация), потом хранит эти значения в памяти и уже из них строит изображение. В итоге ты смотришь не на сам сигнал, а на его числовую реконструкцию. Если частота дискретизации или настройки выбраны неудачно, прибор может пропустить быстрые детали или даже показать искажённую форму сигнала. Именно это иногда создаёт ощущение, что «цифра врёт», хотя на самом деле это вопрос параметров измерения. При этом важно понимать, что в объективном смысле современные цифровые осциллографы гораздо мощнее. Они позволяют сохранять сигнал, анализировать его, ловить редкие события, делать спектральный анализ. Просто аналоговый даёт более прямое и непрерывное представление, которое многим кажется более «честным» и понятным, особенно при работе с простыми или быстро меняющимися сигналами.

Аналоговые представления данных трудно поддаются точному измерению, имеют тенденцию разрушаться со временем и теряют точность при копировании. Компьютерам необходим способ представления всех типов данных в формате, который можно тщательно обрабатывать, хранить и копировать, сравнивать. Строго и точно сравнить два аналоговых сигнала невозможно, а цифровой звук это массив чисел, можно сравнить бит в бит. Цифровая система представляет данные в виде последовательности символов, где каждый символ принадлежит ограниченному набору значений.

Системы счисления

Если ваш компьютер говорит вам, что размер файла составляет 1 MB, на самом деле это 1 048 576 байт! Просто 1 MB это в системе счисление с основанием 10 (SI), а 1 048 576 байт это тоже значение но в двоичной системе (IEC) равно 1 MiB (Mebibyte), тут все кратно 1024. Десятичная система счисления используется производителями дисков, флешек и в маркетинге. (Но в Windows и некоторых системах, MiB до сих пор показывают как “MB”, из-за чего возникает путаница)

Другой момент путаницы с битами и байтами связан со скоростью передачи данных в сети. Интернет-провайдеры обычно заявляют скорость передачи данных в битах в секунду по основанию 10. Так, если вы получаете 50 Мбит в секунду при подключении к интернету, это означает, что вы можете передавать только 6 MiB в секунду. То есть 50 000 000 бит в секунду, разделенных на 8 бит в одном байте, дают нам 6 250 000 байт в секунду. Разделив 6 250 000 на (1 048 576 MiB), мы получим около 6 MiB в секунду.

Двоичный код в действии

Двоичное значение: 0b011000010110001001100011. Как вы думаете, что оно представляет? Если мы предположим, что это текстовая строка ASCII, то она представляет «abc». С другой стороны, возможно, оно представляет 24-битный цвет RGB (0x61, 0x62, 0x63), что делает его оттенком, близким к темно-серому. А может быть, это целое положительное число, в таком случаев десятичном исчислении оно равно 6 382 179.

Это может быть любая из этих интерпретаций или что-то совсем другое. Все зависит от контекста, в котором интерпретируются данные. Программа текстового редактора будет считать, что данные – это текст, в то время как программа для просмотра изображений может считать, что это цвет пикселя изображения, а калькулятор будет считать, что это число. Каждая программа написана так, что ожидает данные в определенном формате, поэтому одно двоичное значение имеет разное значение в разных контекстах. Возможность представить что-либо в виде последовательности из нулей и единиц чрезвычайно полезна, поскольку, создав устройство, работающее с двоичными данными, мы можем адаптировать его с помощью программного обеспечения для работы с любыми данными!

Операционные системы

Операционные системы обеспечивают интерфейс между оборудованием и другим программным обеспечением.

На устройстве без ОС, программное обеспечение имеет прямой доступ к оборудованию. Игровые приставки, как Atari 2600, Nintendo Entertainment System или Sega Genesis, их аппаратное обеспечение позволяет запускать код с картриджа, не задействуя операционную систему, так как у них ее нет. Игровая консоль могла запускать единовременно только одну программу – игру, находящуюся в слоте картриджа. В большинстве подобных систем включение системы без вставленного картриджа ничего не делало, так как у процессора не было никаких инструкций для выполнения. Чтобы переключиться на другую игру, нужно было выключить систему, поменять картридж и снова включить ее. Не существовало возможности переключения между программами во время работы системы. Не было и программ, работающих в фоновом режиме. Аппаратура была полностью занята только одной программой, игрой. Для программиста создание игры для такой системы означало принятие на себя ответственности за непосредственное управление аппаратным обеспечением с помощью кода. Как только система включалась, процессор начинал выполнять код на картридже. Разработчику игры нужно было не только написать программу, поддерживающую логику игровых действий, но и инициализировать систему, управлять видеоаппаратурой, считывать состояние входов контроллера и т. д. Различные аппаратные средства консолей имели радикально отличающийся дизайн, поэтому разработчику необходимо было разбираться в тонкостях устройства оборудования, на которое ему приходилось ориентироваться.

Операционная система – это программное обеспечение, которое взаимодействует с компьютерным оборудованием и обеспечивает среду для выполнения программ. Операционные системы позволяют программам запрашивать системные услуги, такие как чтение из памяти или обмен данными по сети. Операционные системы выполняют инициализацию компьютерной системы и управляют выполнением программ. ОС устанавливает границы, чтобы обеспечить изоляцию программ друг от друга и от самой ОС, а также гарантировать пользователям, работающим в одной системе, предоставление соответствующего разграничения доступа к оборудованию. Операционную систему можно рассматривать как слой кода между оборудованием (аппаратным обеспечением) и приложениями. Этот слой предоставляет набор возможностей, которые позволяют абстрагироваться от деталей базового оборудования, позволяя разработчикам программного обеспечения сосредоточиться на логике своих программ, а не на взаимодействии с конкретной аппаратурой.

Операционная система действует как промежуточный слой между оборудованием и приложениями

Компоненты, входящие в состав операционной системы, можно грубо разделить на два основных блока:

  • ядро
  • все остальное
    • оболочка (CLI, GUI) – пользовательский интерфейс для работы с ядром
    • программамы работающие в фоновом режиме отдельно от ядра - демоны (daemons) в Unix-подобных системах или службы (services) в Windows

Ядро операционной системы отвечает за управление памятью, облегчение работы устройств ввода/вывода и предоставление набора системных услуг для приложений. Ядро позволяет нескольким программам работать параллельно и совместно использовать аппаратные ресурсы. Оно является основной частью операционной системы, но само по себе не дает конечным пользователям возможности взаимодействовать с системой.

При взаимодействии с оборудованием ядро работает в партнерстве с драйверами устройств. Драйвер устройства, или просто драйвер, – это программа, предназначенная для взаимодействия с конкретным оборудованием. Ядро операционной системы должно работать с широким спектром аппаратных средств, поэтому вместо того, чтобы разрабатывать ядро, умеющее взаимодействовать с каждым аппаратным устройством в мире, разработчики программного обеспечения реализуют код для конкретных устройств в драйверах этих устройств. Операционные системы обычно включают набор драйверов, а также предоставляют механизм для установки дополнительных драйверов.

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

Операционные системы также обычно включают библиотеки программного обеспечения для разработчиков. Такие библиотеки содержат общий код, который может использоваться многими приложениями. Кроме того, компоненты самой операционной системы, такие как оболочка и службы, используют функционал, предоставляемый такими библиотеками. Библиотеки обеспечивают функционал, на котором строятся приложения, поэтому библиотеки показаны как слой между ядром и приложениями. Оболочка, службы и утилиты также основаны на библиотеках.

Операционная система включает в себя множество компонентов

Семейства операционных систем

Сегодня существует два доминирующих семейства операционных систем: Unix-подобные операционные системы и Microsoft Windows. Как следует из названия, Unix-подобные операционные системы ведут себя как операционная система Unix. Linux, macOS, iOS и Android c Raspberry Pi OS – все это примеры Unix-подобных операционных систем.

GNU (рекурсивный акроним1, означающий GNU's Not Unix) – это проект программного обеспечения, начатый в 1980-х годах с целью создания Unix-подобной операционной системы в качестве свободного программного обеспечения. Проект GNU и Linux – это отдельные проекты, но они оказались тесно связаны между собой. Выпуск ядра Linux в 1991 году послужил толчком к попытке перенести программное обеспечение GNU в Linux. В то время у GNU не было полного ядра, а у Linux не было оболочки, библиотек и т. д. Linux предоставил ядро для запуска кода GNU, а проект GNU предоставил оболочку, библиотеки и утилиты для Linux. Таким образом, эти два проекта дополняют друг друга и вместе образуют полноценную операционную систему.

Режим ядра и режим пользователя

Разграничение привилегий реализует процессор, а ОС этим механизмом управляет и его использует.

Внутри CPU есть специальный флаг (бит состояния), который говорит:

  • сейчас user mode
  • или kernel mode (режим супервизора)

Это значит, что сам процессор знает, в каком режиме он сейчас (user / kernel), запрещает выполнять определённые инструкции в user mode, контролирует доступ к памяти (через MMU).

Процессор может предоставлять более двух уровней привилегий, но большинство операционных систем использует только два уровня. Уровень более высоких привилегий называется kernel mode (режим ядра), а уровень более низких привилегий – user mode (режим пользователя).

Пользовательский режим user mode и системные вызовы

Код, работающий в пользовательском режиме, не может обращаться к адресам физической памяти, включая адреса, используемые для ввода/вывода с привязкой к памяти. Это означает, что он не может сам по себе печатать текст в консольном окне, получать ввод с клавиатуры, рисовать графику на экране, воспроизводить звук, получать ввод с сенсорного экрана, общаться по сети или читать файл с жесткого диска!

Но код в режиме user mode может попросить код kernel mode выполнить привилегированную операцию от его имени, это называется системным вызовом.

В процессорах предусмотрены инструкции, специально предназначенные для осуществления системных вызовов. В процессорах ARM используется инструкция SVC (ранее SWI), которая называется вызовом супервизора. В процессорах x86 для этой цели предусмотрены инструкции SYSCALL и SYSENTER.

Процессы

Когда операционная система запускает программу, она создает процесс, работающий экземпляр этой программы.

Процесс – это контейнер, в котором выполняется программа. Этот контейнер включает частное адресное пространство виртуальной памяти, копию кода программы, загруженную в память, и другую информацию о состоянии процесса. Программа может быть запущена несколько раз, и каждый запуск приводит к созданию операционной системой нового процесса. Каждый процесс имеет уникальный идентификатор (число), называемый идентификатором процесса, ID процесса или просто PID.

За исключением начальных процессов, запускаемых ядром, каждый процесс имеет родителя, т.е. процесс, который его запустил. Эти отношения родителя и ребенка создают дерево процессов. Если родительский процесс завершается раньше дочернего процесса, то дочерний процесс становится процессом-сиротой, в Linux осиротевший процесс обычно подхватывается начальным процессом (init), первым процессом пользовательского режима, который запускается в системе Linux.

Поток

Поток (thread) – это планируемая единица выполнения программы в рамках процесса. Поскольку потоки принадлежат процессу, они разделяют адресное пространство, код и другие ресурсы со всеми остальными потоками в этом процессе. Процесс начинается с одного потока и может создавать другие потоки по мере необходимости, когда работа должна выполняться параллельно. Каждый поток имеет идентификатор, называемый идентификатором потока, или TID. В Linux группа потоков, разделяющих адресное пространство и имеющих общий идентификатор процесса, считается процессом.

Виртуальная память

Виртуальная память – абстракция операционной системы, которая дает каждому процессу свое собственное большое частное адресное пространство.

Когда процесс записывает в определенный виртуальный адрес, этот адрес не относится непосредственно к области аппаратной памяти. При необходимости виртуальный адрес преобразуется в физический.

Один и тот же виртуальный адрес для нескольких программ соотносится с разными физическими адресами, поэтому вероятность случайного доступа одной программы к данным другой в памяти исключена.

Интерфейс прикладного программирования (API)

API ОС (для Linux ядро даёт системные вызовы это библиотека libc (конкретная реализация glibc - GNU C Library и представлена файлом libc.so.6), для Windows это WinAPI и современная версия UWP) – это спецификация, определенная в исходном коде и описанная в документации, подробно поясняющей, как программа должна взаимодействовать с ОС. Типичный API ОС включает список функций (включая их имена, входы и выходы) и структур данных, необходимых для взаимодействия с операционной системой. Программные библиотеки, входящие в состав операционной системы, обеспечивают реализацию спецификации API.

API ОС и системный вызов

Эти два понятия связаны, но они не эквивалентны.

Системный вызов (system call) это низкоуровневый механизм прямого доступа к ядру, реализация системных вызовов находится в коде самой операционной системы. С помощью системного вызова программа обращается к ядру ОС. Примеры обёрток над системными вызовами: read, write, fork, exec.

API операционной системы это высокоуровневый интерфейс, который используют разработчики. Часто оборачивает системные вызовы. Представляет собой функции/библиотеки. Может добавлять удобство, проверки, кэширование и даже не всегда обращаться к ядру.

Имена совпадают, но сущности разные:

  • read() (в libc) — обычная функция в пользовательском пространстве, передаёт управление в ядро
  • read (syscall) — точка входа в ядро

API ОС. Использование стандартной функции read из libc (конкретная реализация glibc)

#include <unistd.h>

int main() {
    char buf[10];
    read(0, buf, 10); // ← это функция libc
    return 0;
}

Явный системный вызов read. Можно вызвать syscall напрямую, минуя libc-обёртку:

#include <unistd.h>
#include <sys/syscall.h>

int main() {
    char buf[10];
    syscall(SYS_read, 0, buf, 10); // ← прямой syscall
    return 0;
}

Или вообще через inline assembly:

int main() {
    char buf[10];

    asm volatile (
        "mov $0, %%rax\n"    // SYS_read = 0
        "mov $0, %%rdi\n"    // fd = stdin
        "mov %0, %%rsi\n"    // buffer
        "mov $10, %%rdx\n"   // size
        "syscall\n"
        :
        : "r"(buf)
        : "rax", "rdi", "rsi", "rdx"
    );

    return 0;
}

Двоичный интерфейс приложений (application binary interface, ABI)

После компиляции полученный машинный код соответствует ABI для целевой архитектуры. Это означает, что во время выполнения именно ABI, а не API, определяет взаимодействие между скомпилированными программами и программными библиотеками.

ABI это про то как уже скомпилированная программа вообще выполняется процессором, какие регистры использовать, какая очередность загрузки регистров процессора, сколько доступно регистров для переменных функций и т.д.

Драйверы устройств

Современные компьютеры поддерживают широкий спектр аппаратных устройств, таких как дисплеи, клавиатуры, камеры и т. д. Каждое из этих устройств реализует свой собственный интерфейс ввода/вывода, позволяющий устройству взаимодействовать с остальной частью системы.

Прямое взаимодействие с аппаратным обеспечением ограничено кодом, выполняемым в режиме ядра, но не стоит ожидать, что ядро операционной системы будет знать, как взаимодействовать с каждым устройством. В этом случае на помощь приходят драйверы устройств. Драйвером устройства называют программу, которая взаимодействует с аппаратной частью этого устройства и обеспечивает программный интерфейс с ним.

Обычно драйвер устройства реализуется как модуль ядра, т.е. файл, содержащий код, который ядро может загрузить и выполнить в режиме ядра.

Современные вычислительные технологии

Категории облачных вычислений

Среди основных провайдеров облачных услуг можно назвать Amazon Web Services, Microsoft Azure, Google Cloud Platform, IBM Cloud, Oracle Cloud и Alibaba Cloud.

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

Категории облачных вычислений

Независимо от того, какая категория облачных вычислений используется, все компоненты должны присутствовать. Разница между категориями заключается в том, кто отвечает за управление каждым компонентом, облачный поставщик или потребитель облачных услуг.

Инфраструктура как услуга (Infrastructure as a Service, IaaS) – это модель облачных вычислений, в которой поставщик облака управляет только аппаратным обеспечением и виртуализацией, позволяя пользователю управлять операционной системой, средой выполнения, кодом приложения и данными.

Потребитель IaaS обычно получает подключенный к интернету виртуальный компьютер для использования по своему усмотрению, как правило, в качестве сервера. Этот виртуальный компьютер обычно реализуется как виртуальная машина на основе гипервизора или как контейнер пользовательской части дистрибутива Linux. Потребитель виртуального сервера IaaS имеет доступ к операционной системе виртуального компьютера и может настраивать его по своему усмотрению. Это дает потребителю максимальную гибкость, но это также означает, что ответственность за поддержку программного обеспечения системы (включая операционную систему, программное обеспечение сторонних производителей и т.д.) полностью ложится на плечи пользователя. IaaS предоставляет виртуальный компьютер, а потребитель несет ответственность за все, что работает на этом компьютере.

Платформа как услуга (Platform as a Service, PaaS) дает облачному провайдеру больше ответственности. В случае PaaS провайдер управляет не только оборудованием и виртуализацией, но и операционной системой и средой выполнения, которую хочет использовать потребитель. Потребитель облачных услуг PaaS разрабатывает приложение, предназначенное для работы на выбранной им облачной платформе, используя различные возможности, присущие только этой платформе. Пользователям PaaS не нужно заботиться о поддержке базовой ОС или среды выполнения. Потребитель облачных услуг может просто сосредоточиться на коде своего приложения. Хотя облачный провайдер абстрагирует детали базовой системы, потребителю все равно необходимо управлять ресурсами, предоставляемыми провайдером для работы его приложения. Это включает в себя объем необходимого хранилища и тип выделенных виртуальных машин. PaaS предоставляет управляемую платформу для выполнения кода, а потребитель отвечает за приложение, которое работает на этой платформе.

Функция как услуга (Function as a Service, FaaS) еще на один шаг дальше модели PaaS. Она не требует от потребителя развертывания полного приложения или предварительной подготовки экземпляров платформы. Вместо этого потребителю нужно только разместить свой код (функцию), который запускается в ответ на определенные события. Например, разработчик может написать функцию, которая возвращает расстояние до ближайшего продуктового магазина. Эта функция может запускаться в ответ на отправку веб-браузером своих текущих GPS-координат на URL. Эта событийно ориентированная модель означает, что облачный провайдер отвечает за вызов кода потребителя по требованию. Потребителю облачных услуг больше не нужно постоянно запускать код приложения, ожидая запросов. Это может упростить работу для потребителя и снизить затраты, хотя это может означать более медленное время отклика при поступлении запроса, если функциональный код еще не запущен.

FaaS является разновидностью бессерверных вычислений, модели облачных вычислений, в которой потребителям не нужно заниматься управлением серверами или виртуальными машинами. Конечно, этот термин является неправильным, серверы на самом деле необходимы для выполнения кода, просто потребителю не нужно о них думать! FaaS предоставляет событийно-ориентированную платформу для запуска кода, а потребитель отвечает за код, который запускается в ответ на события.

Программное обеспечение как услуга (Software as a Service, SaaS) – это принципиально иной тип облачных услуг. SaaS предоставляет потребителю приложение, которое полностью управляется в облаке. В то время как IaaS, PaaS и FaaS предназначены для команд разработчиков программного обеспечения, которые хотят запускать свой собственный код в облаке, SaaS предоставляет конечным пользователям или организациям уже готовое облачное приложение. Сегодня так много программного обеспечения работает в облаке, что это может показаться непримечательным, но это контрастирует с тем, как пользователь или организация устанавливают и обслуживают программное обеспечение на своих локальных устройствах и в сети. SaaS предоставляет полное приложение, управляемое в облаке, а потребитель отвечает только за данные, которые он хранит в этом приложении. Даже управление данными частично осуществляется провайдером, включая детали хранения, резервного копирования и т. д. Некоторые примеры SaaS включают Microsoft 365, Google G Suite и Dropbox.