Найти в Дзене
Замечательный предел

Как работает Linux - гадание на требушках

На вопрос: "Для чего нам изучать устройство системы?" мы отвечаем, что нам нужны знания, с помощью которых мы сможем добиваться от неё наилучшего результата. Если мы не понимаем, как она устроена, то мы можем лишь догадываться, как она работает, а это не является знанием. (C) Балабоба, нейросеть Яндекса. Главной частью операционной системы является ядро Linux. Система становится доступна для работы только после его загрузки. Этот процесс состоит из нескольких этапов: Название BIOS является аббревиатурой слов basic input-output system. Это программа, записанная в память материнской платы компьютера, предназначенная для правильной инициализации электронных компонентов и их взаимодействия. Одна из её важнейших функций - обеспечение начала загрузки операционной системы. Для этого она читает главную загрузочную запись диска (MBR) и запускает на выполнение инструкции, предваряющие запуск основного загрузчика системы. Кроме того, BIOS более новых плат может работать с другой архитектурой ра
На вопрос: "Для чего нам изучать устройство системы?" мы отвечаем, что нам нужны знания, с помощью которых мы сможем добиваться от неё наилучшего результата. Если мы не понимаем, как она устроена, то мы можем лишь догадываться, как она работает, а это не является знанием. (C) Балабоба, нейросеть Яндекса.
Kandinsky 2.0 и ERNIE-ViLG: андроид, размышляющий об устройстве электроовец
Kandinsky 2.0 и ERNIE-ViLG: андроид, размышляющий об устройстве электроовец

Главной частью операционной системы является ядро Linux. Система становится доступна для работы только после его загрузки. Этот процесс состоит из нескольких этапов:

  1. BIOS считывает и запускает загрузчик системы
  2. Загрузчик системы находит образ ядра на диске, загружает его в память и запускает
  3. Ядро выполняет инициализацию устройств и их драйверов
  4. Ядро запускает команду init, с которой начинается пространство пользователя
  5. Команда init приводит в действие остальные системные процессы
  6. На завершающем этапе загрузки системы команда init запускает процесс, позволяющий войти в систему.
В разрезе (слева на право) - тигр, загрузка Linux. Источники: Ленфильм, "Полосатый рейс" и RAHUL BAJAJ, "Linux Boot Process" соответственно.
В разрезе (слева на право) - тигр, загрузка Linux. Источники: Ленфильм, "Полосатый рейс" и RAHUL BAJAJ, "Linux Boot Process" соответственно.

Название BIOS является аббревиатурой слов basic input-output system. Это программа, записанная в память материнской платы компьютера, предназначенная для правильной инициализации электронных компонентов и их взаимодействия. Одна из её важнейших функций - обеспечение начала загрузки операционной системы. Для этого она читает главную загрузочную запись диска (MBR) и запускает на выполнение инструкции, предваряющие запуск основного загрузчика системы. Кроме того, BIOS более новых плат может работать с другой архитектурой разделов - GPT (GUID Partition Table) и выполнять функции запуска основного загрузчика самостоятельно.

Традиционным загрузчиком Linux является LILO, но из за большей гибкости в новых версиях по умолчанию используется GRUB (Grand Unified Bootloader). Его параметры работы можно изменить отредактировав файл /etc/default/grub.

Пример содержимого конфигурационного файла GRUB
Пример содержимого конфигурационного файла GRUB

Например, для вывода на экран меню загрузки нужно закомментировать строчку GRUB_TIMEOUT_STYLE=hidden, убрав символ # в её начале. И установить время показа меню в строке GRUB_TIMEOUT=10.

Автоматически сгенерированный файл меню загрузки grub.cfg
Автоматически сгенерированный файл меню загрузки grub.cfg

Узнать доступные варианты загрузки можно прочитав файл /boot/grub/grub.cfg. А в каталоге /etc/grub.d содержатся скрипты, предназначенные для создания этого файла. При вводе команды upgrade-grub они используются для нахождения всех имеющихся на компьютере ядер, чтобы вписать их в меню загрузки.

