Пара 3 / октябрь
Shell, структура каталогов и текстовые утилиты Безопасно создавать, читать, копировать и искать файлы; понимать кавычки.
Безопасно создавать, читать, копировать и искать файлы; понимать кавычки. Пробел обычно разделяет аргументы. Путь с пробелом нужно заключить в кавычки. Двойные кавычки сохраняют подстановку переменных, одинарные передают текст буквально. Звёздочку shell может раскрыть в список имён до запуска программы. Это объясняет, почему rm *.txt получает уже список файлов. -- во многих GNU-утилитах завершает параметры: имя, начинающееся с дефиса, после этого рассматривается как операнд. Не все программы имеют одинаковые флаги; проверяйте man или --help.
Контекст Shell превращает командную строку в запуск с аргументами и окружением.
Безопасно создавать, читать, копировать и искать файлы; понимать кавычки.
Terminal или terminal emulator обеспечивает взаимодействие через псевдотерминал. Bash, zsh, dash, fish — программы, интерпретирующие команды. POSIX sh задаёт переносимый интерфейс, но /bin/sh не обязан быть Bash: в Ubuntu часто используется dash. Bash добавляет собственные конструкции; zsh тоже имеет особенности, fish не стремится быть POSIX shell. Shebang #!/usr/bin/env bash выбирает interpreter для исполняемого скрипта, а запуск bash script.sh выбирает его явно. Команда cd обычно builtin: дочерний процесс не мог бы изменить текущий каталог родителя. Export меняет окружение будущих детей; обычное присваивание shell-переменной не всегда экспортируется. Alias — удобство интерактивного shell, а не надёжная dependency скрипта. PATH содержит каталоги для поиска внешних программ; command -v помогает увидеть реальное разрешение имени.
Команда — имя и аргументы Shell разделяет слова до запуска программы.
01 Строка команды
→ 02 Разбор Bash
→ 03 Аргументы
→ 04 Запуск программы
Аналогия: упаковка заказа в отдельные коробки; кавычки удерживают слова вместе.
Пробел обычно разделяет аргументы. Путь с пробелом нужно заключить в кавычки. Двойные кавычки сохраняют подстановку переменных, одинарные передают текст буквально. Звёздочку shell может раскрыть в список имён до запуска программы. Это объясняет, почему rm *.txt получает уже список файлов. -- во многих GNU-утилитах завершает параметры: имя, начинающееся с дефиса, после этого рассматривается как операнд. Не все программы имеют одинаковые флаги; проверяйте man или --help.
Сначала осмотр, затем изменение pwd и ls снижают риск работы не в той папке.
01 pwd / ls
→ 02 Проверка цели
→ 03 cp / mv
→ 04 Сравнение результата
Аналогия: прежде чем переносить коробки, проверьте номер склада.
Терминал не всегда спрашивает подтверждение. cp копирует, mv перемещает или переименовывает, rm удаляет имя. Удаление в shell обычно не отправляет в корзину Windows. На курсе работаем в ~/devops-course/sandbox и используем rm -i для небольших опытов. Не учите привычку sudo перед любой ошибкой: отказ может говорить о неверном пути. Перед изменением прочитайте команду вслух: что является источником, что целью, есть ли перезапись? История команд полезна, но может содержать введённые секреты, поэтому пароли в аргументах не вводим.
Поиск и чтение Ищем имя отдельно от текста внутри файла.
01 find: имя
→ 02 grep: текст
→ 03 less: чтение
→ 04 wc: количество
Имена и содержимое — разные пространства поиска.
find ищет файловые объекты по условиям: имя, тип, размер. grep ищет совпадения в тексте. head и tail показывают начало и конец, less позволяет читать большой файл без загрузки всего текста на экран. wc -l считает строки. grep по умолчанию использует регулярные выражения; grep -F ищет буквальный текст. Выбор простого инструмента — инженерное решение: не нужен Python-скрипт ради одного поиска. Учебный файл мал, но команды пригодятся для логов и результатов экспериментов. Объясните, что grep не понимает смысл модели и не проверяет JSON как структурированный документ.
Terminal, shell и утилиты — три разных слоя Shell превращает командную строку в запуск с аргументами и окружением.
Terminal показывает ввод/вывод, shell разбирает команду, утилита выполняет работу Terminal / PTY Окно и поток ввода Bash / zsh / fish Разбор и запуск grep / sed / Python Отдельные процессы Ядро Процессы, файлы, I/O Builtins: cd, export, read Меняют состояние самого shell Внешние команды: grep, ls Дочерняя программа получает argv / env sh — интерфейс/имя shell, а не всегда Bash. Fish не является POSIX shell. Кавычки, expansion и разбиение argv происходят до исполнения утилиты. На Mac интерактивный zsh и системный старый Bash — разные среды.
Terminal или terminal emulator обеспечивает взаимодействие через псевдотерминал. Bash, zsh, dash, fish — программы, интерпретирующие команды. POSIX sh задаёт переносимый интерфейс, но /bin/sh не обязан быть Bash: в Ubuntu часто используется dash. Bash добавляет собственные конструкции; zsh тоже имеет особенности, fish не стремится быть POSIX shell. Shebang #!/usr/bin/env bash выбирает interpreter для исполняемого скрипта, а запуск bash script.sh выбирает его явно. Команда cd обычно builtin: дочерний процесс не мог бы изменить текущий каталог родителя. Export меняет окружение будущих детей; обычное присваивание shell-переменной не всегда экспортируется. Alias — удобство интерактивного shell, а не надёжная dependency скрипта. PATH содержит каталоги для поиска внешних программ; command -v помогает увидеть реальное разрешение имени.
Кавычки, expansion и разбиение argv происходят до исполнения утилиты. На Mac интерактивный zsh и системный старый Bash — разные среды.
Файл .env скрыт только от обычного списка В Linux точка — соглашение об имени, а не особый секретный тип файла.
Точка в начале имени влияет на обычный показ, а не на права доступа Каталог report.csv, .env, .config/ ls Обычный список report.csv — имена с точкой обычно опущены ls -a Полный список report.csv, .env, .config/, . и .. Скрытие имени не шифрует данные и не запрещает cat .env. chmod и секреты — отдельные механизмы. ls -a показывает скрытые имена; ls -A опускает специальные записи . и .. Hidden не означает encrypted или unreadable.
У имени .env нет автоматического запрета чтения: ls без -a обычно не показывает имена, начинающиеся с точки, но cat .env читает их при разрешённом доступе. . — текущий каталог, .. — родительский; это специальные записи, а не обычные пользовательские файлы. .config хранит пользовательские настройки по распространённому соглашению XDG; .bashrc и .zshrc относятся к соответствующим shell. Windows имеет отдельный атрибут hidden, поэтому похожее отображение достигается другим механизмом. Gitignore тоже не защищает секрет: он влияет на выбор неотслеживаемых файлов, но уже закоммиченный .env остаётся в истории. На практике применяют доступ по правам, разделение конфигурации и секретов, исключение из репозитория и ротацию случайно раскрытого секрета. Не ставьте chmod 777 ради чтения конфигурации.
ls -a показывает скрытые имена; ls -A опускает специальные записи . и .. Hidden не означает encrypted или unreadable.
/etc, /var, /usr, /tmp: роли каталогов По роли данных можно выбрать место хранения и область диагностики.
Роли основных каталогов Linux / Корень /etc Конфигурация хоста /var Логи, cache, spool, state /home Личные данные /usr Программы и данные системы /run Runtime state текущей загрузки /tmp Временные файлы /proc и /sys Интерфейсы сведений ядра /dev Устройства и специальные объекты Настройки, код, runtime state и пользовательские данные имеют разные сроки жизни и требования к резервному копированию.
/etc содержит конфигурацию конкретного хоста; современный смысл важнее спорной расшифровки имени. /usr содержит установленный userland: программы, библиотеки и общие данные. /var предназначен для изменяемых данных — журналов, очередей и состояния приложений. /home содержит домашние каталоги; /root — домашний каталог root, а не синоним /. /run хранит состояние текущей загрузки; /tmp — временные файлы. /proc и /sys — интерфейсы виртуальных файловых систем со сведениями ядра, /dev — специальные объекты устройств. На merged-/usr системах /bin и /sbin могут быть ссылками внутрь /usr, поэтому старый рисунок «каждый каталог отдельный диск» неверен. FHS описывает соглашения, но конкретное приложение или контейнер может выбирать другое размещение. Всегда проверяем эффективный config и mounts.
Настройки, код, runtime state и пользовательские данные имеют разные сроки жизни и требования к резервному копированию.
grep, sed и awk решают разные задачи Текстовый поток можно отобрать, преобразовать и разобрать без изменения оригинала.
grep выбирает строки, sed преобразует поток, awk работает с полями events.log INFO 200 /healthz grep -F ERROR Строки с буквальным ERROR Исходный текст status=failed sed 's/failed/retry/' Преобразованный stdout; исходный файл не изменён sed -i меняет файл и различается между GNU/BSD. Для знакомства сначала выводим результат отдельно. Фильтрацию и преобразование сначала выводим в новый файл; sed -i используется после проверки и с учётом различий GNU/BSD.
Grep выбирает строки по совпадению. -F ищет буквальный текст, -E использует расширенное регулярное выражение, -n добавляет номер строки. Sed применяет команды к потоку: s/old/new/ меняет первое совпадение в строке, флаг g — все подходящие совпадения. Без -i результат идёт в stdout, исходный файл сохраняется. Awk работает с записями и полями, умеет фильтрацию и вычисления; его стандартное разделение по whitespace не является полноценным CSV-парсером с кавычками. Sort упорядочивает строки, uniq объединяет соседние одинаковые, поэтому для полного подсчёта обычно нужны sort | uniq -c. Locale влияет на порядок и классы символов; LC_ALL=C полезен, когда нужен определённый байтовый порядок. GNU/BSD варианты флагов различаются. Для JSON выбираем JSON-парсер, а не пытаемся извлечь вложенную структуру регулярным выражением.
Фильтрацию и преобразование сначала выводим в новый файл; sed -i используется после проверки и с учётом различий GNU/BSD.
Наблюдение механизмов mkdir -p ~/devops-course/text-demo
cd ~/devops-course/text-demo
printf "INFO status=ok\nERROR status=failed\n" > events.log
printf "local-setting\n" > .settings
ls
ls -a
grep -n -F ERROR events.log
sed 's/failed/retry/' events.log
awk '{print $1}' events.log
command -v sh bash grep sedПолный разбор команд в конспекте → Сохраните отфильтрованные ERROR в отдельный файл, сравните оригинал и результат. Работающие команды не требуют sudo. Прочитайте man grep / man sed своей ОС перед использованием расширенных флагов.
Команды и наблюдения Ubuntu / Bash
mkdir -p ~/devops-course/sandbox
cd ~/devops-course/sandbox
printf "OK run=1
ERROR run=2
OK run=3
" > "run log.txt"
cp -- "run log.txt" backup.txt
grep -n -F ERROR "run log.txt"
find . -type f -name "*.txt"
wc -l "run log.txt"
cmp "run log.txt" backup.txtКавычки делают run log.txt одним аргументом; шаблон find заключён в кавычки, чтобы его обработал find, а не shell. Ожидаемое наблюдение: grep находит строку 2; wc показывает 3; cmp молчит и возвращает 0.
Разбор результата grep находит строку 2; wc показывает 3; cmp молчит и возвращает 0.
Кавычки делают run log.txt одним аргументом; шаблон find заключён в кавычки, чтобы его обработал find, а не shell.
Используйте mkdir -p backup; cp -- ./*.txt backup/ до появления новых текстовых файлов в текущей папке. grep -n -F ERROR "run log.txt". Для подсчёта этих учебных имён find . -maxdepth 1 -type f -name "*.txt" | wc -l. Поясните ограничение подсчёта по строкам для необычных имён с переводом строки; в этом упражнении такие имена не создаём.
На macOS mkdir, cp, grep, find, head, tail, less, wc и cmp доступны. Linux find -maxdepth не считайте переносимым; для поиска по всему учебному дереву достаточно команды выше. BSD и GNU-флаги могут различаться. Проверяйте man на своей системе.
mkdir -p ~/devops-course/sandbox
cp -- "run log.txt" backup.txt
grep -n -F ERROR "run log.txt"
find . -type f -name "*.txt"
wc -l "run log.txt"Команды macOS необходимо проверить на конкретном устройстве. mkdir, cp, grep, find, head, tail, less, wc и cmp доступны. Linux find -maxdepth не считайте переносимым; для поиска по всему учебному дереву достаточно команды выше. BSD и GNU-флаги могут различаться. Проверяйте man на своей системе.
Архив экспериментальных запусков Создайте три файла, один с пробелом в имени. Скопируйте их в отдельную папку backup без изменения оригиналов. Найдите строки ERROR и посчитайте файлы через find. Переименуйте один файл и удалите только специально созданную копию с rm -i. Работа в парах: один оператор, второй проверяет гипотезу. Смена ролей в середине. Используйте mkdir -p backup; cp -- ./*.txt backup/ до появления новых текстовых файлов в текущей папке. grep -n -F ERROR "run log.txt". Для подсчёта этих учебных имён find . -maxdepth 1 -type f -name "*.txt" | wc -l. Поясните ограничение подсчёта по строкам для необычных имён с переводом строки; в этом упражнении такие имена не создаём.
Что должно получиться history-команды без секретов и найденные строки с номерами.
Оригиналы сохранены; все пути с пробелами обработаны корректно.
Используйте mkdir -p backup; cp -- ./*.txt backup/ до появления новых текстовых файлов в текущей папке. grep -n -F ERROR "run log.txt". Для подсчёта этих учебных имён find . -maxdepth 1 -type f -name "*.txt" | wc -l. Поясните ограничение подсчёта по строкам для необычных имён с переводом строки; в этом упражнении такие имена не создаём.
Ошибка для диагностики Неэкранированный пробел разбил имя на два аргумента.
Симптом → Гипотеза → Проверка
Используйте mkdir -p backup; cp -- ./*.txt backup/ до появления новых текстовых файлов в текущей папке. grep -n -F ERROR "run log.txt". Для подсчёта этих учебных имён find . -maxdepth 1 -type f -name "*.txt" | wc -l. Поясните ограничение подсчёта по строкам для необычных имён с переводом строки; в этом упражнении такие имена не создаём.
Основные выводы Обычно в shell до запуска команды, если шаблон не заключён в кавычки. Ищет буквальную строку без интерпретации регулярного выражения. Обычно в shell до запуска команды, если шаблон не заключён в кавычки.
Ищет буквальную строку без интерпретации регулярного выражения.
После пары Создать шпаргалку из восьми команд: цель, пример, риск неправильного применения.
Конспект, команды и разбор → Проверьте критерии сдачи и дайте возможность повторной попытки. Первоисточники: https://ubuntu.com/server/docs/ https://www.gnu.org/software/bash/manual/bash.html
Конвейер: порядок операций имеет значение A
B
A
A
B uniq -c sort | uniq -c Выберите порядок обработки Покажите, что uniq объединяет только соседние строки. Предсказание делается до нажатия кнопки.
Пара 4 / октябрь
VFS, библиотеки, память и конвейеры Соединять команды, проверять код возврата и писать небольшой повторяемый скрипт.
Соединять команды, проверять код возврата и писать небольшой повторяемый скрипт. У процесса обычно есть стандартный вход с номером 0, выход с номером 1 и поток ошибок с номером 2. Символ > перенаправляет stdout и перезаписывает файл; >> добавляет. Ошибка может продолжать появляться в терминале, потому что идёт по stderr. Запись 2> сохраняет диагностику отдельно, 2>&1 направляет её туда, куда уже направлен stdout; порядок перенаправлений имеет значение. Конвейер | передаёт stdout одной команды на stdin следующей. Он не передаёт stderr автоматически. Разделение помогает не смешивать полезные CSV-данные с сообщениями об ошибках.
Контекст Одинаковые open/read работают поверх разных файловых систем.
Соединять команды, проверять код возврата и писать небольшой повторяемый скрипт.
VFS — слой внутри Linux-ядра, объединяющий файловые интерфейсы. При разрешении пути dentry связывает имя с объектом, inode представляет сведения о самом объекте, а file description хранит состояние конкретного открытия, например позицию и flags. Дескриптор процесса указывает на открытый объект. Вызов read обращается к операциям конкретной файловой системы: ext4, tmpfs, procfs, NFS и другие имеют разные источники данных. Это объясняет и единое дерево, и то, почему «всё — файл» полезно как соглашение об интерфейсах, но не является буквальным описанием любого объекта ядра. Каталог хранит связи имён, hard link создаёт ещё одну связь с inode, symlink хранит путь. Расширение .csv не проверяет содержимое. Сеть и устройства могут быть доступны через дескрипторы, но операции и свойства отличаются от обычного дискового файла.
Три стандартных потока stdin — вход; stdout — результат; stderr — диагностика.
01 stdin → процесс
02 stdout → данные
03 stderr → ошибки
Аналогия: готовые блюда и сообщения о поломке кухни идут по разным каналам.
У процесса обычно есть стандартный вход с номером 0, выход с номером 1 и поток ошибок с номером 2. Символ > перенаправляет stdout и перезаписывает файл; >> добавляет. Ошибка может продолжать появляться в терминале, потому что идёт по stderr. Запись 2> сохраняет диагностику отдельно, 2>&1 направляет её туда, куда уже направлен stdout; порядок перенаправлений имеет значение. Конвейер | передаёт stdout одной команды на stdin следующей. Он не передаёт stderr автоматически. Разделение помогает не смешивать полезные CSV-данные с сообщениями об ошибках.
Конвейер обработки Каждый инструмент выполняет небольшой понятный шаг.
01 grep
→ 02 cut
→ 03 sort
→ 04 uniq -c
Аналогия: конвейер сортировки посылок; плохой шаг загрязняет весь результат.
Из учебного журнала выберем ошибки, выделим значение run и посчитаем повторения. Конвейер удобно читать слева направо. sort упорядочивает строки, uniq -c считает соседние одинаковые строки, поэтому перед подсчётом обычно нужен sort. Команды не знают бизнес-смысл: если формат изменится, cut может извлечь не то поле. Для JSON используем структурный парсер, а не бесконечные grep. Сейчас работаем с контролируемым простым текстом и явно обсуждаем границу.
Код возврата и повторяемость Успех команды — обычно код 0, а не отсутствие текста.
01 Вход
→ 02 Проверки
→ 03 Обработка
→ 04 Код возврата
Успешно напечатать пустой результат и успешно решить задачу — не всегда одно и то же.
Переменная $? содержит код завершения последней команды. Операторы && и || позволяют запускать следующий шаг при успехе или неуспехе. В Bash код конвейера по умолчанию относится к последней команде; set -o pipefail позволяет увидеть неуспех более раннего звена. set -e не универсальная защита: ожидаемые неуспехи нужно обрабатывать явно. grep возвращает 1, когда совпадений нет, и это иногда нормальный результат. Переменные заключаем в двойные кавычки, не используем eval для входных данных. Скрипт должен иметь понятный выход и ошибку, которую можно объяснить.
Куда на самом деле пишет stdout Shell настраивает дескрипторы перед запуском программы.
Перенаправление связывает файловые дескрипторы процесса с объектами ядра Процесс: python Таблица дескрипторов 0 stdin 1 stdout 2 stderr Терминал Ввод пользователя out.log > out.log err.log 2> err.log Дескриптор — целое число в процессе, не имя файла. Возможны pipe, socket, device. Нет. Она пишет в stdout; shell уже настроил дескриптор.
У процесса есть таблица открытых файловых дескрипторов. Традиционные номера 0, 1 и 2 означают stdin, stdout и stderr, но сами числа не говорят, какой объект подключён. Это могут быть терминал, обычный файл, pipe или socket. При python demo.py >out.log 2>err.log shell открывает объекты и связывает дескрипторы будущего процесса до выполнения программы. Программа пишет в 1, не выбирая экран. После fork ребёнок наследует дескрипторы, при exec открытые дескрипторы обычно сохраняются, если не помечены close-on-exec. Важно различать таблицу дескрипторов процесса и open file description ядра: разные дескрипторы могут ссылаться на одно описание и общий offset. Для первого занятия достаточно отследить маршруты байтов. Покажите print в stdout и stderr, затем откройте оба файла отдельно.
Аналогия: трубка с номером 1 подключена к выбранному приёмнику. Дескриптор не хранит содержимое файла.
Нет. Она пишет в stdout; shell уже настроил дескриптор.
Конвейер создаёт два процесса и канал Pipe передаёт байты и заставляет ждать при пустом или полном буфере.
Два процесса соединены буфером pipe в ядре printf stdout: FD 1 wc -l stdin: FD 0 pipe в ядре Байты: запись → чтение Shell создаёт pipe и процессы dup2 настраивает 1 и 0 перед exec EOF после закрытия всех writers Читатель не знает смысл данных Конвейер передаёт поток байтов; stdout не обязан вести на экран. Когда все записывающие концы pipe закрыты и чтение возвращает EOF.
Shell создаёт pipe и организует процессы по обе стороны. Записывающий конец становится stdout первой программы, читающий — stdin второй. Это не передача имени файла и не запуск второй программы только после полного окончания первой: программы могут работать одновременно. Ядро хранит ограниченный буфер, поэтому быстрый writer может ждать медленного reader; пустой pipe заставляет reader ждать данных. После закрытия всех записывающих концов reader получает EOF. Если лишний writer-дескриптор остался открыт, читатель может продолжать ждать. В опыте printf печатает несколько строк, wc -l считает переводы строк. Просим объяснить, почему терминал видит результат wc, но не исходный поток printf. Детали dup2 даём в конспекте, код системного программирования на C не требуем.
Аналогия: две станции соединены узкой лентой. В действительности передаются байты без знания их смысловой структуры.
Когда все записывающие концы pipe закрыты и чтение возвращает EOF.
VFS даёт общий файловый интерфейс Одинаковые open/read работают поверх разных файловых систем.
VFS связывает путь и открытый файл с реализацией файловой системы open("/data/x") Запрос процесса dentry + inode Имя и объект file description Открытие, flags, offset file operations Реализация FS ext4 / XFS Файлы на носителе tmpfs / procfs Память или данные ядра NFS / другие Другой источник данных Имя, inode и открытый файловый объект — разные сущности; расширение не определяет формат. Путь и inode не тождественны. Разные имена могут указывать на один объект, а одно имя со временем — на разные объекты.
VFS — слой внутри Linux-ядра, объединяющий файловые интерфейсы. При разрешении пути dentry связывает имя с объектом, inode представляет сведения о самом объекте, а file description хранит состояние конкретного открытия, например позицию и flags. Дескриптор процесса указывает на открытый объект. Вызов read обращается к операциям конкретной файловой системы: ext4, tmpfs, procfs, NFS и другие имеют разные источники данных. Это объясняет и единое дерево, и то, почему «всё — файл» полезно как соглашение об интерфейсах, но не является буквальным описанием любого объекта ядра. Каталог хранит связи имён, hard link создаёт ещё одну связь с inode, symlink хранит путь. Расширение .csv не проверяет содержимое. Сеть и устройства могут быть доступны через дескрипторы, но операции и свойства отличаются от обычного дискового файла.
Путь и inode не тождественны. Разные имена могут указывать на один объект, а одно имя со временем — на разные объекты.
Удаление имени и освобождение данных — не один момент Открытый файл может продолжать существовать после rm.
Удаление имени не обязательно сразу освобождает открытый файл Имя events.log Запись каталога inode / содержимое Объект файловой системы FD процесса Файл ещё открыт unlink / rm Имя убрано из каталога Место освобождается позже Когда не осталось hard links и удерживающих открытий Причина случая «rm большой лог, но df не изменился»: процесс продолжает держать открытый файл. Для освобождения места нужно убрать удерживающие ссылки и открытия, а не повторить rm несуществующего имени.
Unlink удаляет связь имени в каталоге. Если у объекта есть другие hard links или его держит открытый file description, данные ещё могут оставаться доступны и занимать место. Типичный инцидент: удалили большой журнал, а df не показывает свободное место; процесс продолжает писать в открытый, уже безымянный файл. Смотрят lsof +L1 или соответствующие /proc/PID/fd для доступных процессов, затем используют поддерживаемый способ переоткрытия журнала или штатный рестарт конкретной службы. Не убивают все процессы и не очищают /var/log целиком. Разница df и du также может происходить из-за mounts, недоступных каталогов, reserved space и других причин — один симптом не доказывает удалённый открытый файл. При учебной демонстрации используем маленький tempfile, который автоматически удаляется, не заполняем диск.
Для освобождения места нужно убрать удерживающие ссылки и открытия, а не повторить rm несуществующего имени.
Shared library загружает динамический linker Библиотека содержит код; ABI задаёт правила связи с программой.
ELF и динамический загрузчик связывают программу с shared libraries exec: ELF Код и ELF metadata PT_INTERP Динамический loader Зависимости .so Проверка ABI, поиск mmap и relocations Образ процесса Общие read-only страницы кода Могут физически разделяться процессами Изменяемые данные Не становятся автоматически общими Linux .so, macOS .dylib/frameworks, Windows DLL. Совпадение имени не гарантирует совместимый ABI. Ubuntu: file /usr/bin/ls; readelf -l /usr/bin/ls; readelf -d /usr/bin/ls. Mac: file /bin/ls; otool -L /bin/ls.
ELF-файл может указывать interpreter через PT_INTERP. При exec ядро организует начальное отображение, а динамический linker ищет необходимые shared libraries, проверяет ожидаемые зависимости и выполняет relocations. .so — Linux-библиотека, .dylib/framework — соответствующие механизмы macOS, DLL — Windows. Read-only страницы библиотечного кода могут разделяться между процессами, тогда как изменяемые данные каждого экземпляра остаются отдельными. Не все программы динамические: статическая сборка включает зависимости иначе. Ошибка «No such file or directory» для существующего ELF может означать отсутствующий interpreter, а не отсутствующий app. ABI mismatch нельзя исправлять скачиванием случайного libc.so. Ldd не выполняют на недоверенных файлах: безопаснее сначала читать metadata через readelf или objdump. Readelf также подходит для понимания архитектуры и dependencies.
Ubuntu: file /usr/bin/ls; readelf -l /usr/bin/ls; readelf -d /usr/bin/ls. Mac: file /bin/ls; otool -L /bin/ls.
RAM нужна для исполнения, но не всё копируется заранее Виртуальная память отображает нужные страницы по мере обращения.
Адресное пространство не равно заранее занятой физической RAM Виртуальные адреса Код, библиотеки, heap, stack Таблицы страниц Связь виртуальных адресов и физических страниц Page fault Нужная страница ещё не доступна Выделить / прочитать страницу Demand paging; доступ проверяет ядро CPU исполняет доступные страницы через память; приложение не обязано загружать весь файл целиком. Исполняются доступные отображённые страницы. Большой файл модели может читаться постепенно или через mmap, а не обязательно целиком до старта.
CPU работает с адресами памяти, а ОС и аппаратная MMU связывают виртуальные адреса с физическими страницами. Код, stack, heap и libraries составляют адресное пространство. Mapping файла не равен немедленному чтению всех его байтов в физическую RAM: demand paging позволяет загрузить нужную страницу при обращении. Page fault — механизм, который может быть нормальным; он не тождественен падению приложения. Ядро также держит page cache, чтобы повторные чтения файлов не требовали каждого обращения к диску. Поэтому занятая RAM не вся принадлежит уникальным данным приложений. VSZ описывает виртуальный размер, RSS — резидентные страницы с особенностями учёта shared mappings. Суммирование RSS всех процессов может учитывать общие страницы многократно. Не путайте RAM с диском, виртуальным адресным пространством или видеопамятью.
Исполняются доступные отображённые страницы. Большой файл модели может читаться постепенно или через mmap, а не обязательно целиком до старта.
Swap помогает вытеснять страницы, но не заменяет RAM Cache, anonymous memory и swap участвуют в разных решениях reclaim.
Page cache и swap сохраняют разные виды страниц при дефиците памяти Физическая RAM Код, данные, cache reclaim Чистая file-backed страница Можно отбросить и позже перечитать файл swap out Anonymous / изменённые данные Могут быть выгружены в swap; затем swap in free: available важнее одного free. Swappiness — политика стоимости I/O, не процент занятой RAM. Ubuntu: free -h; swapon --show; vmstat 1 3. Mac: vm_stat; sysctl vm.swapusage; Activity Monitor показывает другую модель учёта.
Чистую file-backed страницу ядро может удалить из RAM и затем перечитать из исходного файла. Для anonymous или изменённых страниц нужен другой способ сохранить содержимое, например swap. Swap может быть разделом, файлом или связанным с memory compression механизмом; zram и zswap не являются простыми синонимами дискового swap. Swappiness задаёт соотношение оценённой стоимости swap-I/O и файлового paging, а не процент RAM, при котором внезапно начинается swap. free показывает used/free/buff/cache/available: для оценки запаса полезнее available, учитывающий возможность reclaim. Постоянная интенсивная выгрузка/возврат страниц ухудшает отзывчивость; vmstat si/so полезны вместе с CPU, I/O и журналом. При нехватке памяти возможен OOM-kill с записью в kernel journal. Для демонстрации читаем показатели и не вызываем намеренный OOM на ноутбуке.
Ubuntu: free -h; swapon --show; vmstat 1 3. Mac: vm_stat; sysctl vm.swapusage; Activity Monitor показывает другую модель учёта.
/tmp — назначение каталога, tmpfs — тип хранения Временное имя не гарантирует RAM и конкретный срок очистки.
Временные данные различаются по времени жизни и расположению /tmp Временная работа программы /var/tmp Дольше между загрузками /run Runtime state после boot tmpfs — тип файловой системы Данные в памяти; возможен swap, зависит от настроек /tmp ≠ всегда tmpfs Проверить findmnt Очистку определяет политика дистрибутива. /tmp не место для единственной копии результата. findmnt -T /tmp показывает реальное хранение. tmpfs может обмениваться страницами со swap; после reboot данные не являются постоянным архивом.
/tmp используется для временной работы и не годится для единственной копии результата. Его содержимое очищается согласно политике системы; /var/tmp предназначен для более длительных временных данных, /run — для runtime state текущей загрузки. Tmpfs хранит данные в виртуальной памяти и может использовать swap согласно конфигурации. /tmp может быть tmpfs или обычной дисковой файловой системой, поэтому проверяем findmnt -T /tmp, а не делаем вывод из имени. RAM-диск и tmpfs — не всегда один механизм. На общем /tmp действует sticky bit, но это не отменяет безопасного создания файлов: mktemp выбирает уникальное имя и помогает избежать подмены предсказуемого пути. Не очищайте чужие временные файлы ради лабораторной. Свою временную директорию программа должна удалять после использования.
findmnt -T /tmp показывает реальное хранение. tmpfs может обмениваться страницами со swap; после reboot данные не являются постоянным архивом.
Наблюдение механизмов printf "A\nB\n" | wc -l
python3 -c 'import sys; print("result"); print("diagnostic", file=sys.stderr)' >out.log 2>err.log
cat out.log
cat err.log
file /usr/bin/ls
# Optional when binutils is installed
readelf -l /usr/bin/ls | grep -F interpreter
readelf -d /usr/bin/ls | grep -F NEEDED
free -h
swapon --showПолный разбор команд в конспекте → До запуска нарисуйте FD 0/1/2 для обеих программ. После запуска объясните, какие именно байты попали в каждый файл. Команды одинаковы в Ubuntu и macOS при наличии python3.
Свяжите VFS-схему с путём, descriptor и inode маленького tempfile. Сопоставьте free и ps без выделения больших массивов. Команды binutils устанавливаются заранее; readelf не является обязательной утилитой macOS.
Команды и наблюдения Ubuntu / Bash
cd ~/devops-course/sandbox
printf "ERROR api
OK worker
ERROR api
ERROR db
" > events.log
grep -F ERROR events.log | cut -d " " -f 2 | sort | uniq -c
ls missing-file > result.txt 2> error.txt
printf "exit=%s
" "$?"
cat error.txtФайл result.txt может быть пустым при ошибке. Проверяем код непосредственно после команды, пока другая команда не заменила его. Ожидаемое наблюдение: api встречается дважды, db один раз; ls возвращает ненулевой код, диагностика находится в error.txt.
Разбор результата api встречается дважды, db один раз; ls возвращает ненулевой код, диагностика находится в error.txt.
Файл result.txt может быть пустым при ошибке. Проверяем код непосредственно после команды, пока другая команда не заменила его.
Пример: file=${1:-}; if [[ ! -f "$file" ]]; then printf "File not found\n" >&2; exit 2; fi; lines=$(wc -l < "$file"); errors=$(grep -c -F ERROR "$file" || true); printf "lines=%s errors=%s\n" "$lines" "$errors". Здесь || true допустим после проверки читаемого учебного файла для ожидаемого кода 1; усиление решения — проверить отдельно коды grep 1 и 2, чтобы не скрывать ошибку чтения. Запуск bash summary.sh "run log.txt" не требует executable-бита.
На macOS Стандартные потоки и конвейеры переносимы. Пользовательский shell macOS часто zsh; скрипт курса запускайте явно через bash. Системный Bash на macOS может быть старее Linux-версии; не переносите новые возможности Bash без проверки bash --version.
bash summary.sh "run log.txt"
grep -F ERROR events.log | cut -d " " -f 2 | sort | uniq -cКоманды macOS необходимо проверить на конкретном устройстве. Стандартные потоки и конвейеры переносимы. Пользовательский shell macOS часто zsh; скрипт курса запускайте явно через bash. Системный Bash на macOS может быть старее Linux-версии; не переносите новые возможности Bash без проверки bash --version.
Скрипт сводки Создайте summary.sh с #!/usr/bin/env bash и set -o pipefail. Примите путь первым аргументом; при отсутствии файла напечатайте понятную ошибку в stderr и завершитесь с кодом 2. Выведите число строк и число ERROR; отсутствие ошибок должно давать 0, а не падение. Запустите на нормальном журнале, журнале без ERROR и несуществующем файле. Работа в парах: один оператор, второй проверяет гипотезу. Смена ролей в середине. Пример: file=${1:-}; if [[ ! -f "$file" ]]; then printf "File not found\n" >&2; exit 2; fi; lines=$(wc -l < "$file"); errors=$(grep -c -F ERROR "$file" || true); printf "lines=%s errors=%s\n" "$lines" "$errors". Здесь || true допустим после проверки читаемого учебного файла для ожидаемого кода 1; усиление решения — проверить отдельно коды grep 1 и 2, чтобы не скрывать ошибку чтения. Запуск bash summary.sh "run log.txt" не требует executable-бита.
Что должно получиться summary.sh и результаты трёх запусков.
Все три случая предсказуемы; пути с пробелами работают.
Пример: file=${1:-}; if [[ ! -f "$file" ]]; then printf "File not found\n" >&2; exit 2; fi; lines=$(wc -l < "$file"); errors=$(grep -c -F ERROR "$file" || true); printf "lines=%s errors=%s\n" "$lines" "$errors". Здесь || true допустим после проверки читаемого учебного файла для ожидаемого кода 1; усиление решения — проверить отдельно коды grep 1 и 2, чтобы не скрывать ошибку чтения. Запуск bash summary.sh "run log.txt" не требует executable-бита.
Ошибка для диагностики grep без совпадений ошибочно считается аварией скрипта.
Симптом → Гипотеза → Проверка
Пример: file=${1:-}; if [[ ! -f "$file" ]]; then printf "File not found\n" >&2; exit 2; fi; lines=$(wc -l < "$file"); errors=$(grep -c -F ERROR "$file" || true); printf "lines=%s errors=%s\n" "$lines" "$errors". Здесь || true допустим после проверки читаемого учебного файла для ожидаемого кода 1; усиление решения — проверить отдельно коды grep 1 и 2, чтобы не скрывать ошибку чтения. Запуск bash summary.sh "run log.txt" не требует executable-бита.
Основные выводы uniq сравнивает соседние строки. Перезаписывает существующее содержимое файла. uniq сравнивает соседние строки.
Перезаписывает существующее содержимое файла.
После пары Улучшить скрипт: различать отсутствие совпадений и ошибку чтения; объяснить проверку кода возврата.
Конспект, команды и разбор → Проверьте критерии сдачи и дайте возможность повторной попытки. Первоисточники: https://ubuntu.com/server/docs/ https://www.gnu.org/software/bash/manual/bash.html