diff --git a/src/pages/computer-science/index.md b/src/pages/computer-science/index.md index 3e275c9..b5ffc81 100644 --- a/src/pages/computer-science/index.md +++ b/src/pages/computer-science/index.md @@ -21,16 +21,31 @@ order: 10 **Полный ответ** -Программа и данные хранятся в общей памяти, а процессор читает и выполняет инструкции последовательно, если управление -не изменено переходом. Классическая модель включает память, устройство управления, арифметико-логическое устройство и -ввод/вывод. +Архитектура фон Неймана описывает компьютер как систему, в которой **инструкции программы и обрабатываемые данные лежат +в одной адресуемой памяти**. Процессор получает из нее очередную инструкцию, декодирует ее, выполняет и переходит к +следующей, если сама инструкция не изменила поток управления. + +Этот процесс обычно объясняют циклом **fetch — decode — execute**: + +1. **Fetch** — устройство управления читает инструкцию по адресу из счетчика команд. +2. **Decode** — процессор определяет операцию и необходимые операнды. +3. **Execute** — ALU или другой исполнительный блок выполняет операцию. +4. **Store** — результат сохраняется в регистр или память, после чего обновляется счетчик команд. **Главные принципы**: -- Единое хранилище для программ и данных -- Однородность памяти -- Адресуемость памяти -- Последовательное программное управление +- единое хранилище для программ и данных; +- однородность памяти: ячейки различаются адресами, а не назначением; +- адресуемость: процессор обращается к конкретным ячейкам; +- последовательное программное управление с возможностью переходов. + +Общая память и общий путь к ней создают **бутылочное горлышко фон Неймана**: CPU может выполнять операции быстрее, чем +память успевает поставлять инструкции и данные. Поэтому современные процессоры используют регистры, несколько уровней +кеша, предварительную выборку и параллельное выполнение. Внутри процессора кеш инструкций и кеш данных могут быть +разделены, но на уровне программной модели компьютер все равно часто остается фоннеймановским. + +Например, при выполнении цикла над большим массивом производительность зависит не только от числа арифметических +операций, но и от того, насколько последовательно программа читает память и попадают ли данные в кеш. ![img.png](assets/von-neuman-architecture.png) @@ -44,42 +59,38 @@ order: 10 **Короткий ответ** -Основные части: +Классическая модель включает память, устройство управления, арифметико-логическое устройство и устройства ввода/вывода. +Устройство управления и ALU являются ключевыми частями CPU, но современный процессор содержит и другие блоки. **Полный ответ** -Основные части: +В классической архитектуре фон Неймана выделяют четыре основные части: -- память (RAM) -- устройство управления (Central Unit, CU) -- арифметико-логическое устройство (Arithmetic Logic Unit, ALU) -- устройства ввода и вывода (I/O) +- **память** хранит инструкции программы и данные; +- **устройство управления (CU)** получает и декодирует инструкции, а затем координирует работу остальных блоков; +- **арифметико-логическое устройство (ALU)** выполняет арифметические и логические операции; +- **устройства ввода и вывода (I/O)** связывают компьютер с диском, сетью, клавиатурой, экраном и другими устройствами. CPU объединяет управление и вычисления, RAM хранит активные инструкции и данные, а ввод/вывод связывает программу с -внешним миром. Современные компьютеры сложнее, но эта модель полезна как базовая абстракция. - -Строго говоря, **CU + ALU ≠ CPU**: это важные части процессора, но не весь процессор целиком. +внешним миром. Упрощенно выполнение команды выглядит так: CU читает инструкцию из памяти, подготавливает операнды, +направляет их в исполнительный блок и сохраняет результат. -Помимо устройства управления (CU) и арифметико‑логического устройства (ALU), в современном CPU есть и другие критически -важные компоненты: +Строго говоря, **CU + ALU не равны всему CPU**. Это важные части процессора, но современный CPU дополнительно содержит: -- Регистры. Это сверхбыстрая память прямо внутри ядра. В них временно держат операнды и результаты, чтобы ALU не тянул - данные из медленной RAM. Без регистров цикл «взять данные — посчитать — вернуть» был бы слишком долгим. -- Кэш‑память (L1, L2, иногда L3). Она хранит часто используемые данные и инструкции поближе к ядру. Кэш сильно смягчает - проблему «бутылочного горлышка» фон Неймана. -- Блок предсказания переходов и конвейер. В современных процессорах инструкции не выполняются строго по одной: их - «нанизывают» в конвейер, а блок предсказания заранее загружает нужные данные. Этим всем управляет именно логика внутри - CU, но сам блок — отдельная сложная структура. -- Исполнительные блоки помимо ALU. Например, FPU (для чисел с плавающей точкой) и векторные блоки (SIMD, как SSE/AVX в - x86). Они делают вычисления параллельно и отдельно от «обычного» ALU. -- Интерконнект и контроллеры. Внутри кристалла нужно соединять все эти блоки между собой и с внешней памятью; часто - прямо на кристалле размещают и контроллер памяти, и части PCIe‑интерфейса. +- **регистры** — самую быструю память для текущих операндов, адресов и результатов; +- **кеш L1, L2 и L3** — копии часто используемых данных и инструкций ближе к ядрам; +- **FPU и SIMD-блоки** — вычисления с плавающей точкой и обработку нескольких значений одной инструкцией; +- **конвейер и предсказатель переходов** — параллельную подготовку этапов инструкций и попытку заранее выбрать нужную + ветку; +- **декодеры, планировщики и reorder buffer** — внутреннее управление выполнением инструкций; +- **контроллеры памяти и внутренние шины** — обмен между ядрами, кешами, RAM и периферией. -На уровне базовой архитектуры (учебники, фон Нейман): часто говорят «CPU = CU + ALU», потому что цель — показать самую -суть: есть тот, кто управляет (CU), и тот, кто считает (ALU). Это упрощение, удобное для понимания принципов. +Например, выражение `total += prices[i]` требует не только сложения в ALU: нужно вычислить адрес элемента, получить +данные из регистра, кеша или RAM, выполнить операцию и сохранить результат. Поэтому скорость программы определяется всей +иерархией, а не только частотой ALU. -На уровне реального современного процессора: CPU — это сложная система из десятков и сотен подблоков, где CU и ALU — -важные, но не единственные части. +На интервью полезно сначала назвать учебную модель, а затем уточнить, что реальный CPU значительно сложнее. Это +показывает, что кандидат понимает границу между полезной абстракцией и физической реализацией. @@ -91,18 +102,44 @@ CPU объединяет управление и вычисления, RAM хр **Короткий ответ** -Frontend-разработчику эта модель помогает понимать CPU-bound задачи, main thread (главный поток) и стоимость доступа к -памяти. JavaScript выполняется не в вакууме: вычисления занимают CPU, объекты расходуют RAM, а сеть и диск (I/O) -работают существенно медленнее регистров и кешей процессора. Это помогает объяснять long tasks (тяжелые задачи), лаги -main thread, memory leaks (утечки памяти) и пользу Web Workers. +Frontend-разработчику эта модель помогает понимать CPU-bound задачи, main thread и стоимость доступа к памяти. +JavaScript-вычисления занимают CPU, объекты расходуют RAM, а сеть и диск работают существенно медленнее регистров и +кешей. Это объясняет long tasks, лаги интерфейса, memory leaks и пользу Web Workers. **Полный ответ** -Frontend-разработчику эта модель помогает понимать CPU-bound задачи, main thread (главный поток) и стоимость доступа к -памяти. JavaScript выполняется не в вакууме: вычисления занимают CPU, объекты расходуют RAM, а сеть и диск (I/O) -работают существенно медленнее регистров и кешей процессора. Это помогает объяснять long tasks (тяжелые задачи), лаги -main thread, memory leaks (утечки памяти) и пользу Web Workers. Знание базы позволяет оптимизировать измеряемые узкие -места, а не отдельные строки наугад. +Браузер скрывает детали железа, но производительность интерфейса все равно ограничена процессором, памятью и +вводом/выводом. Базовая модель помогает связать симптомы в DevTools с реальными причинами. + +**CPU-bound работа** занимает вычислительные ресурсы: + +- сортировка или агрегация большого массива; +- `JSON.parse()` большого ответа; +- синхронное преобразование изображений; +- сложный JavaScript во время рендера; +- style calculation, layout и часть paint pipeline. + +Если такая работа надолго занимает main thread, браузер не может вовремя обработать ввод пользователя и отрисовать +следующий кадр. Пользователь видит задержку клика, зависший scroll или пропущенную анимацию. Для тяжелых независимых +вычислений можно использовать Web Worker, но DOM остается доступен только из main thread. + +**Память** важна не только из-за утечек. Большие графы объектов увеличивают давление на garbage collector, а частые +выделения временных объектов могут вызывать заметные паузы. Например, хранение нескольких копий большого API-ответа в +store, кеше и view model повышает расход RAM без пользовательской пользы. + +**I/O-bound работа** чаще ждет сеть, диск или другой внешний ресурс. Здесь помогают HTTP-кеш, дедупликация запросов, +pagination, streaming, prefetch и правильная отмена устаревших операций, а не перенос кода в Worker. + +Практический порядок оптимизации: + +1. воспроизвести проблему и записать Performance profile; +2. определить, что ограничивает сценарий: CPU, память, сеть, layout или GPU; +3. найти самый дорогой участок; +4. изменить архитектуру или алгоритм; +5. повторить измерение и проверить пользовательскую метрику. + +Например, для таблицы на 100 000 строк правильное решение обычно не в микроправке одной функции. Нужны виртуализация, +меньше создаваемых DOM-узлов, стабильные ссылки на данные и перенос необязательных вычислений из критического пути. ![img.png](assets/frontend-mindset.png) @@ -116,13 +153,38 @@ main thread, memory leaks (утечки памяти) и пользу Web Worker **Короткий ответ** -CPU выполняет машинные инструкции и вычисления. RAM быстро хранит данные работающих процессов, но очищается после -выключения питания. Storage, например SSD, хранит файлы долговременно, но обычно имеет большую задержку доступа. +CPU выполняет машинные инструкции и вычисления. RAM быстро хранит данные работающих процессов, но теряет их после +выключения питания. Storage, например SSD, хранит файлы долговременно, однако обычно имеет большую задержку доступа. **Полный ответ** -CPU выполняет машинные инструкции и вычисления. RAM быстро хранит данные работающих процессов, но очищается после -выключения питания. Storage, например SSD, хранит файлы долговременно, но обычно имеет большую задержку доступа. +**CPU** выполняет машинные инструкции. Он читает команды, работает с регистрами и кешами, выполняет арифметические, +логические и управляющие операции. Несколько ядер позволяют действительно выполнять несколько потоков одновременно, хотя +конкретное распределение работы определяют операционная система и runtime. + +**RAM** — рабочая энергозависимая память. В ней находятся код и данные запущенных процессов: heap JavaScript, DOM, +буферы, библиотеки и другие активные структуры. RAM значительно больше кеша процессора, но доступ к ней медленнее. + +**Storage** — долговременное хранилище: SSD, HDD или флеш-память. На нем лежат файлы приложения, операционная система, +браузерный профиль и данные между перезапусками. Перед обработкой файл обычно читается со storage в RAM, после чего CPU +работает с загруженными данными. + +Упрощенная иерархия выглядит так: + +`регистры -> кеш CPU -> RAM -> SSD/HDD -> сеть` + +Чем ближе уровень к CPU, тем обычно меньше объем и ниже задержка. Поэтому последовательный доступ к уже загруженным +данным обычно дешевле случайного чтения большого числа мелких файлов или сетевых ресурсов. + +Frontend-пример: + +- JavaScript bundle хранится на сервере и может кешироваться на storage; +- браузер загружает его по сети и помещает необходимые данные в RAM; +- CPU парсит, компилирует и выполняет код; +- созданные объекты остаются в heap, пока доступны приложению или пока их не удалит garbage collector. + +Важно не смешивать физическую память с Web Storage API. `localStorage` и IndexedDB являются браузерными механизмами +долговременного хранения, а не прямым доступом JavaScript к RAM или SSD. ![img.png](assets/cpu-ram-storage.png) @@ -137,14 +199,32 @@ CPU выполняет машинные инструкции и вычислен **Короткий ответ** RAM быстрее и используется как рабочая память процессов, а диск предназначен для долговременного хранения. Данные с -диска обычно сначала читаются в память, после чего CPU может с ними работать. Недостаток RAM приводит к сборке мусора, -выгрузке страниц памяти и ухудшению отзывчивости. +диска обычно сначала читаются в RAM, после чего CPU может их обрабатывать. **Полный ответ** -RAM быстрее и используется как рабочая память процессов, а диск предназначен для долговременного хранения. Данные с -диска обычно сначала читаются в память, после чего CPU может с ними работать. Недостаток RAM приводит к сборке мусора, -выгрузке страниц памяти и ухудшению отзывчивости. +RAM и диск различаются назначением, скоростью и временем жизни данных. + +| Свойство | RAM | SSD/HDD | +| ---------------------- | ------------------------- | --------------------------- | +| Назначение | Активные данные процессов | Долговременные файлы | +| Сохранение без питания | Нет | Да | +| Задержка доступа | Ниже | Выше | +| Типичный объем | Меньше | Больше | +| Доступ CPU | Через память и кеши | После операции ввода/вывода | + +Когда программа открывает файл, операционная система обычно читает нужные блоки с диска в RAM. После этого процессор +работает с данными в памяти. ОС также использует page cache, поэтому повторное чтение файла иногда обслуживается из RAM +и происходит быстрее. + +Если физической RAM недостаточно, операционная система может выгружать неактивные страницы в swap или page file на диск. +Это помогает продолжить работу, но из-за более высокой задержки storage система может начать заметно тормозить. Сам по +себе недостаток RAM не означает, что garbage collector обязательно запустится: GC управляет памятью конкретного runtime, +а paging управляет операционная система. Эти механизмы связаны давлением на память, но не являются одним и тем же. + +Frontend-пример: загруженное изображение может занимать на диске 2 MB в сжатом JPEG, но после декодирования в RGBA-буфер +размером 4000 x 3000 пикселей потребовать около 48 MB RAM. Поэтому размер сетевого файла и фактическое потребление +памяти интерфейсом могут сильно различаться. ![img.png](assets/ram-vs-hard.png) @@ -158,15 +238,46 @@ RAM быстрее и используется как рабочая памят **Короткий ответ** -Это элементарная команда, которую CPU умеет декодировать и выполнять: загрузить данные, сложить значения, сравнить или -перейти к другому адресу. JavaScript сначала преобразуется движком в промежуточное представление и машинный код. Одна -строка исходника может потребовать много инструкций. +Машинная инструкция — элементарная команда из набора команд процессора: загрузить данные, выполнить арифметическую +операцию, сравнить значения или перейти к другому адресу. Одна строка JavaScript обычно превращается во множество таких +инструкций. **Полный ответ** -Это элементарная команда, которую CPU умеет декодировать и выполнять: загрузить данные, сложить значения, сравнить или -перейти к другому адресу. JavaScript сначала преобразуется движком в промежуточное представление и машинный код. Одна -строка исходника может потребовать много инструкций. +Машинная инструкция — команда, закодированная в формате, который понимает конкретная архитектура процессора, например +x86-64 или ARM64. Набор поддерживаемых команд и правила их кодирования называют **ISA — Instruction Set Architecture**. + +Инструкция обычно содержит: + +- **opcode** — какую операцию выполнить; +- **операнды** — регистры, константы или адреса памяти; +- иногда режим адресации и дополнительные флаги. + +Примеры категорий инструкций: + +- загрузка и сохранение данных между памятью и регистрами; +- сложение, умножение и побитовые операции; +- сравнение значений; +- условные и безусловные переходы; +- вызов функции и возврат; +- SIMD-операции над несколькими значениями. + +JavaScript не содержит прямых машинных инструкций. Движок браузера сначала парсит исходный код, строит внутреннее +представление, интерпретирует его и может JIT-компилировать часто выполняемые участки в машинный код. При изменении +предположений о типах оптимизированный код иногда деоптимизируется. + +Например, выражение: + +```js +const total = price + tax; +``` + +может потребовать проверить типы значений, загрузить их из памяти, выполнить сложение, обработать представление числа и +сохранить результат. Точное число инструкций зависит от движка, архитектуры CPU, контекста и примененных оптимизаций, +поэтому нельзя надежно оценивать производительность по числу строк исходного кода. + +Практический вывод: оптимизировать следует алгоритмы, объем данных и измеренные hot paths, а не пытаться вручную угадать +машинный код обычного JavaScript. ![img.png](assets/machine-command.png) diff --git a/src/pages/operating-systems/index.md b/src/pages/operating-systems/index.md index c3cae96..fa5ed2b 100644 --- a/src/pages/operating-systems/index.md +++ b/src/pages/operating-systems/index.md @@ -20,11 +20,21 @@ order: 11 **Полный ответ** -Программа - это набор инструкций и данных, описывающих, что должен сделать компьютер. В исходном коде программа понятна -разработчику, а для выполнения она должна быть интерпретирована, скомпилирована или запущена внутри runtime. +Программа - это **пассивное описание вычисления**: исходный код, байткод, исполняемый файл, конфигурация и связанные +ресурсы. Пока программа не запущена, операционная система не выделяет ей отдельное время CPU и рабочую память процесса. + +Способ выполнения зависит от технологии: + +- нативный бинарный файл содержит машинный код для конкретной архитектуры; +- JavaScript загружается и выполняется движком, который может интерпретировать и JIT-компилировать код; +- Java или .NET обычно используют байткод и runtime; +- TypeScript сначала преобразуется в JavaScript, который затем выполняет JS-движок. -Например, файл `main.ts` - это исходный код. После сборки и запуска его результат становится частью работающего -процесса. +Например, `main.ts` сам по себе является исходным текстом. После сборки появляется JavaScript bundle, но работающим +приложением он становится только после загрузки браузером или запуска Node.js. + +Важно различать программу и ее выполнение: один и тот же Chrome, Node.js script или CLI можно запустить несколько раз. +Каждый запуск создаст отдельный процесс или набор процессов со своим состоянием. @@ -41,12 +51,25 @@ order: 11 **Полный ответ** -Программа - это пассивное описание: файлы на диске, исходный код или исполняемый файл. Процесс - это уже запущенный -экземпляр программы в операционной системе. +Программа описывает, **какой код можно выполнить**, а процесс представляет **конкретное выполнение этого кода сейчас**. +Один файл программы может породить несколько независимых процессов. + +У процесса обычно есть: -У процесса есть память, идентификатор процесса (PID), открытые файлы, переменные окружения, права доступа и состояние -выполнения. Одну и ту же программу можно запустить несколько раз, и операционная система создаст несколько разных -процессов. +- PID; +- виртуальное адресное пространство; +- код, heap и stacks потоков; +- открытые файлы и sockets; +- переменные окружения и аргументы запуска; +- текущая директория; +- пользователь, права и ограничения ресурсов; +- состояние выполнения, которое отслеживает scheduler. + +Например, если открыть два независимых процесса браузера или дважды запустить `node server.js`, код программы может быть +одинаковым, но память, открытые соединения и текущие запросы будут разными. + +Изоляция процессов повышает устойчивость: ошибка одного процесса обычно не дает ему напрямую изменить память другого. +Обратная сторона - обмен между процессами требует IPC: pipes, sockets, shared memory или других механизмов ОС. @@ -63,11 +86,21 @@ order: 11 **Полный ответ** -Операционная система находит исполняемый файл или runtime, создает процесс, выделяет ему память, передает аргументы и -переменные окружения, подключает стандартные потоки ввода/вывода и передает управление начальной точке программы. +Точная последовательность зависит от ОС и формата программы, но упрощенно запуск выглядит так: -Дальше программа выполняет инструкции, обращается к памяти, файлам, сети и другим ресурсам через API операционной -системы. +1. Shell, файловый менеджер или другая программа просит ОС запустить файл. +2. ОС проверяет путь, формат файла и права доступа. +3. Создается процесс с PID и виртуальным адресным пространством. +4. Loader отображает в память код, данные и необходимые shared libraries. +5. Процесс получает аргументы, environment variables, working directory и стандартные потоки. +6. Создается основной поток, его registers и stack. +7. Scheduler допускает поток к CPU, и выполнение начинается с entry point. + +В Unix-подобных системах shell часто создает дочерний процесс и вызывает семейство `exec`, а Windows предоставляет +`CreateProcess`. Детали разные, но смысл одинаков: ОС превращает пассивный файл в управляемое выполнение с ресурсами. + +После старта программа обращается к файлам, сети, таймерам и устройствам через runtime и системные вызовы. Даже простой +`console.log()` в итоге приводит к записи в стандартный output, которой управляет ОС. @@ -84,11 +117,30 @@ order: 11 **Полный ответ** -Операционная система - это слой между программами и аппаратным обеспечением. Она управляет процессами, памятью, файлами, -устройствами ввода/вывода, сетью, пользователями и правами доступа. +Операционная система одновременно является **менеджером ресурсов** и **набором абстракций** над аппаратным обеспечением. -Для разработчика ОС важна потому, что любая программа работает не напрямую с железом, а через правила и API операционной -системы. +Как менеджер ресурсов она решает: + +- какой поток и когда получит CPU; +- какие страницы памяти принадлежат процессу; +- кто может открыть файл или порт; +- как разделить устройства между программами; +- как изолировать пользователей и процессы. + +Как слой абстракций ОС дает программам более удобные сущности: + +- процесс вместо ручного управления CPU; +- файл вместо работы с блоками накопителя; +- socket вместо прямого формирования сетевых сигналов; +- virtual memory вместо физических адресов RAM; +- window, input events и device APIs поверх конкретного железа. + +ОС включает не только kernel. В систему также входят drivers, системные службы, библиотеки, shell и пользовательские +утилиты. Например, Linux технически является ядром, а привычная Linux-система дополнена user-space инструментами и +дистрибутивом. + +Для frontend-разработчика ОС проявляется через файловые watchers, лимиты открытых файлов, процессы dev server, +переменные окружения, сертификаты, DNS, sockets и поведение браузера. @@ -105,11 +157,26 @@ order: 11 **Полный ответ** -Ядро - центральная часть операционной системы, которая управляет самыми низкоуровневыми ресурсами: CPU, памятью, -процессами, драйверами, файловой системой и сетевым вводом/выводом. +Kernel работает с повышенными привилегиями и контролирует ресурсы, которым нельзя безопасно доверить обычным +приложениям. + +К основным задачам ядра относятся: -Обычная программа не должна напрямую управлять железом. Она обращается к ядру через системные вызовы, например чтобы -прочитать файл, открыть сетевое соединение или создать новый процесс. +- планирование потоков на CPU; +- создание и завершение процессов; +- virtual memory и защита адресных пространств; +- обработка interrupts; +- работа с device drivers; +- файловые системы; +- network stack; +- проверка прав доступа и системных ограничений. + +Пользовательская программа не читает диск и не управляет сетевой картой напрямую. Она вызывает API runtime или системной +библиотеки, а те переходят к system call, например `open`, `read`, `write`, `mmap` или `socket`. Kernel проверяет +запрос, выполняет привилегированную операцию и возвращает результат. + +Ошибка в kernel или driver опаснее ошибки обычного приложения, потому что kernel имеет доступ ко всей системе. Поэтому +минимизация кода с высокими привилегиями и четкая граница user/kernel space важны для безопасности и устойчивости. @@ -126,11 +193,24 @@ Kernel space - область, где работает ядро ОС и код **Полный ответ** -Kernel space - область, где работает ядро ОС и код с максимальными правами. User space - область, где работают обычные -пользовательские программы. +Kernel space и user space разделены уровнями привилегий CPU и механизмами virtual memory. + +Код в **kernel space** может управлять page tables, drivers, interrupts и памятью разных процессов. Код в **user space** +обычно видит только собственное виртуальное адресное пространство и не может выполнить привилегированную инструкцию +напрямую. + +Когда приложению нужен защищенный ресурс, оно делает system call: + +1. CPU переключается в привилегированный режим. +2. Kernel проверяет параметры и права. +3. Операция выполняется или отклоняется. +4. Управление возвращается в user space. -Разделение нужно для безопасности и устойчивости: ошибка в обычной программе не должна ломать всю систему. Если -программе нужен файл, сеть или устройство, она просит об этом ядро через системный вызов. +Это разделение ограничивает последствия ошибок. Если вкладка браузера падает, обычно завершается ее процесс, а не вся +ОС. Если же критическая ошибка происходит в kernel driver, возможен crash всей системы. + +Переход между режимами не бесплатен, но обычно проблема производительности не в одном system call, а в огромном числе +мелких операций. Поэтому I/O часто буферизуют и объединяют в batches. @@ -147,11 +227,22 @@ Kernel space - область, где работает ядро ОС и код **Полный ответ** -Поток выполнения - это последовательность инструкций внутри процесса, которую планировщик ОС может выполнять на CPU. -Процесс может иметь один или несколько потоков. +Thread - минимальная единица выполнения, которую обычно планирует ОС. Каждый поток имеет собственные: + +- instruction pointer; +- registers; +- stack; +- состояние scheduler. -Потоки одного процесса обычно разделяют общую память процесса, поэтому обмениваться данными между ними дешевле, чем -между разными процессами. Но из-за общей памяти появляются риски гонок данных и необходимость синхронизации. +При этом потоки одного процесса обычно разделяют code, heap, открытые файлы и sockets. Благодаря общей памяти обмен +данными дешевле, чем между процессами, но требуется синхронизация. + +Например, browser process может использовать отдельные потоки для UI, I/O и служебной работы, а renderer process - main +thread и дополнительные worker threads. JavaScript-код страницы обычно выполняется на main thread, но Web Worker дает +отдельный execution context, который может работать параллельно на другом ядре. + +Если два потока изменяют общие данные без координации, появляется data race. Для защиты используют mutexes, atomics, +message passing или проектируют систему так, чтобы mutable state не разделялся. @@ -167,10 +258,26 @@ Kernel space - область, где работает ядро ОС и код **Полный ответ** -Процесс - изолированный экземпляр программы со своей памятью и ресурсами. Поток - линия выполнения внутри процесса. +Главное различие - **изоляция ресурсов**. + +Процесс обычно имеет отдельное виртуальное адресное пространство. Чтобы два процесса обменялись данными, нужен IPC. +Потоки одного процесса видят общий heap и многие общие ресурсы, но имеют собственные stacks и registers. -Разные процессы обычно не могут напрямую читать память друг друга. Потоки одного процесса разделяют память процесса и -часть ресурсов, поэтому они легче, но требуют аккуратной работы с общим состоянием. +Практические trade-offs: + +| Свойство | Процессы | Потоки | +| --------------------------------- | --------------------- | ---------------------------- | +| Изоляция | Выше | Ниже | +| Обмен данными | Через IPC | Через общую память | +| Стоимость создания и переключения | Обычно выше | Обычно ниже | +| Последствие crash | Чаще локализовано | Может повредить весь процесс | +| Риск data races | Ниже между процессами | Выше при shared state | + +Браузеры используют multi-process архитектуру, чтобы изолировать вкладки, сайты и привилегированные компоненты. Внутри +каждого процесса при этом могут работать несколько потоков. + +Выбор зависит не только от скорости. Для недоверенного кода или сильной изоляции процесс может быть правильнее, даже +если он тяжелее. Для тесно связанной вычислительной работы могут подойти threads или workers. @@ -188,12 +295,23 @@ Kernel space - область, где работает ядро ОС и код **Полный ответ** -Конкурентность означает, что несколько задач продвигаются во времени независимо: одна ждет сеть, другая обрабатывает -ввод, третья готовится к выполнению. Параллельность означает, что несколько задач действительно выполняются одновременно -на разных ядрах CPU. +**Concurrency** описывает организацию нескольких незавершенных задач. Система переключается между ними или продолжает +одну, пока другая ждет I/O. + +**Parallelism** означает физическое одновременное выполнение как минимум двух операций, например на разных CPU cores. + +Однопоточный JavaScript может быть конкурентным: -Однопоточная программа тоже может быть конкурентной, если она умеет переключаться между задачами во время ожидания I/O. -Но для настоящего одновременного CPU-выполнения нужны несколько потоков, процессов, ядер или рабочих окружений. +1. код начинает `fetch`; +2. запрос обрабатывается сетевой подсистемой; +3. main thread продолжает другую работу; +4. после ответа callback или Promise continuation попадает в очередь. + +Но JavaScript callbacks в одном event loop не выполняются параллельно друг другу. Для CPU-параллелизма в браузере нужны +Web Workers, а в Node.js - worker threads или процессы. + +Важно не говорить, что `async/await` автоматически создает новый поток. Он упрощает управление асинхронным ожиданием, но +CPU-bound цикл по-прежнему блокирует текущий поток. @@ -210,12 +328,27 @@ CPU-bound задача упирается в вычисления процесс **Полный ответ** -CPU-bound задача упирается в вычисления процессора: парсинг большого объема данных, сжатие, криптография, сложная -агрегация. I/O-bound задача больше ждет внешний ресурс: диск, сеть, базу данных или файловую систему. +У CPU-bound и I/O-bound задач разные bottlenecks, поэтому им нужны разные оптимизации. + +**CPU-bound**: + +- CPU долго занят вычислением; +- ускоряется улучшением алгоритма, уменьшением данных, SIMD или параллелизмом; +- асинхронная обертка сама по себе не сокращает вычисления. + +Frontend-примеры: тяжелая сортировка, декодирование данных, image processing, сложный synchronous parser. + +**I/O-bound**: -Это различие важно для оптимизации. CPU-bound работу часто ускоряют параллельным выполнением или алгоритмическими -улучшениями. I/O-bound работу чаще улучшают кешированием, батчингом, асинхронностью и уменьшением числа обращений к -внешним ресурсам. +- большая часть времени уходит на ожидание сети, диска или базы данных; +- помогает concurrency, caching, batching, streaming и сокращение round trips; +- дополнительные CPU cores могут почти не изменить latency одного запроса. + +Например, десять HTTP-запросов можно выполнять конкурентно, если backend допускает это. Но запуск десяти тяжелых +вычислений на одном main thread не сделает их параллельными и может ухудшить responsiveness. + +Сначала нужно измерить, где тратится время. Медленный пользовательский сценарий часто смешивает оба типа: сеть +доставляет большой JSON, после чего main thread долго его парсит и рендерит. @@ -232,10 +365,22 @@ CPU-bound задача упирается в вычисления процесс **Полный ответ** -Терминал - это интерфейс для ввода команд и просмотра текстового вывода программ. В современных системах чаще всего это -приложение-эмулятор терминала: Terminal, iTerm, Windows Terminal или встроенный терминал IDE. +Исторически terminal был физическим устройством с клавиатурой и экраном, подключенным к вычислительной машине. Сегодня +terminal emulator - приложение, которое воспроизводит текстовый интерфейс терминала. + +Он отвечает за: + +- отображение символов, colors и cursor; +- передачу нажатий клавиш; +- размер текстовой области; +- обработку control sequences; +- связь с shell или другой консольной программой через pseudo-terminal. + +Сам terminal обычно не решает, что означает `cd`, `npm` или pipe. Он передает ввод запущенной программе и отображает ее +output. Обычно этой программой является shell, но в terminal можно запустить REPL, editor или другую TUI-программу. -Сам терминал не обязательно понимает команды. Обычно он запускает shell, а уже shell интерпретирует введенную строку. +Поэтому закрытие окна terminal может завершить связанные процессы, но это зависит от того, привязаны ли они к terminal, +запущены ли через background manager, `nohup`, `tmux`, system service или IDE. @@ -252,11 +397,29 @@ Terminal отвечает за окно, ввод, вывод и отображ **Полный ответ** -Terminal отвечает за окно, ввод, вывод и отображение текста. Shell - это программа, которая читает команду, разбирает -аргументы, ищет исполняемые файлы, запускает процессы и связывает их ввод/вывод. +Terminal предоставляет **канал взаимодействия**, а shell предоставляет **язык команд и управление процессами**. + +Shell: + +- разбирает quoting, variables и wildcard expansion; +- выполняет built-in команды вроде `cd`; +- ищет программы по `PATH`; +- создает pipelines; +- настраивает redirections; +- запускает foreground и background jobs; +- возвращает exit code. -Примеры shell: `bash`, `zsh`, `fish`, `PowerShell`. Поэтому одна и та же команда может работать по-разному в разных -shell или на разных операционных системах. +Например, при команде: + +```bash +cat app.log | grep ERROR > errors.log +``` + +terminal передает строку shell. Shell создает процессы `cat` и `grep`, соединяет `stdout` первого со `stdin` второго и +перенаправляет результат в файл. Terminal только отображает ввод и то, что не было перенаправлено. + +Примеры shell: `bash`, `zsh`, `fish`, PowerShell. Их syntax и built-ins отличаются, поэтому команда для Bash не обязана +работать в PowerShell, даже если используется то же приложение terminal. @@ -268,18 +431,32 @@ shell или на разных операционных системах. **Короткий ответ** -Это стандартные потоки процесса: +`stdin` - стандартный ввод процесса, `stdout` - обычный результат, `stderr` - ошибки и диагностика. Shell может +соединять и перенаправлять эти потоки. **Полный ответ** -Это стандартные потоки процесса: +При запуске консольный процесс обычно получает три стандартных потока: + +- `stdin` с file descriptor `0`; +- `stdout` с file descriptor `1`; +- `stderr` с file descriptor `2`. -- `stdin` - стандартный ввод -- `stdout` - стандартный вывод обычного результата -- `stderr` - стандартный вывод ошибок и диагностики +По умолчанию они могут быть связаны с terminal, но shell умеет заменить источник или назначение: -Shell может перенаправлять эти потоки: передавать вывод одной программы на вход другой, писать результат в файл или -разделять обычный output и ошибки. +```bash +node build.mjs < input.json +node build.mjs > result.txt +node build.mjs 2> errors.txt +node build.mjs > all.txt 2>&1 +cat app.log | grep ERROR +``` + +Разделение `stdout` и `stderr` важно для автоматизации. CLI может писать machine-readable результат в `stdout`, а +progress и диагностику - в `stderr`, не повреждая данные pipeline. + +Stream не обязательно является файлом на диске. Он может быть terminal, pipe, socket или специальное устройство. Запись +может буферизоваться, поэтому момент вызова logging API и момент появления текста не всегда совпадают. @@ -292,15 +469,32 @@ Shell может перенаправлять эти потоки: переда **Короткий ответ** Переменные окружения - это пары ключ-значение, которые ОС или shell передают процессу при запуске. Они часто -используются для конфигурации: режим окружения, адрес API, токены, порты, feature flags. +используются для конфигурации: режим окружения, адрес API, токены, порты и feature flags. **Полный ответ** -Переменные окружения - это пары ключ-значение, которые ОС или shell передают процессу при запуске. Они часто -используются для конфигурации: режим окружения, адрес API, токены, порты, feature flags. +Environment variables являются частью окружения процесса. Обычно дочерний процесс получает **копию** набора переменных +родителя при запуске. + +Пример: + +```bash +API_URL=https://api.example.com NODE_ENV=production node server.mjs +``` + +Процесс читает значения через API runtime, например `process.env` в Node.js. Если после запуска изменить переменную в +другом окне shell, работающий процесс обычно не получит обновление автоматически. + +Важные ограничения: + +- значения передаются как strings; +- наличие переменной нужно валидировать при старте; +- environment не является безопасным secret storage сам по себе; +- переменные frontend build часто подставляются в bundle во время сборки и становятся видны пользователю; +- runtime-конфигурация браузерного приложения требует отдельного механизма, например config endpoint или файла. -Процесс получает собственный набор environment variables при старте. Если изменить переменную в терминале после запуска -программы, уже работающий процесс обычно не узнает об этом автоматически. +Хорошая практика - преобразовать сырые variables в типизированный config один раз при старте и завершить процесс с +понятной ошибкой, если обязательное значение отсутствует. @@ -312,16 +506,30 @@ Shell может перенаправлять эти потоки: переда **Короткий ответ** -Exit code - числовой код завершения процесса. Обычно 0 означает успешное завершение, а ненулевое значение означает -ошибку или особое состояние. +Exit code - числовой код завершения процесса. Обычно `0` означает успех, а ненулевое значение - ошибку или другое +неуспешное состояние. **Полный ответ** -Exit code - числовой код завершения процесса. Обычно `0` означает успешное завершение, а ненулевое значение означает -ошибку или особое состояние. +Когда процесс завершается, ОС сохраняет его status, который может прочитать родительский процесс или shell. По +соглашению `0` означает успешное выполнение, а ненулевые значения обозначают разные причины неуспеха. -Exit code важен для shell scripts, CI/CD и package scripts. Например, если тесты завершаются с ненулевым кодом, pipeline -понимает, что проверка не прошла. +Shell использует status для управления командами: + +```bash +npm test && npm run build +npm test || echo "Tests failed" +``` + +В первом случае build запускается только после `0`, во втором правая команда выполняется после ошибки. CI/CD работает по +тому же принципу: job считается failed, если команда вернула ненулевой status. + +CLI полезно выбирать стабильные codes и писать объяснение в `stderr`. При завершении из-за signal отображаемое значение +может кодироваться особым образом и отличаться между shell и ОС, поэтому не стоит считать любое конкретное ненулевое +число универсальным без документации инструмента. + +В Node.js можно задать `process.exitCode`, чтобы event loop завершился естественно и buffered output успел записаться, +вместо немедленного `process.exit()`. @@ -333,16 +541,27 @@ Exit code важен для shell scripts, CI/CD и package scripts. Напри **Короткий ответ** -Порт - это числовой идентификатор сетевого endpoint внутри компьютера. IP-адрес помогает найти машину в сети, а порт -помогает понять, какой программе на этой машине предназначено соединение. +Порт - числовой идентификатор transport endpoint внутри хоста. IP-адрес помогает найти сетевой интерфейс, а порт - +программу или службу, которой предназначены данные. **Полный ответ** -Порт - это числовой идентификатор сетевого endpoint внутри компьютера. IP-адрес помогает найти машину в сети, а порт -помогает понять, какой программе на этой машине предназначено соединение. +TCP и UDP используют номера портов от `0` до `65535`, чтобы несколько сетевых программ могли работать на одном +IP-адресе. TCP port и UDP port являются разными пространствами: процесс может использовать один номер для TCP, а +другой - для UDP. + +Server обычно bind-ится к известному порту, например `443`. Client при исходящем соединении получает временный ephemeral +port. TCP-соединение различается набором: + +`protocol + source IP + source port + destination IP + destination port` + +Поэтому тысячи clients могут одновременно обращаться к одному server port: у соединений различаются source endpoints. + +В URL порт указывают после двоеточия: `http://localhost:3000`. Если порт не указан, схема задает default, например `80` +для HTTP и `443` для HTTPS. -Например, в адресе `http://localhost:3000` число `3000` - это порт. На одном компьютере могут одновременно работать -разные серверные процессы на разных портах. +Порт не является физическим разъемом и не гарантирует конкретный protocol приложения. На `3000` может работать HTTP +server, но это определяется программой, а не самим числом. @@ -354,16 +573,28 @@ Exit code важен для shell scripts, CI/CD и package scripts. Напри **Короткий ответ** -Это значит, что программа попросила ОС привязать сетевой socket к конкретному порту и готова принимать входящие -соединения или пакеты. +Это значит, что программа привязала socket к адресу и порту и готова принимать входящие соединения или datagrams. **Полный ответ** -Это значит, что программа попросила ОС привязать сетевой socket к конкретному порту и готова принимать входящие -соединения или пакеты. +Для TCP server последовательность обычно включает: -Например, dev server может слушать `localhost:3000`. Когда браузер открывает этот адрес, ОС направляет сетевое -соединение процессу, который слушает порт `3000`. +1. создать socket; +2. выполнить `bind` к local address и port; +3. перевести socket в состояние `listen`; +4. принимать соединения через `accept`; +5. читать и писать данные через socket каждого соединения. + +Для UDP постоянного соединения может не быть: программа bind-ится к порту и получает отдельные datagrams. + +Адрес binding важен: + +- `127.0.0.1:3000` доступен только через loopback; +- `0.0.0.0:3000` означает все подходящие IPv4 interfaces; +- конкретный LAN IP ограничивает один interface. + +Например, dev server, слушающий только localhost, откроется на ноутбуке разработчика, но обычно не будет доступен с +телефона в той же сети. Чтобы открыть доступ, нужно изменить binding и учесть firewall. @@ -375,16 +606,34 @@ Exit code важен для shell scripts, CI/CD и package scripts. Напри **Короткий ответ** -Обычно один и тот же адрес и порт не могут быть заняты двумя разными процессами одновременно. Если dev server уже -слушает localhost:3000, второй сервер при попытке занять тот же порт получит ошибку вроде EADDRINUSE. +Другой socket уже может быть привязан к несовместимому сочетанию protocol, address и port. Тогда новый server получает +ошибку вроде `EADDRINUSE`. **Полный ответ** -Обычно один и тот же адрес и порт не могут быть заняты двумя разными процессами одновременно. Если dev server уже -слушает `localhost:3000`, второй сервер при попытке занять тот же порт получит ошибку вроде `EADDRINUSE`. +ОС не позволяет двум listening sockets без специальных условий одновременно владеть одним и тем же endpoint. Например, +если процесс слушает `0.0.0.0:3000`, он занимает порт на всех IPv4 interfaces и может конфликтовать с binding к +`127.0.0.1:3000`. + +Причины: + +- старый dev server продолжает работать; +- IDE или terminal оставили background process; +- другой инструмент использует тот же default port; +- предыдущие соединения еще находятся в переходном TCP state; +- настройки контейнера публикуют host port. + +Диагностика зависит от ОС, например: -Решение зависит от ситуации: остановить старый процесс, выбрать другой порт или проверить, не остался ли фоновый процесс -после закрытия терминала или IDE. +```bash +lsof -i :3000 +``` + +После определения процесса можно корректно остановить его или выбрать другой порт. Не следует без проверки завершать +случайные системные процессы. + +Опции вроде `SO_REUSEADDR` влияют на повторное использование адреса, но не означают, что любые два servers безопасно +разделят один endpoint. Семантика зависит от ОС и socket options. @@ -396,16 +645,27 @@ Exit code важен для shell scripts, CI/CD и package scripts. Напри **Короткий ответ** -localhost - имя, которое указывает на текущий компьютер. Обычно оно разрешается в loopback-адрес 127.0.0.1 для IPv4 или -::1 для IPv6. +`localhost` - имя текущего хоста, которое обычно разрешается в loopback-адрес `127.0.0.1` для IPv4 или `::1` для IPv6. **Полный ответ** -`localhost` - имя, которое указывает на текущий компьютер. Обычно оно разрешается в loopback-адрес `127.0.0.1` для IPv4 -или `::1` для IPv6. +Loopback interface позволяет сетевым программам на одном компьютере общаться через обычный network stack без отправки +пакетов во внешнюю сеть. + +Когда браузер открывает `http://localhost:3000`, он соединяется с process на той же network namespace, который слушает +подходящий address и port. + +Важно различать: -Когда браузер открывает `http://localhost:3000`, запрос не уходит во внешнюю сеть. Он направляется на программу, -работающую на этом же компьютере и слушающую порт `3000`. +- `localhost` - host name; +- `127.0.0.1` и `::1` - loopback IP addresses; +- `0.0.0.0` - не адрес для обращения из браузера, а специальный bind address "все IPv4 interfaces". + +В container или virtual machine `localhost` относится к самой изолированной среде, а не автоматически к host OS. +Например, `localhost` внутри Docker container указывает на container. Для доступа к host или соседнему service нужен +отдельный address или network name. + +Также возможна разница IPv4/IPv6: server, слушающий только `127.0.0.1`, не всегда примет соединение на `::1`. @@ -417,16 +677,26 @@ localhost - имя, которое указывает на текущий ком **Короткий ответ** -Сервером могут называть и физическую или виртуальную машину, и программу, и роль в сетевом взаимодействии. В базовом -смысле сервер - это сторона, которая принимает запросы и возвращает ответы или предоставляет ресурс. +Сервером называют роль, программу или машину, которая принимает запросы или соединения и предоставляет данные либо +другой ресурс. **Полный ответ** -Сервером могут называть и физическую или виртуальную машину, и программу, и роль в сетевом взаимодействии. В базовом -смысле сервер - это сторона, которая принимает запросы и возвращает ответы или предоставляет ресурс. +Термин server используется на нескольких уровнях: + +- **роль** в конкретном взаимодействии; +- **server process**, например Node.js HTTP server; +- **host или virtual machine**, на которой работают server processes; +- **hardware**, предназначенное для серверной нагрузки. + +Server обычно слушает endpoint, принимает запрос, выполняет обработку и возвращает response. Он может одновременно +обслуживать много clients через event loop, threads, processes или их комбинацию. -Ноутбук разработчика тоже может быть сервером, если на нем запущена программа, принимающая соединения. Например, -локальный dev server Angular, Vite или Astro обслуживает браузер во время разработки. +Ноутбук разработчика становится server для браузера, когда на нем работает Angular, Vite или Astro dev server. При этом +тот же dev server может выступать client, когда запрашивает данные у backend. + +Server не обязан быть удаленным и не обязан работать по HTTP. Файловый server, database, DNS server и WebSocket server +предоставляют разные protocols и ресурсы. @@ -438,16 +708,26 @@ localhost - имя, которое указывает на текущий ком **Короткий ответ** -Клиент - это сторона, которая инициирует запрос к серверу. Браузер, мобильное приложение, CLI-утилита или другой сервис -могут выступать клиентом. +Клиент - сторона, которая инициирует взаимодействие с server. Им может быть браузер, мобильное приложение, CLI или +другой service. **Полный ответ** -Клиент - это сторона, которая инициирует запрос к серверу. Браузер, мобильное приложение, CLI-утилита или другой сервис -могут выступать клиентом. +Client знает или находит endpoint server, устанавливает необходимое соединение и отправляет request либо команду. + +Для открытия HTTPS-страницы браузер упрощенно: + +1. разрешает domain через DNS; +2. устанавливает TCP или QUIC connection; +3. выполняет TLS handshake; +4. отправляет HTTP request; +5. получает response и обрабатывает данные. + +Роли относительны. Browser является client для web server. Backend может быть server для browser и одновременно client +для database, payment API или другого service. -Роли клиента и сервера зависят от конкретного взаимодействия. Одна и та же программа может быть сервером для одного -соединения и клиентом для другого. +Client также отвечает за собственные concerns: timeouts, retries, cancellation, validation response и обработку ошибок. +Безусловные retries опасны для non-idempotent операций, поэтому поведение зависит от protocol и semantics запроса. @@ -459,16 +739,31 @@ localhost - имя, которое указывает на текущий ком **Короткий ответ** -IP-адрес - это адрес устройства или сетевого интерфейса в IP-сети. Он нужен, чтобы пакеты могли быть доставлены от -одного узла к другому. +IP-адрес идентифицирует network interface в IP-сети и используется routing для доставки packets между узлами. **Полный ответ** -IP-адрес - это адрес устройства или сетевого интерфейса в IP-сети. Он нужен, чтобы пакеты могли быть доставлены от -одного узла к другому. +IP address является логическим адресом network layer. Router анализирует destination address и выбирает, куда переслать +packet дальше. + +Основные версии: + +- IPv4, например `192.168.1.10`; +- IPv6, например `2001:db8::10`. + +Адрес может быть public или private, static или dynamic. За NAT несколько устройств private network могут выходить во +внешнюю сеть через один public address. -IP-адрес отвечает на вопрос "к какой машине или сетевому интерфейсу обратиться", а порт отвечает на вопрос "к какой -программе внутри этой машины обратиться". +IP не всегда однозначно идентифицирует физическое устройство или пользователя: + +- у устройства несколько interfaces и addresses; +- address может измениться; +- proxy, VPN и NAT скрывают исходный address; +- один server address может вести к load balancer; +- domain может разрешаться в несколько addresses. + +IP отвечает за доставку между network endpoints, а transport port помогает выбрать приложение внутри host. Domain name +является удобным именем и разрешается в address через DNS. @@ -480,16 +775,30 @@ IP-адрес отвечает на вопрос "к какой машине и **Короткий ответ** -DNS - система, которая сопоставляет доменные имена с сетевыми адресами и другой служебной информацией. Благодаря DNS -пользователь вводит example.com, а не запоминает IP-адрес сервера. +DNS - распределенная система имен, которая сопоставляет domain names с IP-адресами и другими records. **Полный ответ** -DNS - система, которая сопоставляет доменные имена с сетевыми адресами и другой служебной информацией. Благодаря DNS -пользователь вводит `example.com`, а не запоминает IP-адрес сервера. +Когда приложению нужен `example.com`, resolver сначала проверяет доступные caches. Если ответа нет, запрос проходит +через recursive DNS resolver, который при необходимости обращается к иерархии DNS-серверов. + +Частые record types: + +- `A` - IPv4 address; +- `AAAA` - IPv6 address; +- `CNAME` - alias; +- `MX` - mail servers; +- `TXT` - текстовые данные и verification; +- `NS` - authoritative name servers. -Перед сетевым соединением клиент обычно должен узнать, в какой IP-адрес разрешается доменное имя. Ответ может прийти из -локального кеша, от провайдера, корпоративного DNS или публичного DNS-сервера. +Ответ имеет TTL, поэтому изменения DNS распространяются не мгновенно: разные resolvers могут хранить старое значение до +истечения cache. + +DNS только помогает найти сетевой адрес и другие records. Он не устанавливает HTTP connection и не выполняет redirect +страницы. После resolution client еще должен соединиться с нужным endpoint и выполнить protocol приложения. + +Для frontend-разработки DNS важен при настройке domains, CDN, certificates, CORS и диагностике ситуации, когда domain +уже изменен, но часть users видит старый address. @@ -501,16 +810,24 @@ DNS - система, которая сопоставляет доменные **Короткий ответ** -Socket - программная абстракция сетевого соединения или сетевого endpoint. Через socket программа отправляет и получает -данные по сети. +Socket - программная абстракция network endpoint. Через socket process отправляет и получает bytes или datagrams. **Полный ответ** -Socket - программная абстракция сетевого соединения или сетевого endpoint. Через socket программа отправляет и получает -данные по сети. +Socket является объектом ОС, через который приложение работает с network stack. Он связан с protocol, local endpoint и, +для установленного соединения, remote endpoint. + +Для TCP listening socket принимает новые connections. После `accept` ОС создает отдельный connected socket, через +который server обменивается ordered byte stream с конкретным client. + +UDP socket отправляет и принимает отдельные datagrams без обязательного постоянного connection. UDP не гарантирует +delivery, order или отсутствие duplicates на уровне protocol. -Для TCP-соединения socket связан с адресом, портом и состоянием соединения. Для UDP socket может отправлять и принимать -отдельные datagrams без постоянного соединения. +Socket API лежит ниже HTTP и WebSocket. **WebSocket** - application protocol с handshake и frames, который обычно +работает поверх TCP socket. Эти понятия нельзя использовать как синонимы. + +Приложение должно учитывать partial reads/writes, buffering, timeouts, closed connections и resource cleanup. То, что +`write` принял данные в local buffer, не всегда означает, что remote application уже их обработало. @@ -522,16 +839,36 @@ Socket - программная абстракция сетевого соеди **Короткий ответ** -npm run dev запускает script из package.json. Обычно этот script стартует dev server фреймворка или сборщика: Angular -CLI, Vite, Astro, Next.js и другие инструменты. +`npm run dev` выполняет script `dev` из `package.json`. Обычно этот script запускает dev server framework или build +tool. **Полный ответ** -`npm run dev` запускает script из `package.json`. Обычно этот script стартует dev server фреймворка или сборщика: -Angular CLI, Vite, Astro, Next.js и другие инструменты. +`npm` не имеет встроенного универсального dev server. Он находит запись: + +```json +{ + "scripts": { + "dev": "vite" + } +} +``` + +и запускает указанную command в shell. На время script `npm` добавляет `node_modules/.bin` в `PATH`, поэтому локально +установленный `vite`, `astro` или другой binary доступен без полного пути. + +Dev server является обычным process. Обычно он: -Dev server - это обычный процесс на компьютере разработчика. Он слушает порт, отдает файлы браузеру, пересобирает проект -при изменениях и может поддерживать hot reload или HMR. +- bind-ится к port; +- отдает HTML, JavaScript и assets; +- наблюдает за files; +- запускает incremental rebuild; +- сообщает browser об изменениях через WebSocket; +- выполняет HMR или reload; +- иногда proxy requests к backend. + +Это development tooling, а не production server по умолчанию. Production flow обычно сначала создает optimized build, а +затем размещает static files на CDN/web server или запускает специально настроенный application server. @@ -543,16 +880,22 @@ Dev server - это обычный процесс на компьютере ра **Короткий ответ** -Браузер и dev server - разные процессы. Вкладка браузера является клиентом, а dev server продолжает работать в терминале -или IDE, пока его процесс не завершится. +Вкладка браузера и dev server - разные процессы. Вкладка является client, а server продолжает работать, пока не завершен +его process. **Полный ответ** -Браузер и dev server - разные процессы. Вкладка браузера является клиентом, а dev server продолжает работать в терминале -или IDE, пока его процесс не завершится. +Browser tab только подключается к dev server по HTTP и, возможно, WebSocket. При закрытии вкладки ее connections +закрываются, но server process остается запущенным в terminal, IDE, container или background session. + +Чтобы остановить foreground process в terminal, обычно отправляют `SIGINT` через `Ctrl+C`. IDE может иметь отдельную +кнопку stop. Если process был запущен через `&`, `nohup`, `tmux`, process manager или service manager, закрытие terminal +также может его не завершить. + +Это объясняет частую ошибку `EADDRINUSE`: старый server все еще слушает port, хотя вкладка браузера уже закрыта. -Поэтому закрытие вкладки обычно прекращает только клиентское соединение. Чтобы остановить сервер, нужно остановить -процесс dev server, например через `Ctrl+C` в терминале, кнопку stop в IDE или команду управления процессами. +Server может заметить disconnect конкретного client и освободить связанные ресурсы, но отсутствие clients не означает, +что process обязан завершиться. Он продолжает watch files и ждать новые connections.