Образ ядра хранится в каталоге /boot. Обычно там имеется несколько версий ядра, на любую из которых можно перейти в любой момент. Узнать текущую версию ядра можно с помощью команды uname -r. Общее слово в названии версий ядра- vmlinuz.

Образы ядра Linux из каталога /boot
Образы ядра Linux из каталога /boot

Узнать размер ядра можно выполнив команду du -h /boot/vmlinuz*
Размер в пределах 11 МБайт по современным меркам не впечатляет, но в этом файле сосредоточены лишь основные умения: для работы с памятью, управления процессами и т.д.

Типичные размеры файлов образов ядер Linux
Типичные размеры файлов образов ядер Linux

Более специфические функции, обеспечивающие взаимодействие с сетевым оборудованием, видеокартами и другими устройствами вынесены в отдельные файлы, называемые модулями. Они хранятся в каталоге /lib/modules в виде файлов с расширением ".ko" и подгружаются ядром по мере необходимости. Для каждой версии ядра создан отдельный подкаталог. Из файла modules.builtin можно прочитать список таких модулей. А в файле modules.alias перечислено для каких устройств какие модули грузить в ядро.

Что можно узнать, зайдя в каталог /lib/modules
Что можно узнать, зайдя в каталог /lib/modules

Для получения списка модулей текущего ядра можно воспользоваться командой: find /lib/modules/`uname -r` -name '*.ko' | less
Все эти модули работают в едином адресном пространстве ядра, поэтому Linux считается монолитным ядром.

Получение списка модулей текущей версии ядра
Получение списка модулей текущей версии ядра

На этапе, когда загрузчик передал управление ядру, возникает проблема с доступом к модулям, которые размещаются на устройстве хранения, драйверы для работы файловой системой которого ещё не загружены, поскольку находятся там же. Для её решения ядро разворачивает в оперативной памяти мини-образ системы, содержащий минимально необходимые модули для монтирования корневой файловой системы. Этот мини-образ обычно находится там же, где и образ ядра системы и называется initrd или initramfs.

С просторов Интернета: участие initrd в процессе загрузки
С просторов Интернета: участие initrd в процессе загрузки

После того как корневая файловая система смонтирована, наступает этап запуска команды init и инициализации устройств. Эта работа идёт асинхронно, а упорядочивает её менеджер устройств udev. Ядро, по мере обнаружения устройств отправляет об этом сообщение (uevent) менеджеру udev, а тот в свою очередь выполняет инициализацию устройства и создаёт несколько символических ссылок в каталоге /dev. Что именно должен делать udev при поступлении uevent, описано в правилах, которые хранятся в каталоге /usr/lib/udev/rules.d. Для администрирования udev используется команда udevadm.

Слева на право: Kandinsky 2.0 - менеджер устройств udev,  список правил его  работы
Слева на право: Kandinsky 2.0 - менеджер устройств udev, список правил его работы

Команда init - это программа, отвечающая за инициализацию пространства пользователя. Она управляет процессом запуска и останова всех других программ после того, как будет запущена ядром. И является первым процессом в этом пространстве. Существуют различные реализации init: System V init (SysV), systemd, Upstart, OpenRC, Runinit и другие. В новых версиях Ubuntu по умолчанию используется systemd. Убедиться в этом можно с помощью команды ps -p 1.

Первый процесс в пространстве пользователя - systemd
Первый процесс в пространстве пользователя - systemd

Systemd ориентируется на достижении определённого целевого состояния системы (target), запуская все необходимые для этого процессы. Также она выполняет и другие функции:

  • управление службами;
  • монтирование файловых систем;
  • запуск по расписанию;
  • реагирование на подключение устройств;
  • отслеживание сетевых сокетов.

Настроить каждую из этих функций можно добавляя и редактируя конфигурационные файлы, являющиеся блоками (units) systemd. Блоки, идущие в составе системы и добавляемые при установке пакетов, находятся в каталоге /usr/lib/systemd/system, а для добавления пользовательских блоков существует каталог /etc/systemd/system. Блоки, предназначенные для выполнения определённого типа функции различаются расширением: .target, .service, .device, .timer, .socket, .mount, .slice и т.д.

Места, имеющие отношение к systemd
Места, имеющие отношение к systemd

Создавая блоки в любом текстовом редакторе с соблюдением синтаксиса, понятного systemd, можно добавлять новые службы и выполнять другие действия, доступные системе инициализации. Управление блоками осуществляется командой systemctl. Например, с помощью неё можно узнать общее состояние системы и список блоков, соответствующих запущенным процессам: systemctl status | less

Информация о состоянии служб systemd, полученная с помощью команды systemctl
Информация о состоянии служб systemd, полученная с помощью команды systemctl

В systemd поддерживается частичная совместимость c SysV. Если при обращении к службе systemd не находит соответствующий блок, он ищет сценарий SysV с таким же именем, но без расширения и руководствуется им при создании службы. Для этого системе созданы каталоги /etc/rc<N>.d, где N - число от 0 до 6 соответствующее уровню запуска SysV. В них хранятся символические ссылки, для которых systemd идентифицирует сценарии из каталога /etc/init.d и ассоциирует названия сценариев с соответствующими блоками.

Уровни запуска System V частично поддерживаются systemd
Уровни запуска System V частично поддерживаются systemd

Помимо самого systemd в каталоге /lib/systemd можно найти и ряд вспомогательных программ, некоторые из которых дублируют функции системных программ, потому что способны обрабатывать механизмы уведомления systemd - fsck, makefs, sysctl, networkd, udevd и другие. Например, systemd-networkd и systemd-resolvd - системные службы управления сетевыми настройками, реализованные как программы-демоны (daemon programs), т.е. systemd может запустить их в фоновом режиме, чтобы они выполняли свои функции без участия пользователя.

Вспомогательные программы systemd
Вспомогательные программы systemd

Посмотреть все запущенные программы (процессы) можно с помощью команды pstree. Запуск этой команды без параметров построит в окне терминала древовидную структуру, отображающую иерархическую зависимость процессов друг от друга. В этой структуре systemd отведено почётное место корневого элемента, расположенного в верхнем левом углу. Он является родительским процессом для всех остальных.

Отображение древовидной структуры запущенных процессов
Отображение древовидной структуры запущенных процессов

Ядро хранит информацию обо всех процессах, следит за памятью выделенной каждому процессу и за готовностью процессов возобновить выполнение. Каждому процессу присваивается идентификационный номер PID (Process ID) по мере их запуска в порядке возрастания. Увидеть их список можно с помощью команды ps -axu.

Просмотр списка процессов
Просмотр списка процессов

Поле TIME содержит объём процессорного времени, занимаемого процессом. STAT показывает информацию о текущем состоянии процесса:

  • R, выполняется;
  • S, приостановлен;
  • T, принудительно остановлен;
  • <, высокоприоритетный;
  • N, низкоприоритетный;
  • D, приостановлен без возможности прерывания;
  • Z, недействующий процесс - "зомби" (дочерний процесс, который завершился, но не был удалён родителем).

Поля CPU и MEM показывают в процентах использование ресурсов процессора и памяти, VSZ- объём виртуальной памяти, RSS - размер страниц памяти, START - время запуска процессора.

Данная команда получает информацию из каталога /proc и предоставляет её пользователю в более удобном виде. В этот каталог смонтирована виртуальная файловая система procfs, в которую ядро помещает информацию о процессах, отсортированную по каталогам с именами, соответствующими PID этих процессов. Многие и другие команды пользуются ресурсами этого каталога. Например, команда free -m, выводящая количество свободной памяти в мегабайтах берёт эти сведения из /proc/meminfo. Там же можно получить информацию о процессоре: cat /proc/cpuinfo и времени работы системы cat /proc/uptime.

Что можно узнать из каталога /proc
Что можно узнать из каталога /proc

С помощью таких команд как top или ps можно узнать PID процесса, а затем по этому номеру зайти в каталог в /proc и посмотреть как он пользуется ресурсами системы.

Допустим, окно терминала bash имеет PID 42959. Тогда информацию о том, куда направлены потоки ввода/вывода этого процесса, можно прочитать с помощью команды ls -l /proc/42959/fd

По номеру PID можно найти информацию о процессе в одноимённом подкаталоге /proс
По номеру PID можно найти информацию о процессе в одноимённом подкаталоге /proс

Судя по информации из этого каталога, все стандартные потоки направлены в файл терминального устройства (/dev/pts/5). Именно поэтому в окне терминала отображаются результаты выполнения команд и сообщения об ошибках.

Каталог /proc/42959 содержит файлы, в которых собрана вся информация о процессе. Из файла cmdline можно прочитать команду, которой был запущен процесс. Символическая ссылка cwd указывает на текущий рабочий каталог, а exe на запущеный исполняемый файл. Файл wchan содержит последний системный вызов процесса. Из файла status можно прочитать ID владельца процесса, а по нему узнать его имя.

Примеры получения информации о процессе по его PID из подкаталога /proc
Примеры получения информации о процессе по его PID из подкаталога /proc

Подобными также являются каталоги /dev и /sys, в которые смонтированы виртуальные файловые системы devfs и sysfs. Все они нужны для того, чтобы программы из пространства пользователя могли получать информацию от ядра, потому в Linux не предусмотрено их общение напрямую. На самом деле в их основе файловая система Ramfs, поэтому они находятся в оперативной памяти, но при загрузке системы происходит их монтирование в соответствующие каталоги на диске.

В каталоге /dev находится перечень файлов, которые отвечают за взаимодействие с реальными устройствами. Многие стандартные команды можно применять к содержимому этого каталога также как обычным файлам. Например, вывести список всех подключённых устройств хранения: ls /dev/disk или отправить данные в какое-либо устройство: echo "Hello, world!" > /dev/pts/5

Получение списка установленных в системе дисков из каталога /dev/disk
Получение списка установленных в системе дисков из каталога /dev/disk
Отправка данных из одного терминала в другой, с помощью pts
Отправка данных из одного терминала в другой, с помощью pts

Linux наполняет этот каталог динамически. Когда ядро видит, к примеру, что подключён, новый диск, оно посылает сигнал udev, а тот в свою очередь читает правило, в котором прописано, что надо создать файл, связанный с этим диском в каталоге /dev. Файлы устройств в этом каталоге - абстракции реальных устройств, через которые приложения могут взаимодействовать с ними с помощью системных вызовов ввода-вывода (read, write, ioctl). Также в этот каталог помещаются файлы псевдоустройств, которые не соответствуют реально существующим аппаратным средствам компьютера, но предоставляют весьма полезные функции, например /dev/random служит источником случайных чисел.

Основные типы устройств - символьные (character) и блочные (block). На тип устройства указывает первый символ, который выводится командой ls -l: c или b.

Тип устройства можно узнать, обратив внимание на первый символ в строках вывода ls -l
Тип устройства можно узнать, обратив внимание на первый символ в строках вывода ls -l

Разница в том, в какой форме происходит обмен информацией. В символьном устройстве минимальная доступная для обмена единица информации - байт, а в блочном - сектор, размер которого как правило кратен 256 байт.

Символьные устройства - это обычно терминалы и принтеры. Блочные устройства - это, например, диски. Чисто технически можно вытащить данные с такого устройства, обращаясь к нему напрямую, но чтобы иметь дело с каталогами и файлами, нужно пользоваться драйвером файловой системы. Существуют и другие типы устройств, такие как каналы (FIFO, named pipe)- объекты, используемые для организации конвейеров и сокеты (socket) - объекты, предназначенные для взаимодействия между процессами по сети.

Отсортированный по типам список устройств, для которых загружены модули ядра, находится в файле /proc/devices.

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

В этом каталоге все файлы устройств создаются во время загрузки, а также udevd следит за тем, чтобы добавлять необходимые файлы в процессе подключения и отключения таких устройств как USB Flash. Однако существует возможность добавить или переинициализировать устройство и вручную, воспользовавшись командой mknod. Ей нужно передать в качестве аргументов имя устройства, а также его старший (major) и младший (minor) номера.

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

Вывод списка блочных устройств с соответствующими старшими и младшими номерами
Вывод списка блочных устройств с соответствующими старшими и младшими номерами

Узнать старший и младший номера блочных устройств, установленных в системе, можно с помощью команды lsblk. А ознакомиться с описанием полного списка возможных номеров по адресу:
https://www.kernel.org/doc/Documentation/admin-guide/devices.txt

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

Если какой-либо зарегистрированный в системе объект создаёт каталог в sysfs, то место создания каталога зависит от родительского элемента этого объекта, который также является объектом. Таким образом получается наглядная иерархическая зависимость. Каталоги верхнего уровня представляют общих предков иерархии объектов или подсистем, к которым принадлежат объекты.

С просторов Интернета: взаимоотношения оборудования, драйверов и каталога /sys
С просторов Интернета: взаимоотношения оборудования, драйверов и каталога /sys

Верхний уровень /sys - это блоки (block), шины (bus), классы (class), устройства (devices и dev), прошивки (firmware), ядро (kernel), файловые системы (fs), питание (power), модули (module).

В каталоге /sys/modules многие драйверы автоматически размещают определённые данные, которые могут быть использованы программами из пространства пользователя для выдачи информации об устройствах. Например, список загруженных модулей можно узнать с помощью команды lsmod, а информацию о модуле получить с помощью команды modinfo. Всё это хранится в каталоге /sys/module и может быть просмотрено стандартными средствами, такими как ls, tree, cat и т.д.

Откуда берётся информация о модулях
Откуда берётся информация о модулях

В /sys/block находятся каталоги всех блочных устройств, зарегистрированных в системе. Каждый из них содержит подкаталоги для разделов на устройстве.

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

В /sys/firmware представлено специфичное для аппаратного обеспечения системы дерево низкоуровневых подсистем, таких как ACPI, DMI и EFI .

Каталог /sys/devices даёт реальное представление о топологии устройств в системе.

Где хранится информация об устройствах, сгруппированных в иерархическую структуру
Где хранится информация об устройствах, сгруппированных в иерархическую структуру

В /sys/dev отображаются зарегистрированные узлы устройств в необработанном (без иерархии) виде, причём каждый из них является символической ссылкой на реальное устройство в системе.

Список блочных и символьных устройств в необработанном виде
Список блочных и символьных устройств в необработанном виде

Каталог /sys/bus содержит зарегистрированные в системе шины.

В каталоге /sys/fs перечислены файловые системы, которые используются в системе.

Откуда можно узнать о зарегистрированных шинах и используемых файловых системах
Откуда можно узнать о зарегистрированных шинах и используемых файловых системах

Каталог /sys/kernel содержит параметры конфигурации ядра и информацию о состоянии системы. Каталог /sys/power предоставляет интерфейс управления питанием системы из пользовательского пространства.

Где можно получить параметры конфигурации ядра и интерфейс управления питанием
Где можно получить параметры конфигурации ядра и интерфейс управления питанием

Слово "объект" в связи с каталогом /sys появилось не просто так, а потому, что виртуальная файловая система sysfs опирается в своей работе на такую структуру как kobject, реализующую базовый объектно-ориентированный механизм управления устройств. Она встроена во все объекты-контейнеры, описывающие модель устройства, такие как шина, драйверы и т.д. Атрибуты kobject в sysfs можно задавать в форме стандартных файлов. Это может пригодиться в задачах, связанных с отладкой и написанием скриптов.

Ещё один интересный, с токи зрения исследователя, каталог - /var. В нём хранятся файлы системных переменных - кэши, буферы, журналы. Если что-то пошло не так при запуске какой-нибудь программы первым делом имеет смысл заглянуть в /var/log. Глобальный системный журнал находится в /var/log/journal. Сюда собирает данные служба сбора логов системы systemd - journald и хранятся они в бинарном виде. Поэтому для их просмотра необходимо использовать специальную команду: journalctl. Введя journalctl --list-boots | less можно увидеть все запуски системы. Под цифрой 0 - текущая загрузка, -1 - предыдущая загрузка и т.д.

Пронумерованные записи в журнале о запусках системы
Пронумерованные записи в журнале о запусках системы

Ссылаясь на эту цифру можно посмотреть логи конкретной загрузки, указав её в команде: journalctl -b 0 | less.

Вывод последнего лога загрузки системы
Вывод последнего лога загрузки системы

Аналогичным образом можно получить логи, касающиеся только определённой службы. Например, NetworkManager:
journalctl -u NetworkManager | less.

Вывод логов NetworkManager
Вывод логов NetworkManager

Для вывода логов ядра системы: journalctl -k. Если нужно следить за логами в реальном времени, то используется параметр -f. Параметры можно комбинировать для получения более конкретного результата, например, посмотреть как прошла прошлая загрузка NetworkManager: journalctl -b -1 -u NetworkManager | less

У systemd есть также программа, собирающая статистику её самой: systemd-analyze. Если запустить её без каких-либо параметров, то мы увидим общее время загрузки системы.

Общее время загрузки ситемы
Общее время загрузки ситемы
Время загрузки каждой из служб
Время загрузки каждой из служб

А если добавить параметр blame, то будет показано время загрузки каждой из служб. Можно сократить время загрузки, отключив некоторые службы.

Логи в системе генерируются в зависимости от типа события. А программы их обрабатывают в соответствии с определённым приоритетом важности:
0: emerg - неработоспособность системы (наивысший приоритет)
1: alert - тревога (требуется немедленное вмешательство)
2: crit - критическое состояние
3: err - ошибка
4: warning - предупреждение
5: notice - уведомление
6: info - информационное сообщение
7: debug - отладочная информация

Можно указывать journalctl сообщения какого уровня выводить с помощью параметра -p. Например, вывести сообщения об ошибках при текущей загрузке: journalctl -p err -b. Также можно указать номер приоритета: journalctl -p 2 -b. В обоих случаях указанный приоритет - это точка отсчёта для показа всех сообщений такого типа и с более высоким приоритетом.

Вывод информации из логов загрузки в соответствии с определённым приоритетом
Вывод информации из логов загрузки в соответствии с определённым приоритетом

Помимо подсистемы логирования systemd многие программы занимаются подобным самостоятельно и помещают свои логи в отдельные файлы и подкаталоги внутри /var/log. В том числе там можно найти глобальный системный журнал /var/log/syslog, в котором пишутся сообщения от ядра Linux, различных служб, устройств и т.д. Эти файлы текстовые и с ними можно работать стандартными средствами, например можно вывести логи с сообщениями об ошибках командой: grep 'warning' /var/log/syslog

Вывод информации из глобального системного журнала syslog
Вывод информации из глобального системного журнала syslog

Также могут представлять интерес файлы /var/log/auth.log, где хранится информация об авторизации пользователей и /var/log/dmesg, где находится информация от драйверов.

Вывод информации об авторизации пользователей
Вывод информации об авторизации пользователей
Вывод информации от драйверов
Вывод информации от драйверов

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

Kandinsky 2.0: Linux-детектив
Kandinsky 2.0: Linux-детектив
"Как видите, работая с Linux, иногда приходится проводить массу интересных детективных расследований"! (С) Уильям Шоттс "Командная строка Linux".