КулЛиб - Классная библиотека! Скачать книги бесплатно
Всего книг - 714656 томов
Объем библиотеки - 1414 Гб.
Всего авторов - 275121
Пользователей - 125173

Новое на форуме

Новое в блогах

Впечатления

iv4f3dorov про Дорнбург: Змеелов в СССР (Альтернативная история)

Очередное антисоветское гавно размазанное тонким слоем по всем страницам. Афтырь ты мудак.

Рейтинг: 0 ( 1 за, 1 против).
A.Stern про Штерн: Анархопокалипсис (СИ) (Фэнтези: прочее)

Господи)))
Вы когда воруете чужие книги с АТ: https://author.today/work/234524, вы хотя бы жанр указывайте правильный и прологи не удаляйте.
(Заходите к автору оригинала в профиль, раз понравилось!)

Какое же это фентези, или это эпоха возрождения в постапокалиптическом мире? -)
(Спасибо неизвестному за пиар, советую ознакомиться с автором оригинала по ссылке)

Ещё раз спасибо за бесплатный пиар! Жаль вы не всё произведение публикуете х)

Рейтинг: 0 ( 0 за, 0 против).
чтун про серию Вселенная Вечности

Все четыре книги за пару дней "ушли". Но, строго любителям ЛитАниме (кароч, любителям фанфиков В0) ). Не подкачал, Антон Романович, с "чувством, толком, расстановкой" сделал. Осталось только проду ждать, да...

Рейтинг: +2 ( 2 за, 0 против).
Влад и мир про Лапышев: Наследник (Альтернативная история)

Стиль написания хороший, но бардак у автора в голове на нечитаемо, когда он начинает сочинять за политику. Трояк ставлю, но читать дальше не буду. С чего Ленину, социалистам, эссерам любить монархию и терпеть черносотенцев,убивавших их и устраивающие погромы? Не надо путать с ворьём сейчас с декорациями государства и парламента, где мошенники на доверии изображают партии. Для ликбеза: Партии были придуманы ещё в древнем Риме для

  подробнее ...

Рейтинг: 0 ( 0 за, 0 против).
Влад и мир про Романов: Игра по своим правилам (Альтернативная история)

Оценку не ставлю. Обе книги я не смог читать более 20 минут каждую. Автор балдеет от официальной манерной речи царской дворни и видимо в этом смысл данных трудов. Да и там ГГ перерождается сам в себя для спасения своего поражения в Русско-Японскую. Согласитесь такой выбор ГГ для приключенческой фантастики уже скучноватый. Где я и где душонка царского дворового. Мне проще хлев у своей скотины вычистить, чем служить доверенным лицом царя

  подробнее ...

Рейтинг: +1 ( 1 за, 0 против).

Внутреннее устройство Linux [Брайан Уорд] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]

ВНУТРЕННЕЕ
УСТРОЙСТВО
LINUX
3-Е И З Д А Н И Е

БРАЙАН

2022

УОРД

ББК 32.973.2-018.2
УДК 004.451
У64

Уорд Б.
У64 Внутреннее устройство Linux. 3-е изд. — СПб.: Питер, 2022. — 480 с.: ил. — (Серия «Для профессионалов»).
ISBN 978-5-4461-3946-0

16+

Познакомьтесь со всеми тонкостями работы операционной системы Linux — от системного администрирования до глубинных механизмов, обеспечивающих низкоуровневый функционал Linux. Эта
книга, сразу после выхода ставшая бестселлером Amazon, даст вам базовые знания о работе с ядром
Linux и принципах правильной эксплуатации компьютерных сетей, о программировании сценариев
оболочки и обращении с языком С. Вы изучите вопросы защиты информации, виртуализацию и многое
другое. Книга необходима системным администраторам, программистам, специалистам по защите
информации, а также всем, кто изучает или хочет изучить Linux максимально быстро и эффективно.
(В соответствии с Федеральным законом от 29 декабря 2010 г. № 436-ФЗ.)

ББК 32.973.2-018.2
УДК 004.451

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

ISBN 978-1718500402 англ.

ISBN 978-5-4461-3946-0

© 2021 by Brian Ward. How Linux Works, 3rd Edition:
What Every Superuser Should Know, ISBN 9781718500402,
published by No Starch Press Inc. 245 8th Street,
San Francisco, California United States 94103.
© Перевод на русский язык ООО «Прогресс книга», 2022
© Издание на русском языке, оформление
ООО «Прогресс книга», 2022
© Серия «Для профессионалов», 2022

Краткое содержание

Благодарности.....................................................23
Предисловие.......................................................24
Глава 1. Общая картина...........................................28
Глава 2. Основные команды и иерархия каталогов......................39
Глава 3. Устройства...............................................79
Глава 4. Диски и файловые системы.................................102
Глава 5. Загрузка ядра Linux.......................................156
Глава 6. Запуск пользовательского пространства......................176
Глава 7. Настройка системы: журналирование, системное время,
пакетные задачи и пользователи............................208
Глава 8. Процессы и использование ресурсов.........................244
Глава 9. Сеть и ее конфигурация...................................270
Глава 10. Сетевые приложения и службы..............................320
Глава 11. Сценарии оболочки.......................................344
Глава 12. Передача файлов по сети и доступ к ним......................368
Глава 13. Пользовательское окружение...............................390
Глава 14. Краткий обзор рабочего стола Linux. Вывод на печать...........402
Глава 15. Инструменты разработки..................................419
Глава 16. Компиляция программ из исходного кода на языке С............442
Глава 17. Виртуализация...........................................458
Библиография.....................................................478

Оглавление

Об авторе......................................................22
О научных редакторах............................................22
Благодарности...................................................23
Предисловие.....................................................24
Для кого эта книга................................................24
Требуемый уровень знаний.........................................24
Как читать книгу..................................................25
Практический подход.............................................25
Как устроена эта книга............................................25
Новое в третьем издании..........................................26
Немного о терминологии..........................................27
От издательства..................................................27
Глава 1. Общая картина............................................28
1.1. Уровни абстракции в системе Linux...............................29
1.2. Оборудование: оперативная память..............................31
1.3. Ядро.......................................................32
1.3.1. Управление процессами.................................32
1.3.2. Управление памятью....................................34
1.3.3. Управления драйверами устройств........................34
1.3.4. Системные вызовы и поддержка..........................35
1.4. Пользовательское пространство.................................36
1.5. Пользователи................................................37
1.6. Что дальше?.................................................38
Глава 2. Основные команды и иерархия каталогов.......................39
2.1. Оболочка Bourne Shell (bash): /bin/sh............................40
2.2. Использование оболочки......................................40

Оглавление   7

2.2.1. Окно оболочки........................................40
2.2.2. Программа cat........................................41
2.2.3. Стандартный поток ввода (stdin) и стандартный
поток вывода (stdout).........................................42
2.3. Основные команды...........................................43
2.3.1. Команда ls............................................43
2.3.2. Команда cp...........................................44
2.3.3. Команда mv...........................................44
2.3.4. Команда touch.........................................44
2.3.5. Команда rm...........................................44
2.3.6. Команда echo.........................................45
2.4. Перемещение по каталогам....................................45
2.4.1. Команда cd...........................................45
2.4.2. Команда mkdir.........................................46
2.4.3. Команда rmdir.........................................46
2.4.4. Шаблоны поиска (переменные Wildcards)..................46
2.5. Команды среднего уровня......................................47
2.5.1. Команда grep.........................................48
2.5.2. Команда less..........................................48
2.5.3. Команда pwd.........................................49
2.5.4. Команда diff..........................................49
2.5.5. Команда file...........................................50
2.5.6. Команды find и locate...................................50
2.5.7. Команды head и tail.....................................50
2.5.8. Команда sort..........................................51
2.6. Смена пароля и оболочки......................................51
2.7. Файлы с точками.............................................51
2.8. Переменные окружения и оболочки..............................52
2.9. Переменная пути PATH........................................52
2.10. Специальные символы........................................53
2.11. Редактирование в командной строке.............................54
2.12. Текстовые редакторы.........................................55
2.13. Онлайн-поддержка..........................................56
2.14. Ввод и вывод командной оболочки..............................58
2.14.1. Стандартная ошибка...................................59
2.14.2. Стандартное перенаправление ввода.....................59

8   Оглавление
2.15. Сообщения об ошибках.......................................60
2.15.1. Структура сообщений об ошибках в Unix..................60
2.15.2. Распространенные ошибки.............................61
2.16. Перечисление процессов и управление ими......................62
2.16.1. Параметры команды ps................................63
2.16.2. Завершение процесса.................................64
2.16.3. Управление заданиями.................................65
2.16.4. Фоновые процессы....................................65
2.17. Режимы файлов и права доступа................................66
2.17.1. Изменение прав доступа...............................68
2.17.2. Использование символических ссылок....................69
2.18. Архивирование и сжатие файлов...............................71
2.18.1. Утилита gzip..........................................71
2.18.2. Утилита tar...........................................71
2.18.3. Сжатые архивы (.tar.gz)................................72
2.18.4. Утилита zcat..........................................73
2.18.5. Другие утилиты сжатия.................................73
2.19. Основная иерархия каталогов Linux.............................74
2.19.1. Другие корневые подкаталоги...........................76
2.19.2. Каталог /usr.........................................76
2.19.3. Местонахождение ядра................................76
2.20. Запуск команд от имени суперпользователя......................77
2.20.1. Команда sudo........................................77
2.20.2. Файл /etc/sudoers....................................77
2.20.3. Журналы sudo........................................78
2.21. Что дальше?................................................78
Глава 3. Устройства................................................79
3.1. Файлы устройств.............................................79
3.2. Путь к устройству sysfs.........................................81
3.3. Утилита dd и устройства.......................................82
3.4. Имена устройств.............................................83
3.4.1. Жесткие диски: /dev/sd*................................84
3.4.2. Виртуальные диски: /dev/xvd*, /dev/vd*..................85
3.4.3. Устройства долговременной памяти: /dev/nvme*............85

Оглавление   9

3.4.4. Подсистема создания виртуальных блочных устройств:
/dev/dm-*, /dev/mapper/*..................................85
3.4.5. CD- и DVD-приводы: /dev/sr*............................85
3.4.6. Жесткие диски PATA: /dev/hd*...........................86
3.4.7. Терминалы: /dev/tty*, /dev/pts/* и /dev/tty...............86
3.4.8. Последовательные порты: /dev/ttyS*, /dev/ttyUSB*,
/dev/ttyACM*.............................................87
3.4.9. Параллельные порты: /dev/lp0 и /dev/lp1.................88
3.4.10. Аудиоустройства: /dev/snd/*, /dev/dsp, /dev/audio
и другие...................................................88
3.4.11. Создание файла устройства.............................88
3.5. Менеджер устройств udev......................................89
3.5.1. Файловая система devtmpfs..............................90
3.5.2. Работа и настройка менеджера udevd.....................91
3.5.3. Команда udevadm......................................93
3.5.4. Отслеживание устройств................................94
3.6. Подробнее об интерфейсе SCSI и ядре Linux.......................95
3.6.1. USB-накопитель и SCSI..................................98
3.6.2. Интерфейсы SCSI и ATA.................................99
3.6.3. Обобщенные устройства SCSI............................99
3.6.4. Методы множественного доступа к одному устройству.......100
Глава 4. Диски и файловые системы..................................102
4.1. Разбиение дисков на разделы..................................104
4.1.1. Просмотр таблицы разделов............................105
4.1.2. Редактирование таблиц разделов........................108
4.1.3. Создание таблицы разделов............................109
4.1.4. Геометрия дисков и разделов............................111
4.1.5. Чтение твердотельных дисков............................113
4.2. Файловые системы...........................................114
4.2.1. Типы файловых систем.................................114
4.2.2. Создание файловой системы............................116
4.2.3. Монтирование файловой системы.......................117
4.2.4. Идентификатор UUID файловой системы..................119
4.2.5. Буферизация диска, кэширование и файловые системы......120
4.2.6. Параметры монтирования файловой системы..............120

10   Оглавление
4.2.7. Повторное монтирование файловой системы...............122
4.2.8. Таблица файловой системы /etc/fstab....................122
4.2.9. Альтернативы файлу /etc/fstab..........................124
4.2.10. Емкость файловой системы............................124
4.2.11. Проверка и восстановление файловых систем.............126
4.2.12. Файловые системы специального назначения..............129
4.3. Область подкачки swap.......................................130
4.3.1. Использование раздела диска в качестве области подкачки...130
4.3.2. Использование файла в качестве области подкачки .........131
4.3.3. Определение необходимого размера области подкачки .....131
4.4. Менеджер логических томов LVM...............................132
4.4.1. Работа с менеджером LVM..............................134
4.4.2. Реализация менеджера LVM.............................145
4.5. Что дальше? Диски и пользовательское пространство...............149
4.6. Что находится внутри традиционной файловой системы.............150
4.6.1. Сведения о дескрипторе и количество ссылок..............152
4.6.2. Распределение блоков.................................154
4.6.3. Работа с файловыми системами в пользовательском
пространстве..............................................154
Глава 5. Загрузка ядра Linux........................................156
5.1. Сообщения при загрузке......................................157
5.2. Параметры инициализации и загрузки ядра......................158
5.3. Параметры ядра............................................159
5.4. Загрузчики.................................................160
5.4.1. Задачи загрузчика....................................161
5.4.2. Обзор загрузчиков....................................162
5.5. Введение в загрузчик GRUB...................................163
5.5.1. Изучение устройств и разделов с помощью командной
строки GRUB..............................................165
5.5.2. Конфигурация GRUB..................................167
5.5.3. Установка GRUB......................................170
5.6. Проблемы безопасной загрузки UEFI............................171
5.7. Метод цепной загрузки других операционных систем...............172
5.8. Детали работы загрузчика....................................173
5.8.1. Загрузчик MBR.......................................173

Оглавление   11

5.8.2. Загрузчик UEFI.......................................173
5.8.3. Как работает GRUB...................................174
Глава 6. Запуск пользовательского пространства.......................176
6.1. Основные сведения об init.....................................177
6.2. Определение системы инициализации...........................178
6.3. systemd....................................................178
6.3.1. Юниты и типы юнитов..................................179
6.3.2. Графики загрузки и зависимостей юнитов.................179
6.3.3. Конфигурация systemd.................................180
6.3.4. Процесс работы systemd...............................183
6.3.6. Зависимости systemd..................................188
6.3.7. Запуск по запросу и параллелизация ресурсов в systemd.....191
6.3.8. Вспомогательные компоненты systemd.....................196
6.4. Уровни выполнения в System V..................................197
6.5. System V init.................................................198
6.5.1. System V init: последовательность команд при запуске........199
6.5.2. Ферма ссылок System V init.............................200
6.5.3. Команда run-parts.....................................202
6.5.4. Управление System V init................................202
6.5.5. Совместимость systemd и System V........................203
6.6. Завершение работы системы..................................203
6.7. Начальная файловая система оперативной памяти.................205
6.8. Аварийная загрузка системы и однопользовательский режим........206
6.9. Что дальше?................................................207
Глава 7. Настройка системы: журналирование, системное время,
пакетные задачи и пользователи......................................208
7.1. Ведение системного журнала...................................209
7.1.1. Проверка настроек журнала............................209
7.1.2. Поиск и мониторинг журналов...........................210
7.1.3. Ротация файлов журнала...............................214
7.1.4. Обслуживание журналов journald........................215
7.1.5. Детали системного журналирования ......................215
7.2. Структура каталога /etc......................................218
7.3. Файлы управления пользователями.............................218
7.3.1. Файл /etc/passwd....................................219

12   Оглавление
7.3.2. Особые пользователи..................................220
7.3.3. Файл /etc/shadow....................................221
7.3.4. Управление пользователями и паролями...................221
7.3.5. Работа с группами пользователей........................222
7.4. Команды getty и login.........................................223
7.5. Установка времени...........................................224
7.5.1. Представление времени ядра и часовые пояса..............224
7.5.2. Сетевое время........................................225
7.6. Планирование повторяющихся задач с помощью команды cron
и юнитов таймера...............................................226
7.6.1. Установка файлов Crontab..............................227
7.6.2. Системные файлы Crontab..............................227
7.6.3. Юниты таймера.......................................228
7.6.4. Утилита cron против юнитов таймера......................230
7.7. Планирование разовых задач с помощью службы at................230
7.7.1. Аналоги таймера......................................231
7.8. Юниты таймера обычных пользователей.........................231
7.9. Доступ пользователя.........................................232
7.9.1. ID пользователей и переключение пользователей...........232
7.9.2. Владельцы процессов, действующий UID, реальный UID
и сохраненный UID.........................................233
7.9.3. Идентификация пользователя, аутентификация
и авторизация.............................................235
7.9.4. Применение библиотек для получения информации
о пользователе............................................236
7.10. Подключаемые модули аутентификации (PAM)...................237
7.10.1. Конфигурация PAM...................................237
7.10.2. Советы по синтаксису конфигурации PAM................241
7.10.3. Модули PAM и пароли................................242
7.11. Что дальше?................................................243
Глава 8. Процессы и использование ресурсов..........................244
8.1. Отслеживание процессов.....................................244
8.2. Поиск открытых файлов с помощью команды lsof..................245
8.2.1. Считывание вывода команды lsof.........................245
8.2.2. Использование команды lsof............................247

Оглавление   13

8.3. Отслеживание выполнения программ и системных вызовов..........247
8.3.1. Команда strace.......................................247
8.3.2. Команда ltrace........................................249
8.4. Потоки....................................................250
8.4.1. Однопоточные и многопоточные процессы.................250
8.4.2. Просмотр потоков....................................250
8.5. Введение в мониторинг ресурсов...............................252
8.5.1. Измерение процессорного времени......................252
8.5.2. Настройка приоритетов процесса........................253
8.5.3. Измерение производительности процессора с помощью
среднего значения загрузки .................................254
8.5.4. Мониторинг состояния памяти...........................255
8.5.5. Мониторинг производительности процессора и памяти
с помощью команды vmstat...................................258
8.5.6. Мониторинг ввода-вывода I/O..........................260
8.5.7. Мониторинг каждого процесса с помощью команды pidstat....262
8.6. Группы управления (cgroups)...................................263
8.6.1. Различие между версиями cgroup........................264
8.6.2. Просмотр cgroups.....................................266
8.6.3. Управление и создание cgroups..........................267
8.6.4. Отображение использования ресурсов...................268
8.7. Дополнительно..............................................269
Глава 9. Сеть и ее конфигурация.....................................270
9.1. Основы сети................................................271
9.2. Пакеты....................................................271
9.3. Сетевые уровни.............................................272
9.4. Сетевой уровень............................................273
9.4.1. Просмотр IP-адреса...................................275
9.4.2. Подсети.............................................275
9.4.3. Распространенные маски подсети и нотация CIDR...........276
9.5. Маршруты и таблица маршрутизации ядра.......................277
9.6. Шлюз по умолчанию.........................................278
9.7. IPv6-адреса и сети...........................................279
9.7.1. Просмотр конфигурации IPv6 в системе...................280
9.7.2. Настройка сетей с двумя стеками........................281

14   Оглавление
9.8. Основные инструменты ICMP и DNS............................281
9.8.1. Команда ping........................................282
9.8.2. Служба DNS и команда host............................283
9.9. Физический уровень и сеть Ethernet.............................283
9.10. Сетевые интерфейсы ядра....................................284
9.11. Введение в настройки сетевого интерфейса......................285
9.11.1. Ручная настройка интерфейсов.........................285
9.11.2. Добавление и удаление маршрутов вручную..............286
9.12. Конфигурация сети, активируемая при загрузке..................287
9.13. Проблемы с настройкой сети вручную и при загрузке..............288
9.14. Менеджеры настройки сети...................................289
9.14.1. Работа NetworkManager..............................289
9.14.2. Взаимодействие с NetworkManager.....................290
9.14.3. Настройка NetworkManager...........................290
9.15. Разрешения сетевых имен....................................292
9.15.1. Файл /etc/hosts.....................................293
9.15.2. Файл resolv.conf.....................................293
9.15.3. Кэширование и DNS с нулевой конфигурацией............294
9.15.4. Файл /etc/nsswitch.conf...............................295
9.16. Localhost..................................................296
9.17. Транспортный уровень: TCP, UDP и службы.......................296
9.17.1. TCP-порты и соединения...............................297
9.17.2. Протокол UDP.......................................301
9.18. Возврат к простой локальной сети.............................301
9.19. Протокол DHCP............................................302
9.19.1. DHCP-клиенты Linux..................................302
9.19.2. DHCP-серверы Linux..................................303
9.20. Автоматическая настройка сети IPv6...........................303
9.21. Настройка Linux в качестве маршрутизатора.....................304
9.22. Частные сети (IPv4).........................................306
9.23. Преобразование сетевых адресов NAT (маскарадинг IP-адресов)...306
9.24. Linux и маршрутизаторы.....................................308
9.25. Брандмауэры..............................................309
9.25.1. Основы брандмауэров Linux...........................310
9.25.2. Настройка правил брандмауэра........................311
9.25.3. Стратегии брандмауэра...............................313

Оглавление   15

9.26. Ethernet, IP, ARP и NDP.......................................315
9.27. Беспроводная сеть Ethernet...................................317
9.27.1. Утилита iw..........................................318
9.27.2. Безопасность беспроводной сети.......................318
9.28. Выводы...................................................319
Глава 10. Сетевые приложения и службы..............................320
10.1. Основы работы служб.......................................320
10.2. Взглянем глубже............................................322
10.3. Сетевые серверы...........................................323
10.3.1. Служба защищенной оболочки SSH.....................324
10.3.2. Сервер sshd........................................326
10.3.3. Утилита fail2ban.....................................328
10.3.4. SSH-клиент.........................................329
10.4. Сетевые серверы до systemd: inetd/xinetd.......................330
10.5. Средства диагностики.......................................331
10.5.1. Утилита lsof.........................................332
10.5.2. Утилита tcpdump.....................................333
10.5.3. Утилита netcat.......................................335
10.5.4. Сканирование портов................................336
10.6. Удаленный вызов процедур...................................337
10.7. Безопасность сети..........................................338
10.7.1. Типичные уязвимости..................................339
10.7.2. Ресурсы безопасности................................340
10.8. Что дальше?...............................................341
10.9. Сетевые сокеты............................................341
10.10. Доменные сокеты Unix......................................342
Глава 11. Сценарии оболочки.......................................344
11.1. Основы сценариев оболочки..................................344
11.1.1. Ограничения скриптов оболочки........................345
11.2. Кавычки и литералы.........................................346
11.2.1. Литералы...........................................346
11.2.2. Одинарные кавычки..................................347
11.2.3. Двойные кавычки.....................................348
11.2.4. Литерал с одинарными кавычками.......................348

16   Оглавление
11.3. Специальные переменные....................................349
11.3.1. Индивидуальные аргументы: $1, $2 и другие..............349
11.3.2. Количество аргументов: $#.............................350
11.3.3. Все аргументы: $@...................................350
11.3.4. Имя сценария: $0....................................350
11.3.5. ID процесса: $$......................................351
11.3.6. Код возврата: $?.....................................351
11.4. Коды возврата..............................................351
11.5. Условные операторы.........................................352
11.5.1. Обходной путь для предотвращения ошибки
Empty Parameter Lists........................................353
11.5.2. Другие команды для проверки условий...................353
11.5.3. Ключевое слово elif...................................353
11.5.4. Логические конструкции...............................354
11.5.5. Проверка условий....................................355
11.5.6. Ключевое слово case..................................357
11.6. Циклы....................................................358
11.6.1. Циклы for...........................................358
11.6.2. Циклы while.........................................359
11.7. Подстановка команд.........................................359
11.8. Управление временными файлами.............................360
11.9. Here-документы............................................362
11.10. Важные утилиты сценариев оболочки..........................362
11.10.1. Утилита basename...................................362
11.10.2. Утилита awk........................................363
11.10.3. Утилита sed........................................363
11.10.4. Утилита xargs.......................................364
11.10.5. Утилита expr........................................365
11.10.6. Утилита exec.......................................365
11.11. Подоболочки..............................................366
11.12. Добавление файлов в скрипты................................366
11.13. Чтение пользовательского ввода..............................367
11.14. Когда не стоит использовать сценарии оболочки.................367
Глава 12. Передача файлов по сети и доступ к ним......................368
12.1. Быстрое копирование данных.................................369

Оглавление    17

12.2. Утилита rsync...............................................370
12.2.1. Начало работы c rsync................................370
12.2.2. Создание точной копии структуры каталогов..............371
12.2.3. Добавление косой черты..............................372
12.2.4. Исключение файлов и каталогов........................373
12.2.5. Проверка копирования, меры предосторожности
и подробный режим........................................374
12.2.6. Сжатие данных......................................375
12.2.7. Ограничение ширины полосы пропускания................375
12.2.8. Передача файлов на ваш компьютер....................376
12.2.9. Больше информации о rsync............................376
12.3. Совместное использование файлов............................376
12.3.1. Производительность совместного использования файлов....377
12.3.2. Безопасность совместного доступа к файлам..............378
12.4. Совместное использование файлов с помощью Samba.............378
12.4.1. Настройка сервера...................................379
12.4.2. Контроль доступа к серверу............................379
12.4.3. Пароли............................................380
12.4.4. Запуск сервера вручную..............................382
12.4.5. Диагностика и файлы журналов.........................382
12.4.6. Настройка совместного использования файлов............382
12.4.7. Домашние каталоги..................................383
12.4.8. Совместное применение принтеров.....................383
12.4.9. Клиенты сервера Samba...............................384
12.5. Клиентская программа SSHFS.................................386
12.6. NFS......................................................387
12.7. Облачное хранилище........................................388
12.8. Состояние совместного доступа к файлам в сети..................388
Глава 13. Пользовательское окружение...............................390
13.1. Создание файлов запуска....................................391
13.2. Изменение файлов запуска...................................391
13.3. Элементы файла запуска оболочки............................391
13.3.1. Путь к команде......................................392
13.3.2. Путь к странице руководства...........................393
13.3.3. Приглашения prompt..................................393

18   Оглавление
13.3.4. Псевдонимы ........................................394
13.3.5. Маска прав доступа .................................394
13.4. Порядок и примеры файлов запуска............................395
13.4.1. Оболочка bash......................................395
13.4.2. Оболочка tcsh.......................................398
13.5. Пользовательские настройки по умолчанию.....................399
13.5.1. Параметры оболочки по умолчанию.....................399
13.5.2. Текстовый редактор...................................400
13.5.3. Пейджер...........................................400
13.6. Подводные камни в файлах запуска............................400
13.7. Далее о файлах запуска.....................................401
Глава 14. Краткий обзор рабочего стола Linux. Вывод на печать...........402
14.1. Компоненты рабочего стола..................................403
14.1.1. Фреймбуфер........................................403
14.1.2. Система X Window...................................403
14.1.3. Протокол Wayland...................................404
14.1.4. Менеджеры окон.....................................404
14.1.5. Наборы инструментов................................405
14.1.6. Окружение рабочего стола............................405
14.1.7. Приложения.........................................405
14.2. Wayland или X?............................................406
14.3. Обзор протокола Wayland...................................406
14.3.1. Композитный менеджер окон...........................406
14.3.2. Библиотека libinput...................................407
14.3.3. Совместимость X и Wayland............................408
14.4. Обзор системы X Window....................................409
14.4.1. Дисплейные менеджеры...............................410
14.4.2. Сетевая прозрачность................................411
14.4.3. Способы исследования X-клиентов......................411
14.4.4. События на сервере (X events)..........................412
14.4.5. X-ввод и настройки предпочтений.......................413
14.5. Шина D-Bus...............................................415
14.5.1. Системные и сеансовые экземпляры.....................416
14.5.2. Мониторинг сообщений D-Bus..........................416

Оглавление    19

14.6. Печать....................................................417
14.6.1. Сервер печати CUPS..................................417
14.6.2. Фильтры преобразования форматов и печати.............418
14.7. Дополнительно об окружениях рабочего стола...................418
Глава 15. Инструменты разработки..................................419
15.1. Компилятор C..............................................419
15.1.1. Компиляция нескольких исходных файлов.................420
15.1.2. Связывание с библиотеками............................422
15.1.3. Разделяемые библиотеки..............................423
15.1.4. Файлы заголовков (Include) и каталоги...................428
15.2. Утилита make..............................................430
15.2.1. Makefile............................................431
15.2.2. Встроенные правила..................................432
15.2.3. Финальная сборка программы.........................432
15.2.4. Обновление зависимостей.............................433
15.2.5. Аргументы и параметры командной строки...............433
15.2.6. Стандартные макросы и переменные....................434
15.2.7. Стандартные цели....................................435
15.2.8. Организация файла Makefile...........................436
15.3. Программы Lex и Yacc.......................................437
15.4. Языки сценариев...........................................437
15.4.1. Язык Python.........................................438
15.4.2. Язык Perl...........................................439
15.4.3. Другие языки сценариев...............................439
15.5. Язык Java..................................................440
15.6. А что дальше? Компиляция пакетов.............................441
Глава 16. Компиляция программ из исходного кода на языке С............442
16.1. Системы сборки программ...................................443
16.2. Распаковка пакетов с исходным кодом С........................443
16.3. Утилита GNU Autoconf.......................................445
16.3.1. Пример использования Autoconf........................446
16.3.2. Установка с помощью инструментов создания пакетов......447
16.3.3. Параметры сценария configure.........................447
16.3.4. Переменные окружения...............................448

20   Оглавление
16.3.5. Цели Autoconf.......................................449
16.3.6. Файлы журналов Autoconf.............................450
16.3.7. Утилита pkg-config....................................450
16.4. Процесс установки..........................................452
16.4.1. Места установки.....................................453
16.5. Применение исправлений....................................453
16.6. Устранение ошибок компиляции и установки.....................454
16.6.1. Особые ошибки.....................................455
16.7. Что дальше?...............................................457
Глава 17. Виртуализация...........................................458
17.1. Виртуальные машины........................................458
17.1.1. Гипервизоры.........................................459
17.1.2. Оборудование виртуальной машины.....................460
17.1.3. Использование виртуальных машин......................461
17.1.4. Недостатки виртуальных машин.........................462
17.2. Контейнеры................................................463
17.2.1. Docker, Podman и привилегии...........................464
17.2.2. Пример использования инструмента Docker...............465
17.2.3. Система LXC........................................473
17.2.4. Платформа Kubernetes................................473
17.2.5. Ошибки работы контейнеров...........................474
17.3. Виртуализация времени исполнения............................476
Библиография..................................................478

Если вы интересуетесь системой Linux, книга «Внутреннее устройство Linux» подойдет вам лучше всего.
LinuxInsider

В книге множество информации практически по всем аспектам архитектуры Linux.
Everyday Linux User

Вы получите полноценное представление о том, что происходит внутри системы,
не увязая во множестве деталей. Это отличное дополнение к литературе по Linux.
Фил Булл, соавтор книги Ubuntu Made Easy и член команды
по составлению документации для Ubuntu

Автор погружается в глубину операционных систем на базе Linux и рассказывает,
как все части системы сочетаются друг с другом.
DistroWatch

Книга заслуживает места на полке суперпользователя Linux.
Журнал The MagPi

Об авторе
Брайан Уорд работает с операционной системой Linux с 1993 года. Кроме этой, он
написал книги The Linux Kernel HOWTO, The Book of VMware и The Linux Problem
Solver.

О научных редакторах
Жорди Гутьеррес Эрмосо — профессиональный разработчик и пользователь
GNU/Linux с почти двадцатилетним опытом работы, внесший вклад в развитие
GNU Octave и Mercurial. В разное время он работал с криптографией, медицинской визуализацией и в сфере экологии — везде на Linux. Когда Жорди не сидит
за компьютером, то занимается плаванием, математикой и вязанием.
Петрос Кутупис в настоящее время старший инженер по производительности программного обеспечения в компании HPE (ранее Cray Inc.) в подразделении Lustre
High Performance File System. Он создатель RapidDisk Project (www.rapiddisk.org)
и занимается его сопровождением. Петрос более десяти лет работает в индустрии
хранения данных и помог внедрить многие современные технологии.

Благодарности

Вклад в эту книгу внесли не только те, кто участвовал в процессе ее создания,
но и те, без кого я бы ничего не узнал о Linux, а именно: Джеймс Дункан, Дуглас
Н. Арнольд, Билл Феннер, Кен Хорнстайн, Скотт Диксон, Дэн Эрлих, Феликс Ли
и Грегори П. Смит. За помощь в работе над предыдущими изданиями благодарю
Кароля Хурадо, Лорел Чун, Серену Янг, Элисон Лоу, Райли Хоффмана, Скотта
Шварца, Дэна Салли, Доминика Пулена, Дональда Кейрона и Джину Стил.
В подготовке третьего издания участвовали Барбара Куин, Рэйчел Монаган, Джилл
Франклин, Ларри Уэйда, Хорди Гутьерреса Эрмосо и Петрос Кутуписа. Билл Поллок, основатель издательства No Starch Press, сыграл особую роль в создании этой
книги с ее первого издания. И еще раз благодарю Синьцзю Сие за то, что вытерпел
меня и в этот раз.

Предисловие

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

Для кого эта книга
Интерес к устройству Linux затрагивает разные сферы жизни. Профессионалы
в сфере DevOps и разработчики должны знать почти всю информацию, которая
рассматривается в этой книге. Архитекторы и разработчики программного обеспечения Linux также должны знать это, чтобы пользоваться операционной системой
наилучшим образом. Для исследователей и студентов, часто работающих в своих
собственных системах Linux, будет полезно узнать, почему в системе все устроено
именно так, а не иначе, что и рассказывается в книге. Кроме того, есть еще и любители — люди, которые просто проводят время за своими компьютерами ради
развлечения, выгоды или и того и другого сразу.
Хотите знать, почему одни вещи работают, а другие нет? Вам интересно, что произойдет, если что-либо изменить? Если вы ответили «Да!», то вы, скорее всего,
любитель и найдете ответы на свои вопросы в этой книге.

Требуемый уровень знаний
Вам необязательно быть программистом, чтобы прочитать эту книгу. Понадобятся
только базовые навыки пользователя компьютера: вы должны ориентироваться
в графическом интерфейсе (особенно в интерфейсе установки и настроек для
дистрибутива Linux) и знать, что такое файлы и каталоги (папки). Кроме того,
будьте готовы искать и проверять дополнительную документацию в вашей системе
и в интернете. И самое главное — быть готовыми исследовать свой компьютер.

Как устроена эта книга   25

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

Практический подход
Для работы вам понадобится компьютер с операционной системой Linux. Возможно,
вы предпочтете виртуальную установку — для проверки большей части материала
данной книги я пользовался приложением VirtualBox. Вы должны обладать правами доступа superuser (root), хотя основную часть времени следует использовать
учетную запись обычного пользователя. Вы будете работать главным образом
в командной строке, окне терминала или в удаленном сеансе. Если вы нечасто
работали в этой среде, ничего страшного, в главе 2 вы узнаете об этом подробнее.
Как правило, команды будут выглядеть следующим образом:
$ ls /
[some output]

Вводите текст, который выделен жирным шрифтом; обычным шрифтом показан
ответный текст, который выдаст машина. Символ $ является приглашением для
пользователя с обычной учетной записью. Если вы увидите в качестве приглашения
символ #, следует работать в учетной записи superuser (подробнее об этом в главе 2).

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

26   Предисловие
И наконец, вы познакомитесь с частями работающей системы, освоите необходимые
навыки и рассмотрите инструменты, которые используют программисты.
В большинстве первых глав (кроме главы 2) активно задействовано ядро системы
Linux, но по мере продвижения по книге вы будете работать и в своем пространстве
пользователя. Если вы не понимаете, о чем я сейчас говорю, не беспокойтесь, объяснения будут даны в главе 1.
Материал излагается по возможности без привязки к какому-либо дистрибутиву
системы. Было бы скучно описывать все варианты системы, поэтому я попытался
рассказать о двух основных семействах дистрибутивов: Debian (включая Ubuntu)
и RHEL/Fedora/CentOS. Кроме того, я описал настольные и серверные установки.
Значительный объем материала можно использовать во встроенных системах, таких
как Android и OpenWRT, но поиск различий между ними предоставляется вам.

Новое в третьем издании
Второе издание вышло в переходный период для всех систем Linux. Часть традиционных компонентов постепенно изменялась, что затрудняло изучение, потому что
читатели сталкивались с большим разнообразием настроек. Однако сейчас новые
элементы (в частности, systemd) надежно заняли свои места в системах, поэтому
я смог значительно упростить и сократить материал по этой теме.
Я все так же сохранил акцент на роли ядра в системе Linux. Именно эта информация
была наиболее интересна читателям, и вы сами, скорее всего, взаимодействуете
с ядром чаще, чем думаете.
В новом издании появилась глава о виртуализации. Несмотря на то что Linux
часто устанавливают именно через виртуальные машины (например, через облачные серверы), подобный способ виртуализации выходит за рамки данной
книги. Все потому, что система работает на виртуальной машине практически
так же, как на «голом» железе. Таким образом, информация об этом отличается
в основном лишь терминологией. Кроме того, со времени выхода второго издания
контейнеризация стала более популярной, поэтому я добавил информацию и о
ней, ведь контейнеры в основном состоят из множества функций Linux, которые
описаны в книге. В контейнерах часто используются контрольные группы cgroups,
о которых я также рассказываю в третьем издании. В книгу в том числе добавлены сведения о менеджере логических томов, системе ведения журнала journald
и протоколе Ipv6 (в главе 9).
Мне хотелось снабдить вас сведениями, которые понадобятся для быстрого начала
работы. Их усвоение потребует некоторых усилий, однако я не намереваюсь делать
из вас «тяжелоатлетов», чтобы вы смогли одолеть эту книгу. Когда вы будете понимать важнейшие моменты, изложенные здесь, для вас не составит труда отыскать
подробности и разобраться в них.

От издательства   27
Я удалил кое-какие исторические детали, которые были в первом издании. Если вы
интересуетесь системой Linux и ее отношением к истории системы Unix, обратитесь
к книге Питера Салуса The Daemon, the Gnu, and the Penguin (Reed Media Services,
2008) — в ней рассказано о том, как развивалось используемое нами программное
обеспечение.

Немного о терминологии
До сих пор ведутся споры о наименованиях некоторых элементов операционных
систем. Они затрагивают даже само название системы Linux — как она должна называться: Linux или же GNU/Linux (чтобы отразить применение некоторых элементов проекта GNU)? В книге я старался использовать самые распространенные
и по возможности наименее громоздкие названия.

От издательства
Ваши замечания, предложения, вопросы отправляйте по адресу comp@piter.com
(издательство «Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
На веб-сайте издательства www.piter.com вы найдете подробную информацию о наших книгах.

1

Общая картина

На первый взгляд такая современная операционная система, как Linux, очень сложна и состоит из невероятного
количества инструментов, которые работают и взаимодействуют друг с другом одновременно. Например, веб-сервер
может взаимодействовать с сервером базы данных, который,
в свою очередь, может применять разделяемую библиотеку,
используемую многими другими программами. Как же все это
работает и какой во всем этом смысл?
Самый эффективный способ понять, как работает операционная система, — абстрагироваться от большинства деталей, составляющих часть, которую вы изу­
чаете, и сосредоточиться на ее основной цели и функциональности. Например,
когда вы едете в автомобиле, вам не нужно думать о таких деталях, как крепежные
болты, которые удерживают двигатель внутри него, или о людях, которые строят
и ремонтируют дорогу, по которой он едет. Все, что вам действительно нужно
знать, — это цель автомобиля (перевезти вас куда-то) и основные знания о том,
как им пользоваться (например, как открывать дверь и пристегивать ремень
безопас­ности).
Подобный уровень абстрагирования сработает, если вы просто пассажир в машине. Но если вы управляете автомобилем, вам нужно копнуть глубже и разбить
свою абстракцию на несколько частей. Вы должны расширить свои знания в трех
областях: сам автомобиль (например, его размер и возможности), манипуляции
его частями (рулевое колесо, педаль акселератора и т. д.) и особенности дороги.
Абстрагирование от деталей может помочь при попытке найти и устранить проблемы. Предположим, что вы ведете машину и поездка дается тяжело. Вы можете

1.1. Уровни абстракции в системе Linux   29
быстро оценить три основные упомянутые абстракции, связанные с автомобилем,
чтобы определить источник проблемы. Если с первыми двумя абстракциями все
в порядке (ваш автомобиль и способ вождения) и ни одна из них не является проблемой, можете сузить проблему до самой дороги. В таком случае выясняется, что
дорога ухабистая. Теперь, если нужно, вы можете копнуть глубже в абстракцию
дороги и подумать, почему ее состояние ухудшилось или, если она новая, почему
строители так паршиво сделали свою работу.
Разработчики программного обеспечения используют абстракцию в качестве инструмента при создании операционной системы и ее приложений. В программном
обеспечении существует множество терминов для абстрактного разделения, включая «подсистему», «модуль» и «пакет», но в этой главе мы будем применять термин
«компонент», потому что так проще. При создании программного компонента разработчики обычно не задумываются о внутренней структуре других компонентов,
но рассматривают те из них, которые могут задействовать (чтобы не приходилось
писать дополнительное ненужное программное обеспечение), и размышляют о том,
как их использовать.
В этой главе мы подробно рассмотрим компоненты, составляющие систему Linux.
Хотя каждый из них имеет огромное количество технических деталей, сейчас не
будем брать их в расчет и сосредоточимся на том, что именно компоненты делают
по отношению ко всей системе. А детали рассмотрим в последующих главах.

1.1. Уровни абстракции в системе Linux
Использование абстракции для разделения вычислительных систем на компоненты
облегчает понимание, но не работает без организации процессов. Мыобъединяем
компоненты в слои или уровни классификации (или группы) в соответствии с тем,
где они находятся между пользователем и оборудованием. Веб-браузеры, игры и т. п.
находятся на верхнем уровне, на нижнем располагается память в компьютерном
оборудовании — нули и единицы. Операционная система занимает множество
промежуточных уровней.
Система Linux имеет три основных уровня. На рис. 1.1 показаны эти уровни и компоненты внутри каждого из них. Аппаратное оборудование компьютера (hardware)
находится на базовом уровне. Оно включает в себя память и один или несколько
процессоров (CPU) для выполнения вычислений, а также для чтения из памяти
и записи в нее. Такие устройства, как диски и сетевые интерфейсы, — тоже часть
аппаратного оборудования.
Следующий уровень — это ядро (kernel), основа операционной системы. Ядро — это
программное обеспечение, содержащееся в памяти. Оно сообщает процессору, где
находится его следующая задача. Выступая в качестве посредника, ядро управляет
оборудованием (особенно оперативной памятью) и является основным интерфейсом между оборудованием и любой запущенной программой.

30   Глава 1. Общая картина

Рис. 1.1. Основные компоненты системы Linux
Процессы (processes) — запущенные программы, которыми управляет ядро, — в совокупности составляют верхний уровень системы, называемый пользовательским
пространством (user space). (Более конкретный термин для процесса — «пользовательский процесс» (user process), независимо от того, взаимодействует ли пользователь непосредственно с процессом. Например, все веб-серверы работают как
пользовательские процессы.)
Существует огромная разница между тем, как работает ядро и пользовательские
процессы: ядро работает в режиме ядра (kernel mode), а пользовательские процессы — в пользовательском режиме. Код, работающий в режиме ядра, имеет неограниченный доступ к процессору и оперативной памяти. Такая мощная, но опасная
привилегия позволяет легко повредить ядро и вывести из строя всю систему.
Область памяти, доступ к которой может получить только ядро, называется пространством ядра (kernel space).
Пользовательский же режим ограничен доступом к подмножеству памяти (обычно
довольно небольшому) и безопасным операциям процессора. Пользовательское
пространство — это части основной памяти, к которым могут получить доступ
пользовательские процессы. Если процесс совершает ошибку и выходит из строя,

1.2. Оборудование: оперативная память   31
последствия локальны и устраняются с помощью ядра. Это означает, что если ваш
веб-браузер выйдет из строя, он не будет продолжать научные вычисления, которые
выполнялись в фоновом режиме в течение нескольких дней.
Теоретически, пользовательский процесс, вышедший из строя, не может нанести
серьезного ущерба остальной части системы. На самом деле это зависит от того,
что вы считаете серьезным ущербом, а также от особых привилегий процесса, ведь
некоторым процессам разрешено делать больше, чем другим. Например, может ли
пользовательский процесс полностью уничтожить данные на диске? Если имеет
необходимые привилегии — да, и это довольно опасно. Однако существуют меры
предосторожности, и большинству процессов просто не разрешается сеять хаос
в системе.
ПРИМЕЧАНИЕ
Ядро Linux может запускать потоки ядра (kernel threads), очень похожие на процессы, но
имеющие доступ к пространству ядра, например kthreadd и kblockd.

1.2. Оборудование: оперативная память
Оперативная память — это, пожалуй, самый важный элемент оборудования компьютерной системы. В своей первоначальной форме оперативная память — это
просто огромная область хранения для группы нулей и единиц. Каждый слот для
нуля или единицы называется битом. Именно в оперативной памяти хранятся
запущенные ядро и процессы — по сути, тоже большие наборы битов. Все входные
и выходные данные с периферийных устройств проходят через оперативную память также в виде набора битов. Процессор — это оператор памяти, он считывает
свои инструкции и данные из памяти и записывает данные обратно в память.
В отношении памяти, процессов, ядра и других частей компьютерной системы часто
используется термин «состояние» (state). Строго говоря, состояние — это особое
расположение битов. Например, если у вас есть четыре бита в памяти, то биты 0110,
0001 и 1011 представляют три разных состояния системы.
Если учесть, что один процесс может легко состоять из миллионов битов в памяти, то когда речь идет о состояниях, проще использовать абстрактные термины.
Вместо того чтобы описывать состояние с помощью битов, процесс описывают
как завершенный или происходящий в данный момент. Например, вы можете
сказать: «Процесс ожидает ввода» или «Процесс находится на втором этапе запуска».
ПРИМЕЧАНИЕ
Поскольку принято ссылаться на состояние в абстрактных терминах, а не на фактические
биты, термин «образ» (image) относится к определенному физическому расположению
битов.

32   Глава 1. Общая картина

1.3. Ядро
Почему мы говорим об оперативной памяти и состояниях? Почти все, что делает
ядро, связано с оперативной памятью. Одной из задач ядра является разделение
памяти на множество областей, и оно должно постоянно отслеживать определенную информацию об их состоянии. Каждый процесс получает некоторую часть
памяти, и ядро должно обеспечивать необходимое количество памяти для каждого
из процессов. Ядро отвечает за управление задачами в четырех основных областях
системы.
Процессы. Ядро определяет, каким процессам разрешено использовать
процессор.
Память. Ядро должно отслеживать распределение памяти: сколько в данный
момент выделено конкретному процессу, сколько можно разделить между
другими процессами и сколько свободно.
Драйверы устройств. Ядро действует как интерфейс между оборудованием
(например, диском) и процессами. Обычно оно управляет подключенным
оборудованием.
Системные вызовы и поддержка. Процессы обычно используют системные
вызовы для связи с ядром.
Далее мы кратко изучим каждую из этих областей.
ПРИМЕЧАНИЕ
Если вы хотите изучить работу ядра более подробно, то я советую прочитать две отличные
книги на эту тему: десятое издание книги Ави Зильбершаца Operating System Concepts
(Wiley, 2018) и четвертое издание книги Эндрю Таненбаума Modern Operating Systems
(Prentice Hall, 2014).

1.3.1. Управление процессами
Управление процессами описывает запуск, приостановку, возобновление, планирование и завершение процессов. Концепции, лежащие в основе запуска и завершения
процессов, довольно просты, но описание того, как процессом используется процессор, немного сложнее.
В любой современной операционной системе многие процессы выполняются параллельно. Например, у вас могут быть открыты веб-браузер и электронная таб­
лица одновременно. Однако все не так, как кажется: процессы, стоящие за этими
приложениями, обычно не выполняются в одно и то же время.
Рассмотрим систему с одноядерным процессором. Многие процессы могут задействовать процессор, но только один из них фактически использует процессор

1.3. Ядро   33
в любой момент времени. На практике процесс работает с процессором в течение
небольшой доли секунды и делает паузу, затем другой процесс занимает процессор
в течение еще одной небольшой доли секунды, потом в дело вступает еще один
процесс и т. д. Акт передачи одним процессом управления процессором другому
процессу называется переключением контекста.
Каждый отрезок времени, называемый квантом времени, позволяет процессу выполнить значительные вычисления (и действительно, процесс часто завершает свою
задачу в течение одного кванта времени). Однако из-за того, что кванты очень малы,
пользователи их не воспринимают, и возникает ощущение, что система выполняет
несколько процессов одновременно (режим многозадачности).
Ядро отвечает за переключение контекста. Чтобы понять, как это работает, рассмотрим ситуацию, в которой процесс работает в пользовательском режиме, но его
временной квант истек. Вот что происходит.
1. Процессор (фактическое оборудование) прерывает текущий процесс на основе внутреннего таймера, переключается в режим ядра и передает управление
обратно ядру.
2. Ядро записывает текущее состояние процессора и памяти, что необходимо
для возобновления только что прерванного процесса.
3. Ядро выполняет любые задачи, которые могли возникнуть в течение предыдущего временного кванта (например, сбор данных из ввода-вывода).
4. Теперь ядро готово к запуску другого процесса. Оно анализирует список
процессов, готовых к запуску, и выбирает один из них.
5. Ядро подготавливает память для нового процесса, а затем готовит к нему
процессор.
6. Ядро сообщает процессору длительность временного кванта для нового
процесса.
7. Ядро переключает процессор в пользовательский режим и передает управление процессором процессу.
Переключение контекста позволяет понять, когда именно запускается ядро. Суть
заключается в том, что ядро запускается между временными квантами процесса во
время переключения контекста.
В случае многопроцессорной системы, как и в большинстве современных машин,
все немного сложнее, потому что ядро не перестает управлять текущим процессором, чтобы позволить процессу работать на другом процессоре, и одновременно
могут выполняться несколько процессов. Но чтобы максимально задействовать
все доступные процессоры, ядро в любом случае выполняет необходимые шаги
(и может использовать определенные лазейки, чтобы занять больше времени процессора для себя).

34   Глава 1. Общая картина

1.3.2. Управление памятью
Ядро должно управлять памятью во время переключения контекста, а это довольно
сложная задача. Должны выполняться следующие условия.
Ядро должно иметь в памяти выделенную область, к которой пользовательские процессы не могут получить доступ.
Каждому пользовательскому процессу необходима собственная область
памяти.
Один пользовательский процесс не может получить доступ к области памяти,
выделенной другому процессу.
Пользовательские процессы могут совместно работать с памятью.
Часть памяти пользовательских процессов может быть доступна только для
чтения.
Система может использовать больше памяти, чем ее существует физически,
задействуя дисковое пространство в качестве вспомогательного механизма.
К счастью для ядра, оно не одно выполняет всю работу. Современные процессоры
включают в себя блок управления памятью (memory management unit, MMU), который обеспечивает схему доступа к памяти, называемую виртуальной памятью. При
использовании виртуальной памяти процесс не получает прямого доступа к памяти
через ее физическое местоположение в компьютерной системе. Вместо этого ядро
настраивает каждый процесс, чтобы он действовал так, будто ему доступна вся
система. Когда процесс обращается к части своей памяти, MMU перехватывает
обращение и с помощью таблицы соответствий преобразует адрес памяти с точки
зрения процесса в фактическое физическое местоположение памяти в системе.
Ядро по-прежнему должно инициализировать, постоянно поддерживать и изменять
таблицу соответствий адресов памяти. Например, во время переключения контекста ядро должно заменить таблицу соответствий исходного процесса на таблицу
последующего процесса.
ПРИМЕЧАНИЕ
Реализация таблицы соответствий адресов памяти называется таблицей страниц.
Подробнее о том, как отслеживать производительность памяти, описано в главе 8.

1.3.3. Управления драйверами устройств
Роль ядра в работе с устройствами относительно проста. Устройство обычно доступно только в режиме ядра, поскольку неправильный доступ (например, пользовательский процесс, запрашивающий отключение питания) может привести к сбою
системы. Значительная проблема заключается в том, что различные устройства
редко имеют один и тот же интерфейс программирования, даже если устройства

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

1.3.4. Системные вызовы и поддержка
Существует несколько других функций ядра, доступных пользовательским процессам. К примеру, системные вызовы (system calls, syscalls) выполняют определенные задачи, которые сам по себе пользовательский процесс выполнить не
может. Например, все действия по открытию, чтению и записи файлов связаны
с системными вызовами.
Два системных вызова, fork() и exec(), важны для понимания того, как запускаются процессы.
fork() — когда процесс вызывает fork(), ядро создает почти идентичную

копию процесса.

exec() — когда процесс вызывает exec(program), ядро загружает и запускает
программу program, заменяя текущий процесс.

Все новые пользовательские процессы в системе Linux, за исключением процесса init (см. главу 6), запускаются в результате вызова fork(), и в большинстве
случаев exec() применяется для запуска новой программы вместо запуска копии
существующего процесса. Возьмем простой пример — любую программу, которую
вы запускаете в командной строке, например, команду ls, предназначенную для
отображения содержимого каталога. Когда вы вводите ls в окно терминала, оболочка, работающая внутри этого окна, вызывает fork() для создания копии оболочки, а затем новая копия оболочки вызывает exec(ls) для запуска команды ls.
На рис. 1.2 показана последовательность исполнения процессов и системных вызовов для запуска команды ls.

Рис. 1.2. Запуск нового процесса
ПРИМЕЧАНИЕ
Системные вызовы обычно обозначаются круглыми скобками. В примере на рис. 1.2
процесс, запрашивающий ядро для создания другого процесса, должен выполнить системный вызов fork (). Использование круглых скобок зависит от того, как именно вызов
будет описан на языке программирования С. Вам не нужно знать язык С, чтобы понять

36   Глава 1. Общая картина
эту книгу, просто помните, что системный вызов — это взаимодействие между процессом
и ядром. Кроме того, книга упрощает некоторые группы системных вызовов. Например,
exec() относится ко всему семейству системных вызовов, которые выполняют одну и ту
же задачу, но различаются по способу программирования. Существует также вариант
процесса, называемый потоком, который мы рассмотрим в главе 8.
Ядро поддерживает пользовательские процессы с функциями, отличными от традиционных системных вызовов, наиболее распространенными из которых являются
псевдоустройства. Они выглядят как устройства для пользовательских процессов,
но реализуются исключительно в программном обеспечении. Это означает, что
технически их не должно быть в ядре, но обычно они там присутствуют из практической необходимости. Например, устройство генератора случайных чисел ядра
(/dev/random) было бы трудно безопасно реализовать с помощью пользовательского
процесса.
ПРИМЕЧАНИЕ
Технически пользовательский процесс, который обращается к псевдоустройству, должен
задействовать системный вызов, чтобы открыть устройство, поэтому процессы не могут
полностью избежать применения системных вызовов.

1.4. Пользовательское пространство
Как уже упоминалось, оперативная память, выделяемая ядром для пользовательских процессов, называется пользовательским пространством. Поскольку
процесс — это просто состояние (или образ) в памяти, пользовательское пространство ­соответствует и всем запущенным процессам в памяти. (Для обозначения пользовательского пространства применяется также более неформальный
термин userland, иногда он означает программы, запущенные в пользовательском
пространстве.)
Большая часть реальных действий в системе Linux происходит в пользовательском пространстве. Хотя все процессы, по существу, одинаковы для ядра,
они выполняют разные задачи для пользователей. Существует элементарная
структура уровней (или слоев) обслуживания для различных типов системных
компонентов, которые представляют пользовательские процессы. На рис. 1.3
показано, как примерный набор компонентов сочетается и взаимодействует
в системе Linux. Основные службы находятся на нижнем уровне (ближе всего
к ядру), служебные — посередине, а приложения, с которыми соприкасаются
пользователи, — сверху. Рисунок 1.3 представляет собой значительно упрощенную схему, поскольку отображает только шесть компонентов, но на ней видно,
что компоненты вверху находятся ближе всего к пользователю (пользовательский интерфейс и браузер), компоненты на среднем уровне включают сервер
кэширования доменных имен, используемый веб-браузером, и есть несколько
меньших компонентов снизу.

1.5. Пользователи   37

Рис. 1.3. Типы процессов и их взаимодействие
Нижний уровень, как правило, состоит из небольших компонентов, которые выполняют отдельные несложные задачи. На среднем уровне находятся более крупные
компоненты, такие как службы почты, печати и баз данных. Наконец, компоненты
на верхнем уровне выполняют сложные задачи, которыми пользователь часто управляет напрямую. Кроме того, одни компоненты используют другие. Как правило,
если один компонент хочет использовать другой, то последний находится либо на
том же уровне обслуживания, либо на уровень ниже.
Рисунок 1.3 лишь приблизительно показывает, как организовано пользовательское пространство. На самом деле в нем нет никаких правил. Например, основная
масса приложений и служб записывают диагностические сообщения в логи (logs).
Большинство программ задействуют для этого стандартную службу логирования
syslog, но некоторые предпочитают писать логи самостоятельно.
Кроме того, некоторые компоненты пользовательского пространства трудно
классифицировать. Серверные компоненты, такие как веб-серверы и серверы баз
данных, можно считать приложениями очень высокого уровня, так как они выполняют сложные задачи, поэтому их можно поместить на верхний уровень в схеме,
изображенной на рис. 1.3. Однако пользовательские приложения могут зависеть от
этих серверов, так что такие компоненты можно разместить и на среднем уровне.

1.5. Пользователи
Ядро Linux поддерживает традиционную концепцию пользователя Unix. Пользователь (user) — это сущность, которая может запускать процессы и владеть файлами. Чаще всего он ассоциируется с именем пользователя (username), например,

38   Глава 1. Общая картина
в системе может быть пользователь с именем billyjoe. Однако ядро не управляет
именами пользователей, оно лишь идентифицирует пользователей с помощью
простых числовых идентификаторов пользователей (user ID, UID). (Подробнее
о соответствии имен пользователей их идентификаторам вы узнаете в главе 7.)
Пользователи существуют в основном для поддержки прав и границ в системе.
Каждый процесс пользовательского пространства имеет владельца (owner) и, как
говорят, выполняется от его имени. Пользователь может прекратить свои процессы
или изменить их поведение (в определенных пределах), но не может вмешиваться
в процессы других пользователей. Кроме того, пользователи могут владеть файлами
и выбирать, делиться ли ими с другими пользователями.
Система Linux обычно имеет несколько пользователей в дополнение к тем, которые
соответствуют реальным пользователям. Подробнее об этом написано в главе 3,
однако уже сейчас нужно знать о самом важном пользователе — суперпользователе
(superuser, root). Суперпользователь — это исключение из рассмотренных ранее
правил, поскольку он может завершить и изменить процессы других пользователей
и получить доступ к любому файлу в локальной системе. Человек, который может
действовать как суперпользователь, то есть имеет корневой доступ (root access),
считается администратором в традиционной системе Unix.
ПРИМЕЧАНИЕ
Работа в системе в качестве суперпользователя может быть опасной. Трудно бывает
выявить и исправить ошибки, потому что система позволит вам делать все, что угодно,
даже если это навредит ей. По этой причине разработчики систем стараются сделать
подобный доступ как можно более ненужным: например, система не требует корневого
доступа для переключения между беспроводными сетями на ноутбуке. Кроме того, как
бы ни был силен суперпользователь, он по-прежнему задействует пользовательский
режим операционной системы, а не режим ядра.
Группы (groups) — это наборы пользователей. Основная цель групп — позволить
пользователю получать доступ к файлам совместно с другими членами группы.

1.6. Что дальше?
Мы рассмотрели, что представляет собой работающая система Linux. Пользовательские процессы образуют среду, с которой вы непосредственно взаимодействуете, ядро
управляет процессами и оборудованием. И ядро, и процессы находятся в памяти.
Это важная информация, но ее недостаточно: нельзя узнать подробности о системе
Linux, не углубившись в нее и «не запачкав руки». В следующей главе мы начнем
путешествие в глубины Linux с рассмотрения основ пользовательского пространства. Попутно вы узнаете о важной части системы Linux, которая не обсуждалась
в этой главе, — долговременное хранилище (диски, файлы и т. п.). В конце концов,
вам же нужно где-то хранить свои программы и данные, верно?

2

Основные команды
и иерархия каталогов

В этой главе мы разберем основные команды и утилиты
Unix, с которыми вы будете сталкиваться на протяжении
всей книги. Эта информация начального уровня, и вам,
возможно, уже известна большая ее часть. Даже если вам
кажется, что вы все знаете, потратьте несколько минут на
то, чтобы пролистать главу и убедиться в этом, особенно это
касается темы иерархии каталогов в разделе 2.19.
Вы можете задать вопрос: «Почему же команды Unix? Разве это не книга о том, как
работает Linux?» Это так, но Linux — это разновидность Unix. В этой главе слово
«Unix» будет появляться чаще, чем «Linux», а все, что вы узнаете, относится также
к системам BSD и другим, основанным на Unix-системе. Я постарался уменьшить
количество расширений пользовательского интерфейса, специфичных для Linux,
не только для того, чтобы дать основы для применения других операционных
систем, но и потому, что эти расширения, как правило, нестабильны. Вы сможете
гораздо быстрее адаптироваться к новым версиям Linux, если будете знать основные
коман­ды. Кроме того, знание этих команд поможет вам лучше понять ядро, так как
многие из них непосредственно соответствуют системным вызовам.
ПРИМЕЧАНИЕ
Чтобы больше узнать о системе Unix, прочитайте книги для начинающих пользователей:
второе издание книги The Linux Command Line (No Starch Press, 2019), второе издание
книги Пола Абрахама UNIX for the Impatient (Addison-Wesley Professional, 1995), а также
пятое издание книги Learning the UNIX Operating System (O’Reilly, 2001).

40   Глава 2. Основные команды и иерархия каталогов

2.1. Оболочка Bourne Shell (bash): /bin/sh
Оболочка (shell) — одна из самых важных частей системы Unix. Это программа,
выполняющая команды, которые пользователи вводят в окно терминала. Эти
команды могут быть другими программами или встроенными функциями оболочки. Оболочка — это еще и среда программирования. Программисты Unix часто
разбивают типичные задачи на более мелкие компоненты и применяют оболочку
для управления ими.
Многие важные части системы на самом деле являются сценариями (скриптами,
shell scripts) оболочки — текстовыми файлами, которые содержат последовательность команд. Если раньше вы работали с системой MS-DOS, вы можете думать
о скриптах оболочки как о мощных .BAT-файлах. Поскольку скрипты важны для
работы системы, вся глава 11 полностью посвящена им.
По ходу книги вы будете пополнять свои знания о том, как обращаться с командами
с помощью оболочки. Одно из важных ее преимуществ состоит в том, что если вы
допустили ошибку, то можете увидеть, какой текст набрали, исправить ошибку,
а затем повторить снова.
Существует множество различных оболочек Unix, но все они — производные от оболочки Bourne shell (/bin/sh), стандартной оболочки, разработанной в компании Bell
Labs для ранних версий Unix. Любая система Unix требует ту или иную версию Bourne
shell для корректной работы, что будет показано на протяжении всей этой книги.
Система Linux использует расширенную версию оболочки Bourne под названием
bash, или Bourne-again. Оболочка bash — это оболочка по умолчанию в большинстве дистрибутивов Linux, и каталог /bin/sh обычно указывает на bash в системе
Linux. Для выполнения заданий из книги необходимо использовать оболочку bash.
ПРИМЕЧАНИЕ
Оболочка bash может быть не установлена в качестве оболочки по умолчанию, если вы
пользуетесь этой главой как руководством для работы с системой Unix в организации,
где не являетесь системным администратором. Вы можете изменить свою оболочку
с помощью команды chsh или обратиться за помощью к системному администратору.

2.2. Использование оболочки
После установки системы Linux необходимо создать как минимум одного обычного
пользователя для своей личной учетной записи. В этой главе зайдите в систему как
обычный пользователь.

2.2.1. Окно оболочки
После входа в систему откройте окно оболочки (часто называется терминалом).
Самый простой способ — войти с помощью графического интерфейса, такого как

2.2. Использование оболочки   41
Gnome или KDE, — открыть приложение Terminal, которое запускает оболочку
внутри нового окна. После того как вы откроете оболочку, в верхней части окна
появится подсказка, которая обычно заканчивается знаком доллара ($). В системе
Ubuntu это приглашение должно выглядеть следующим образом: name@host:path$,
а в Fedora — [name@host path]$, где name — ваше имя пользователя, host — имя вашей
машины, а path — текущий рабочий каталог (см. подраздел 2.4.1). Если вы знакомы
с системой Windows, окно оболочки будет выглядеть примерно как командная строка DOS, в macOS приложение Terminal, по сути, совпадает с окном оболочки Linux.
Эта книга содержит множество команд, которые вы будете вводить в командной
строке. Все они начинаются с одного знака $, который обозначает приглашение оболочки. Например, наберите следующую команду (только часть, которая выделена
жирным шрифтом, а не знак $) и нажмите клавишу Enter:
$ echo Hello there.

ПРИМЕЧАНИЕ
Многие команды оболочки в этой книге начинаются со знака #. Вы должны запускать их
от имени суперпользователя (root), поэтому они требуют особой осторожности. Лучше
всего при запуске таких команд применять команду sudo, чтобы защитить систему и записать все действия в лог, который вы позже сможете просмотреть на предмет возможных
ошибок. О том, как это сделать, говорится в разделе 2.20.
Теперь введите команду
$ cat /etc/passwd

Она отображает содержимое системного файла /etc/passwd, а затем возвращает
приглашение оболочки. Пока не обращайте внимания на вывод команды, вы узнаете
все об этом в главе 7.
Текст команды обычно начинается с названия программы для запуска и может
сопровождаться аргументами (arguments), которые сообщают программе, с чем
работать и как именно. В приведенном примере используется программа cat, в нее
добавлен один аргумент, /etc/passwd. Многие аргументы являются параметрами
(options), которые изменяют поведение программы по умолчанию и обычно начинаются с дефиса (-). Далее это будет показано при описании команды ls. Однако
есть исключения, которые не соответствуют этой обычной структуре команд, —
встроенные модули оболочки и переменные окружения.

2.2.2. Программа cat
Программа cat одна из самых простых для понимания в системах Unix, она просто
выводит содержимое одного или нескольких файлов или другого источника ввода.
Синтаксис команды cat выглядит следующим образом:
$ cat file1 file2 ...

42   Глава 2. Основные команды и иерархия каталогов
При выполнении cat выводит содержимое файлов file1, file2 и любых других
файлов, указанных в качестве аргументов (в примере это обозначено многоточием ...), а затем завершает работу. Программа называется cat, потому что она выполняет конкатенацию (concatenation, присоединение), когда выводит содержимое
нескольких файлов. Существует много способов запустить cat. Изучим процесс
ввода-вывода Unix с помощью этой программы.

2.2.3. Стандартный поток ввода (stdin) и стандартный
поток вывода (stdout)
Процессы Unix используют потоки ввода-вывода (Input/Output, I/O streams) для
чтения и записи данных. Они считывают данные из входных потоков и записывают данные в выходные потоки. Сами по себе потоки очень гибкие. Например,
источником потока ввода могут быть файл, устройство, окно терминала или даже
выходной поток из другого процесса.
Чтобы увидеть поток ввода в работе, введите команду cat (без аргументов) и нажмите клавишу Enter. На этот раз вывод появится не сразу, и вы не получите приглашение оболочки, потому что cat все еще работает. Теперь введите что-нибудь
и нажмите клавишу Enter в конце каждой строки. Теперь команда cat повторяет
любую строку, которую вы вводите. Как только вам это наскучит, нажмите сочетание клавиш Ctrl+D в пустой строке, чтобы завершить команду cat и вернуться
к командной строке.
Причина, по которой команда cat так сработала, связана с потоками. Если вы не
указываете входное имя файла, cat считывает данные из стандартного потока ввода
(standard input), предоставляемого ядром Linux, а не из потока, подключенного
к файлу. В этом случае стандартный поток ввода подключен к терминалу, где вы
запускаете команду cat.
ПРИМЕЧАНИЕ
Нажатие сочетания клавиш Ctrl+D на пустой строке останавливает текущую стандартную
запись ввода с терминала с сообщением EOF (end-of-file, конец файла) и в большинстве
случаев завершит и саму программу. Не путайте с сочетанием клавиш Ctrl+C, которое
обычно завершает программу независимо от ее ввода или вывода.
Стандартный поток вывода (standard output) работает точно так же. Ядро предоставляет каждому процессу стандартный поток вывода, в который процессы могут
записывать свои выходные данные. Команда cat всегда записывает свои выходные
данные в стандартный поток вывода. При запуске cat в терминале в приведенных
ранее примерах стандартный вывод был подключен к этому терминалу, так что
именно там отображался вывод.
Стандартный ввод и вывод часто сокращают как stdin и stdout соответственно. Многие команды работают так же, как команда cat: если вы не указываете входной файл,

2.3. Основные команды   43
команда читает из stdin. Вывод же немного отличается. Некоторые программы
(например, cat) отправляют выходные данные только в stdout, но другие имеют
возможность отправлять выходные данные непосредственно в файлы.
Существует и третий стандартный поток ввода-вывода, называемый стандартной
ошибкой (standard error). О нем рассказывается в подразделе 2.14.1.
Одна из самых полезных особенностей стандартных потоков заключается в том,
что вы можете легко управлять ими для чтения и записи не только в терминале
(см. раздел 2.14). В частности, вы узнаете, как подключать потоки к файлам и другим процессам.

2.3. Основные команды
Теперь рассмотрим еще несколько команд Unix. Большинство из приводимых
здесь программ работают только с несколькими аргументами, а некоторые имеют
так много параметров и форматов, что их сокращенный список был бы бессмысленным. Далее представлен упрощенный список основных команд — вам пока не
нужны все детали.

2.3.1. Команда ls
Команда ls перечисляет (lists) содержимое каталога. По умолчанию используется
текущий каталог, но вы можете добавить любой каталог или файл в качестве аргумента, и для этой операции существует много полезных параметров. Например,
применяйте ls -l для подробного (long, длинного) списка и ls -F для отображения
информации о типе файла. Вот пример длинного списка, он включает владельца
файла (столбец 3), группу (столбец 4), размер файла (столбец 5) и дату/время изменения (между столбцом 5 и именем файла):
$ ls -l
total 3616
-rw-r--r--rw-r--r--rw-r--r--rw-r--r-drwxr-xr-x
-rwxr-xr-x
-rw-r--r--rw-r--r-drwxr-xr-x
drwxr-xr-x

1
1
1
1
2
1
1
1
6
3

juser
juser
juser
juser
juser
juser
juser
juser
juser
juser

users
users
users
users
users
users
users
users
users
users

3804
4165
131219
6255
4096
7108
11309
56
4096
4096

May
Aug
Aug
May
Jul
Jun
Aug
Jul
Feb
Jul

28 10:40 abusive.c
13 10:01 battery.zip
13 10:33 beav_1.40-13.tar.gz
20 14:34 country.c
17 20:00 cs335
16 13:05 dhry
13 10:26 dhry.c
9 15:30 doit
20 13:51 dw
1 16:05 hough-stuff

Подробнее о столбце 1 этого вывода рассказывается в разделе 2.17. Вы можете пока
игнорировать столбец 2 — это количество жестких ссылок на файл, о нем говорится
в разделе 4.6.

44   Глава 2. Основные команды и иерархия каталогов

2.3.2. Команда cp
В своей простейшей форме команда cp копирует файлы. Например, чтобы скопировать файл file1 в file2, введите следующее:
$ cp file1 file2

Вы также можете скопировать файл в другой каталог, сохранив в нем то же имя файла:
$ cp file dir

Чтобы скопировать более одного файла в каталог (папку) с именем dir, попробуйте
применить команду из следующего примера, которая копирует три файла:
$ cp file1 file2 file3 dir

2.3.3. Команда mv
Команда mv (move) работает так же, как и команда cp. В своей простейшей форме
она переименовывает файл. Например, чтобы переименовать файл file1 в file2,
введите следующее:
$ mv file1 file2

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

2.3.4. Команда touch
Команда touch может создать файл. Если целевой файл уже существует, touch не
изменяет его, но обновляет временную метку (timestamp) его изменения. Например, чтобы создать пустой файл, введите следующее:
$ touch file

Затем запустите в нем команду ls -l. Появится вывод, как в следующем примере,
где дата и время указывают, когда была запущена команда touch:
$ ls -l file
-rw-r--r-- 1 juser users 0 May 21 18:32 file

Чтобы увидеть обновление метки времени, подождите не менее минуты, а затем
снова выполните ту же команду touch. Метка времени, возвращенная из команды ls -l, будет обновлена.

2.3.5. Команда rm
Команда rm удаляет (removes) файл. После этого он обычно исчезает из вашей системы
и, как правило, не может быть восстановлен, если вы не создали его резервную копию:
$ rm file

2.4. Перемещение по каталогам   45

2.3.6. Команда echo
Команда echo выводит свои аргументы в стандартный вывод:
$ echo Hello again.
Hello again.

Команда echo очень полезна для шаблонов поиска и подстановки имен файлов
оболочки (подстановочных знаков (wildcards), таких как *) и переменных (таких,
как $HOME), о которых вы узнаете позже в этой главе.

2.4. Перемещение по каталогам
Иерархия каталогов Unix начинается с каталога /, называемого также корневым
(root directory). Разделителем каталогов является косая черта (/), но не обратная
косая черта (\). В корневом каталоге есть несколько стандартных подкаталогов,
таких как /usr, о которых вы узнаете из раздела 2.19.
Ссылаясь на файл или каталог, вы указываете путь (path) или имя пути (pathname).
Путь, начинающийся со знака / (например, /usr/lib), — это полный, или абсолютный путь.
Компонент пути, обозначенный двумя точками (..), указывает на то, что это родительский каталог. Например, если вы работаете в каталоге /usr/lib, путь .. ведет
к /usr. Аналогично ../bin ведет к /usr/bin.
Одна точка (.) относится к текущему каталогу. Например, если вы находитесь
в каталоге /usr/lib, путь . по-прежнему ведет к /usr/lib, а путь ./X11 — к каталогу
/usr/lib/X11. Нет нужды применять одну точку . очень часто, потому что большинство команд по умолчанию задействуют текущий каталог, если путь не начинается
со знака / (поэтому вместо ./X11 можно использовать X11).
Путь, не начинающийся с косой черты /, называется относительным. Большую
часть времени вы будете работать с относительными путями, потому что уже находитесь в нужном каталоге или рядом с ним. Теперь, когда у вас есть представление
об основных механизмах работы с каталогами, перечислим некоторые основные
команды каталогов.

2.4.1. Команда cd
Текущий рабочий каталог (current working directory) — это каталог, в котором в данный момент находится процесс (например, командная оболочка). В дополнение
к командной строке по умолчанию в большинстве дистрибутивов Linux вы можете
просмотреть текущий каталог с помощью команды pwd, описанной в подразделе 2.5.3.

46   Глава 2. Основные команды и иерархия каталогов
Каждый процесс может самостоятельно установить собственный текущий рабочий
каталог. Команда cd изменяет текущий рабочий каталог оболочки:
$ cd dir

Если вы опустите dir, оболочка вернется в ваш домашний каталог (home directory) —
первоначальный каталог входа в систему. Некоторые программы обозначают домашний каталог символом ~ (волнистая черта, тильда).
ПРИМЕЧАНИЕ
Команда cd встроена в командную оболочку. Она не работает как отдельная программа,
потому что, если бы она запускалась как подпроцесс, то не смогла бы (чаще всего) изменить текущий родительский рабочий каталог. В данный момент это может показаться
не особо важным отличием, однако бывают моменты, когда этот факт может прояснить
путаницу.

2.4.2. Команда mkdir
Команда mkdir создает новый каталог dir:
$ mkdir dir

2.4.3. Команда rmdir
Команда rmdir удаляет каталог dir:
$ rmdir dir

Если в каталоге dir есть данные, эта команда не завершается и выводит ошибку.
Однако вы можете не захотеть сначала тщательно удалить все файлы и подкаталоги
внутри dir. Удаляйте каталог и его содержимое с помощью команды rm -r dir, но
будьте осторожны! Это одна из немногих команд, которая способна нанести серьезный ущерб, особенно если вы запускаете ее от имени суперпользователя. Параметр
-r указывает на рекурсивное удаление (recursive delete), убирающее все внутри
dir. Не применяйте флаг -r с шаблонами поиска, такими как знак звездочки (*).
И, конечно же, всегда перепроверяйте команду, прежде чем запускать ее.

2.4.4. Шаблоны поиска (переменные Wildcards)
Оболочка может сопоставлять простые шаблоны с именами файлов и каталогов,
этот процесс известен как подстановка имен файлов (globbing). Это похоже на
концепцию подстановочных знаков в других системах. Самым простым из них
является символ *, обозначающий любое количество произвольных символов.
Например, следующая команда выводит список файлов, находящихся в текущем
каталоге:
$ echo *

2.5. Команды среднего уровня   47
Оболочка сопоставляет шаблоны с именами файлов, подставляет имена файлов
в соответствии с аргументами, а затем запускает модифицированную команду. Подстановка называется расширением шаблона, потому что оболочка подставляет все
совпадающие имена файлов под упрощенное выражение. Вот несколько способов
использования символа * для расширения имен файлов.
Команда at* расширяет вывод до всех имен файлов, которые начинаются с at.
Команда *at расширяет вывод до всех имен файлов, которые заканчиваются
на at.
Команда *at* расширяет вывод до всех имен файлов, которые содержат at.
Если ни один файл не соответствует шаблону, оболочка bash не производит расширение и команда выполняется как написано буквально, без подстановки специальных символов, таких как *. Например, попробуйте выполнить команду echo
*dfkdsafh.
ПРИМЕЧАНИЕ
Если вы привыкли к командной строке Windows, то можете машинально ввести *.*, чтобы
обратиться ко всем файлам. Откажитесь от этой привычки. В Linux и других версиях Unix
используется * как символ соответствия всем файлам. В оболочке Unix *.* соответствует
только файлам и каталогам, в именах которых есть символ точки (.). Имена файлов Unix
не нуждаются в расширениях и часто не имеют их.
Другой символ шаблона поиска — знак вопроса (?) — указывает на соответствие
ровно одному произвольному символу. Например, b?at соответствует boat и brat.
Если вы не хотите, чтобы оболочка расширяла шаблон в команде, заключите его
в одинарные кавычки ('). Например, команда echo '*' выводит звездочку. Это
удобно для выполнения некоторых команд, описанных в следующем разделе, таких как grep и find. (Вы узнаете больше о заключении в кавычки в разделе 11.2.)
ПРИМЕЧАНИЕ
Важно помнить, что оболочка выполняет расширения перед выполнением команд. Поэтому, если символ * превращается в команду без расширения, оболочка больше ничего
с ней не сделает: дальнейшие интерпретации зависят от самой команды.
Конечно, это еще не все возможности шаблонов оболочки, однако пока достаточно
изучить символы * и ?. В разделе 2.7 описывается, как ведут себя шаблоны с теми
забавными файлами, которые начинаются с точки.

2.5. Команды среднего уровня
В этом разделе описаны наиболее важные команды Unix среднего уровня.

48   Глава 2. Основные команды и иерархия каталогов

2.5.1. Команда grep
Команда grep выводит строки из файла или потока ввода, соответствующие выражению. Например, чтобы напечатать строки из файла /etc/passwd, содержащие
текст root, введите следующее:
$ grep root /etc/passwd

Команда grep чрезвычайно удобна, когда нужно работать с несколькими файлами
одновременно, поскольку она печатает имя файла в дополнение к найденной строке.
Например, если вы хотите проверить каждый файл в /etc, содержащий слово root,
используйте команду
$ grep root /etc/*

Два наиболее важных параметра grep — это -i (для поиска без учета регистра) и -v
(инвертированный поиск, вывод всех строк, которые не подпадают под выражение).
Существует также более мощный вариант команды под названием egrep (является
просто синонимом grep -E).
Команда grep понимает регулярные выражения (regular expressions, regexp), шаблоны, которые основаны на теории информатики и очень распространены в утилитах
Unix. Регулярные выражения более мощны, чем шаблоны в стиле подстановочных
знаков, и у них другой синтаксис. Есть три важные вещи, которые следует помнить
о регулярных выражениях:
.* соответствует любому количеству символов, в том числе ни одному (например, * в шаблонах и подстановочных знаках);
.+ соответствует любому одному или нескольким символам;
. соответствует ровно одному произвольному символу.

ПРИМЕЧАНИЕ
Страница руководства grep(1) содержит подробное описание регулярных выражений,
но его довольно трудно читать. Чтобы узнать больше, вы можете прочитать третье издание книги Джеффри Э. Ф. Фридла «Регулярные выражения» (Символ-плюс, 2008)
или просмотреть главу о регулярных выражениях в книге Тома Кристенсена и др. «Программирование на Perl» (Символ-плюс, 2014). Если вы любите математику и интересуетесь, откуда берутся регулярные выражения, изучите книгу Джеффри Уллмана и Джона
Хопкрофта «Введение в теорию автоматов, языков и вычислений» (Диалектика, 2019).

2.5.2. Команда less
Команда less используется, когда файл довольно большой или вывод команды
длинный и не помещается на экране.
Чтобы просмотреть большой файл, например /usr/share/dict/words , можете применить команду less /usr/share/dict/words. Содержимое файла будет

2.5. Команды среднего уровня   49
отображаться фрагментами по размеру окна терминала. Нажмите Пробел,чтобы
перейти вперед по тексту файла, нажатие клавиши b (в нижнем регистре) поможет
вернуться назад. Чтобы выйти, нажмите клавишу q.
ПРИМЕЧАНИЕ
Команда less — это расширенная версия старой программы more. Настольные компьютеры и серверы Linux используют команду less, но она не применяется по умолчанию
для многих встроенных систем и других систем Unix. Если вы когда-нибудь столкнетесь
с ситуацией, когда не сможете задействовать команду less, берите команду more.
Вы также можете искать текст внутри вывода команды less. Например, для поиска
слова вперед по тексту можно ввести /word, а для поиска назад — -?ord. Когда будет
найдено совпадение, нажмите клавишу n, чтобы продолжить поиск.
Как описано в разделе 2.14, вы можете отправить стандартный вывод практически
любой программы непосредственно на стандартный ввод другой программы. Это
полезно, когда у вас есть команда с большим количеством выходных данных и вы
хотели бы использовать команду less для их просмотра. Вот пример отправки выходных данных команды grep в less:
$ grep ie /usr/share/dict/words | less

Попробуйте сделать это. Вероятно, вы найдете много подобных применений для
команды less.

2.5.3. Команда pwd
Команда pwd (print working directory, вывод рабочего каталога) просто выводит имя
текущего рабочего каталога. Вы можете задаться вопросом, зачем она нужна, если
большинство дистрибутивов Linux настраивают учетные записи пользователей
с текущим рабочим каталогом в приглашении командной строки. На это есть две
причины.
Во-первых, не все приглашения включают текущий рабочий каталог, особенно если
в дальнейшем от него нужно избавиться, потому что он занимает много места. Для
этого вам понадобится команда pwd.
Во-вторых, символические ссылки, о которых вы узнаете в разделе 2.17.2, иногда
могут скрывать истинный полный путь к текущему рабочему каталогу. Используйте
команду pwd -P, чтобы устранить эту путаницу.

2.5.4. Команда diff
Чтобы увидеть различия (difference) между двумя текстовыми файлами, задействуйте команду diff:
$ diff file1 file2

50   Глава 2. Основные команды и иерархия каталогов
Несколько параметров могут управлять форматом вывода, и он по умолчанию
наиболее понятен для пользователя. Однако большинство программистов предпочитают вывод из команды diff -u, когда им нужно перенаправить этот вывод
куда-то еще, потому что автоматизированным инструментам легче работать с таким
форматом.

2.5.5. Команда file
Если вы видите файл и не уверены в его формате, попробуйте команду file, чтобы
узнать, сможет ли система распознать его:
$ file file

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

2.5.6. Команды find и locate
Обидно, когда вы знаете, что определенный файл точно существует в каком-то из
каталогов, но не знаете, где именно. Чтобы найти файл, запустите команду find
с указанием каталога dir, как в следующем примере:
$ find dir -name file -print

Как и большинство программ, описанных в этом разделе, команда find способна еще
на некоторые необычные вещи. Однако не используйте такие параметры, как -exec,
пока не выучите наизусть форму, показанную здесь, и не поймете, зачем нужны
параметры -name и -print. Команда find принимает специальные символы, шаблоны, такие как *, но их нужно заключить в одинарные кавычки ('*'), чтобы не дать
специальным символам выполниться непосредственно в оболочке. (Как говорилось
в подразделе 2.4.4, оболочка расширяет шаблоны перед выполнением команд.)
В большинстве систем для поиска файлов используется также команда locate.
Вместо того чтобы искать файл в режиме реального времени, locate выполняет
поиск по индексу, который периодически создается системой. Поиск с помощью
команды locate намного быстрее, чем с командой find, но если нужный файл новее
созданного индекса, locate не найдет его.

2.5.7. Команды head и tail
Команды head и tail позволяют быстро просматривать часть файла или потока
данных. Например, команда head /etc/passwd показывает первые 10 строк файла
с паролями, а tail /etc/passwd — последние 10 строк.
Чтобы изменить количество отображаемых строк, используйте параметр -n, где
n — это количество строк (например, head -5 /etc/passwd). Для вывода строк, начинающихся со строки n, используйте tail +n.

2.7. Файлы с точками   51

2.5.8. Команда sort
Команда sort сортирует строки текстового файла в алфавитно-цифровом порядке.
Если строки файла начинаются с цифр и вы хотите отсортировать их в числовом
порядке, используйте параметр -n. Параметр -r изменяет порядок сортировки на
обратный.

2.6. Смена пароля и оболочки
Используйте команду passwd для изменения пароля. Система попросит ввести
старый пароль, а затем дважды ввести новый.
Лучше всего создавать длинные пароли, которые представляют собой бессмысленные предложения, но которые легко запомнить. Чем длиннее пароль (с точки
зрения числа символов), тем лучше; попробуйте использовать 16 символов или
более. (Раньше количество символов было ограничено, поэтому система советовала
добавлять необычные символы и т. п.)
Вы можете изменить свою оболочку с помощью команды chsh (на альтернативу,
такую как zsh, ksh или tcsh), но имейте в виду, что в этой книге применяется
оболочка bash, поэтому, если вы смените оболочку, некоторые примеры могут не
сработать.

2.7. Файлы с точками
Перейдите в свой домашний каталог, введите команду ls, чтобы осмотреться, а затем запустите команду ls -a. Видите ли вы разницу в результатах? При запуске
команды ls без параметра -a файлы конфигурации, называемые файлами с точками (дот-файлами, скрытыми файлами), не отображаются. Это файлы и каталоги,
имена которых начинаются с точки (.). Обычные файлы с точками — это .bashrc
и .login, а также каталоги с точками, такие как .ssh.
В файлах и каталогах с точками нет ничего особенного. Некоторые программы
не показывают их по умолчанию, так что полного беспорядка при перечислении
содержимого вашего домашнего каталога не будет. Например, команда ls не перечисляет файлы с точками, если вы не используете параметр -a. Кроме того, шаблоны
оболочки не подпадают под файлы с точками, если вы явно не применяете такой
шаблон, как .*.
ПРИМЕЧАНИЕ
Вы можете столкнуться с проблемой с шаблонами, потому что символ .* совпадает
с точками . и .. (текущий и родительский каталоги). Используйте шаблон .[^.]* или .??*,
чтобы вывести все файлы с точками, кроме текущего и родительского каталогов.

52   Глава 2. Основные команды и иерархия каталогов

2.8. Переменные окружения и оболочки
Оболочка может хранить временные переменные, называемые переменными оболочки, содержащие значения текстовых строк. Переменные оболочки очень полезны
для отслеживания значений в скриптах, а некоторые из них управляют поведением
оболочки. (Например, оболочка bash считывает переменную PS1 перед отображением приглашения.)
Чтобы присвоить значение переменной оболочки, используйте знак равенства (=).
Вот простой пример:
$ STUFF=blah

Здесь значение переменной с именем STUFF устанавливается равным значению blah.
Чтобы обратиться к этой переменной, используйте $STUFF (например, попробуйте
запустить команду echo $STUFF). Вы узнаете о многих примерах использования
переменных оболочки в главе 11.
ПРИМЕЧАНИЕ
Не ставьте никаких пробелов вокруг знака равенства (=) при присвоении значения
переменной.
Переменная окружения похожа на переменную оболочки, но она не специфична для
оболочки. Все процессы в системах Unix имеют хранилище переменных окружения.
Основное различие между переменными окружения и оболочки заключается в том,
что операционная система передает все переменные окружения оболочки программам, выполняемым оболочкой, в то время как переменные оболочки недоступны
в запускаемых вами командах.
Переменная окружения назначается с помощью команды export. Например, если
вы хотите превратить переменную оболочки $STUFF в переменную окружения,
введите следующее:
$ STUFF=blah
$ export STUFF

Поскольку дочерние процессы наследуют переменные окружения от родительских,
многие программы считывают их для собственной настройки и получения параметров. Например, вы можете поместить свои любимые параметры командной строки
less в переменную окружения LESS, и команда less будет использовать их при
запуске. (Во многих руководствах есть раздел «Окружение» (ENVIRONMENT),
описывающий эти переменные.)

2.9. Переменная пути PATH
PATH — это специальная переменная окружения, содержащая путь к команде (или

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

2.10. Специальные символы   53
команду. Например, при запуске команды ls оболочка выполняет поиск в каталогах,
перечисленных в PATH для команды ls. Если программы с одинаковым именем появляются в нескольких каталогах в пути, оболочка запускает первую из найденных
программ.
Если запустить команду echo $PATH, можно увидеть, что компоненты пути разделены
двоеточиями (:), например:
$ echo $PATH
/usr/local/bin:/usr/bin:/bin

Чтобы дать оболочке задание искать программы в большем количестве мест, измените переменную окружения PATH. Например, с помощью приведенной далее
команды можно добавить в начало пути каталог dir, чтобы оболочка просматривала
его перед поиском в любом другом каталоге переменной PATH:
$ PATH=dir:$PATH

Или можете добавить имя каталога в конец переменной PATH, в результате чего
оболочка будет просматривать каталог dir последним:
$ PATH=$PATH:dir

ПРИМЕЧАНИЕ
Вы можете случайно стереть весь свой путь, если неправильно наберете $PATH при его
изменении. Если это произошло, не паникуйте! Это не навсегда — можно просто запустить новую оболочку. (Для длительного эффекта вам нужно ввести путь неправильно
при редактировании определенного файла конфигурации, но даже это нетрудно исправить.) Самый простой способ вернуться к нормальному состоянию оболочки — закрыть
текущее окно терминала и запустить другое.

2.10. Специальные символы
При обсуждении Linux необходимо знать названия нескольких специальных символов, с которыми вы столкнетесь во время работы. Если вас забавляют подобные
вещи, изучите словарь для хакеров Jargon File (www.catb.org/jargon/html/) или его
печатную версию — книгу Эрика С. Рэймонда New Hacker’s Dictionary (MIT Press,
1996).
В табл. 2.1 приведены избранные специальные символы, многие из которых вы
уже встречали в этой главе. Некоторые утилиты, такие как язык программирования Perl, используют почти все эти специальные символы! (В скобках приведены
американские названия специальных символов.)
ПРИМЕЧАНИЕ
Вы часто будете встречать управляющие символы, отмеченные знаком карет, например,
^C для Ctrl+C.

54   Глава 2. Основные команды и иерархия каталогов
Таблица 2.1. Специальные символы
Символ

Название

Значение

*

Звездочка, или астериск
(star, asterisk)

Регулярное выражение, символ шаблона

.

Точка (dot)

Текущий каталог, разделитель имени файла/
хоста

!

Восклицательный знак
(bang)

Отрицание, история команд

|

Вертикальная черта (pipe)

Конвейер (канал) для команд

/

Косая черта (slash)

Разделитель каталогов, команда поиска

\

Обратная косая черта,
(backslash)

Литералы, макросы (но НЕ каталоги)

$

Доллар (dollar)

Переменные, конец строки

'

Одинарные кавычки (tick,
quote)

Не интерпретируемые как шаблоны литеральные строки

`

Обратный апостроф
(backtick, backquote)

Подстановка команд

"

Двойные кавычки (double
quote)

Частично интерпретируемые как шаблоны
(полулитеральные) строки

^

Карет (caret)

Отрицание, начало строки

~

Тильда, волнистая линия
(tilda, squiggle)

Отрицание, ярлык каталога

#

Октоторп, решетка, хеш,
фунт, шарп (hash, sharp,
pound)

Комментарии, препроцессор, подстановка

[ ]

Квадратные скобки (square
brackets)

Диапазоны

{ }

Фигурные скобки (curly
brackets, braces)

Блоки выражений, диапазоны

_

Нижнее подчеркивание
(underscore, under)

Заменитель пробела, когда пробелы не нужны
или не разрешены либо когда запутываются
алгоритмы автозаполнения

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

2.12. Текстовые редакторы   55
Однако лучше забыть о клавишах со стрелками и вместо этого использовать сочетания клавиш управления (control key). Если вы изучите перечисленные в табл. 2.2
сочетания, то обнаружите, что во многих программах Unix с их помощью вводить
текст гораздо удобнее, чем с использованием клавиш со стрелками.
Таблица 2.2. Сочетания клавиш командной строки
Сочетание клавиш

Действие

Ctrl+B

Переместить курсор влево

Ctrl+F

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

Ctrl+P

Переместить курсор выше (просмотреть предыдущую команду)

Ctrl+N

Переместить курсор ниже (просмотреть следующую команду)

Ctrl+A

Переместить курсор в начало строки

Ctrl+E

Переместить курсор в конец строки

Ctrl+W

Стереть предыдущее слово

Ctrl+U

Стереть все от курсора до начала строки

Ctrl+K

Стереть все от курсора до конца строки

Ctrl+Y

Вставить стертый текст (например, после использования Ctrl+U)

2.12. Текстовые редакторы
Поскольку мы перешли к теме редактирования, пришло время изучить текстовые
редакторы. Чтобы серьезно работать в системах Unix, необходимо уметь редактировать текстовые файлы, не повреждая их. Большинство частей системы используют
обычные текстовые файлы конфигурации (например, файл /etc). Редактировать
файлы несложно, но вы будете делать это так часто, что вам точно понадобится
мощный инструмент для этой работы.
Необходимо изучить один из двух стандартных текстовых редакторов Unix: vi или
Emacs. Большинство пользователей Unix непреклонны в своем выборе редактора,
но не слушайте их. Просто выбирайте сами. Если выберете тот, который соответствует вашему стилю работы, вам будет легче научиться. В принципе, выбор
сводится к следующему.
Если вам нужен редактор, который может делать практически все, имеет
обширное онлайн-руководство и вы не возражаете против дополнительного ввода текста, чтобы использовать эти функции, попробуйте редактор
Emacs.
Если скорость — это самое важное, попробуйте редактор vi, его действия
и возможности похожи на видеоигру.

56   Глава 2. Основные команды и иерархия каталогов
Книга Арнольда Роббинса, Элберта Ханна и Линды Ламб Learning the vi and Vim
Editors: Unix Text Processing (O’Reilly, 2008) подробно расскажет о редакторе vi. Что
касается редактора Emacs, используйте онлайн-учебник: запустите Emacs, нажмите сочетание клавиш Ctrl+H, а затем введите T. Или прочитайте книгу Ричарда М.
Столлмана «Руководство по GNU Emacs» (1999).
У вас может появиться соблазн поэкспериментировать с более дружелюбным редактором, например nano, Pico или одним из прочих бесчисленных графических
редакторов, но если вы склонны привыкать к тому, что попробовали первым, лучше
не начинать с них.
ПРИМЕЧАНИЕ
Редактирование текста — это этап, где появляется видимая разница между терминалом и графическим интерфейсом. Редакторы, такие как vi, запускаются внутри окна
терминала, используя стандартный интерфейс ввода-вывода терминала. Графические
редакторы запускают свое окно и представляют собственный интерфейс, независимый
от терминалов. Редактор Emacs по умолчанию работает в графическом интерфейсе, но
будет работать и в окне терминала.

2.13. Онлайн-поддержка
Системы Linux поставляются с большим количеством документации. Страницы
руководства (или справочные страницы, man pages) расскажут вам все, что нужно
знать об основных командах. Например, чтобы просмотреть страницу руководства
для команды ls, выполните команду man следующим образом:
$ man ls

Большинство страниц руководства дают в основном справочную информацию,
возможно, с некоторыми примерами и перекрестными ссылками, но не более.
Не ждите, что вам предоставят целый учебник с красивым литературным стилем
описания.
Когда у программ много параметров, на странице руководства они некоторым образом систематизированы (например, перечисляются в алфавитном порядке), но
там не сказано, какие из них важны. Обладая определенным терпением, можно
найти на справочной странице нужные параметры. Если вы нетерпеливы, попросите друга — или заплатите кому-нибудь, чтобы он стал вашим другом, — который
все найдет за вас.
Для поиска страницы руководства по ключевому слову (keyword) используйте
параметр -k:
$ man -k keyword

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

2.13. Онлайн-поддержка   57
$ man -k sort
--пропуск-comm (1) - построчное сравнение двух отсортированных файлов
qsort (3) - сортировка массива
sort (1) - сортировка строк текстового файла
sortm (1) - сортировка сообщений
tsort (1) - выполняет топологическую сортировку
--пропуск--

Выходные данные включают название страницы руководства, раздел руководства
(см. далее) и краткое описание того, что там содержится.
ПРИМЕЧАНИЕ
Используйте команду man для поиска информации о командах из предыдущих разделов.
Разделы руководства пронумерованы. Когда кто-то ссылается на страницу руководства, он указывает номер раздела в скобках рядом с именем, например ping(8).
В табл. 2.3 перечислены разделы и их номера.
Таблица 2.3. Разделы онлайн-руководства
Раздел

Описание

1

Пользовательские команды

2

Системные вызовы

3

Высокоуровневая документация библиотек программирования Unix

4

Интерфейсы устройств и информация о драйверах

5

Описание файлов конфигурации системы

6

Игры

7

Форматы файлов, соглашения и кодировки (ASCII, суффиксы и др.)

8

Системные команды и серверы

Разделы 1, 5, 7 и 8 — хорошее дополнение к этой книге. Раздел 4 используется
нечасто, а раздел 6 был бы великолепен, если бы был немного побольше. Если
вы не программист, скорее всего, раздел 3 вам не понадобится. Вы сможете понять материалы раздела 2, когда прочитаете больше о системных вызовах в этой
книге.
Общие термины упоминаются на множестве страниц руководства в нескольких
разделах. По умолчанию команда man отображает первую найденную страницу.
Вы можете выбрать страницу руководства, относящуюся к определенному разделу. Например, чтобы прочитать описание файла /etc/passwd (а не команды

58   Глава 2. Основные команды и иерархия каталогов
passwd), можете вставить номер раздела перед именем страницы следующим

образом:

$ man 5 passwd

Страницы руководства охватывают основные вопросы, но существует еще много способов получить онлайн-помощь (помимо поиска в интернете). Если вы просто ищете
определенный параметр для команды, попробуйте ввести ее имя, за которым следует
--help или -h (параметр варьируется в зависимости от команды). Вы или утонете в ненужной информации (как в случае с ls --help), или получите именно то, что ищете.
Не так давно сообщество проекта GNU решило, что ему не очень нравятся страницы
руководства, и переключилось на другой формат, называемый info (или texinfo).
Эта документация шире, чем обычная страница руководства, но и сложнее. Чтобы
получить доступ к справочному руководству, используйте команду info с указанием
команды command:
$ info command

Если вам не нравится формат вывода info, можете отправить вывод в команду less
(просто добавьте команду | less).
Некоторые пакеты сбрасывают доступную документацию в каталог /usr/share/
doc, не обращая внимания на man или info. Просматривайте этот каталог в своей
системе и, конечно же, ищите информацию в интернете.

2.14. Ввод и вывод командной оболочки
Теперь, познакомившись с основными командами, файлами и каталогами Unix,
вы готовы научиться перенаправлять стандартный ввод и вывод. Начнем со стандартного вывода.
Чтобы отправить вывод команды command в файл file вместо терминала, используйте символ перенаправления >:
$ command > file

Оболочка создает файл file, если он еще не существует. Если же file существует,
оболочка сначала стирает (затирает) исходный файл. Некоторые оболочки имеют
параметры, которые предотвращают затирание файла. (Например, можете ввести
команду set -C, чтобы избежать затирания в bash.)
Вы можете добавить выходные данные в файл, вместо того чтобы перезаписывать
его с помощью синтаксиса перенаправления >>:
$ command >> file

Это удобный способ сбора выходных данных в одном месте при выполнении последовательностей связанных команд.

2.14. Ввод и вывод командной оболочки   59
Отправить стандартный вывод команды на стандартный ввод другой команды
можно с помощью символа конвейера (|). Чтобы увидеть, как это работает, попробуйте выполнить следующие две команды:
$ head /proc/cpuinfo
$ head /proc/cpuinfo | tr a-z A-Z

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

2.14.1. Стандартная ошибка
Бывает так, что при перенаправлении стандартного вывода программа все еще
продолжает что-то выводить на терминал. Это называется стандартной ошибкой
(standard error, stderr) и представляет собой дополнительный поток вывода для
диагностики и отладки. Например, эта команда выдает ошибку:
$ ls /fffffffff > f

После завершения файл f должен быть пустым, но все равно как стандартная
ошибка на терминале появится следующее сообщение об ошибке:
ls: cannot access /fffffffff: No such file or directory

По желанию вы можете перенаправить стандартную ошибку. Например, чтобы отправить стандартный вывод в файл f и стандартную ошибку в файл e, используйте
синтаксис 2>, как здесь:
$ ls /fffffffff > f 2> e

Число 2 указывает на идентификатор потока (stream ID), который изменяется с помощью оболочки. Идентификатор потока 1 — стандартный вывод (по умолчанию),
а 2 — стандартная ошибка. Вы также можете отправить стандартную ошибку в то
же место, куда отправляется stdout, с помощью синтаксиса >&. Например, чтобы
отправить как стандартный вывод, так и стандартную ошибку в файл с именем f,
попробуйте выполнить команду
$ ls /fffffffff > f 2>&1

2.14.2. Стандартное перенаправление ввода
Чтобы направить файл на стандартный ввод программы, используйте оператор /home/origdir

Если вы попытаетесь получить доступ к каталогу somedir в данном каталоге, система вместо этого выдаст вам /home/origdir. Символические ссылки — это просто
имена файлов, которые указывают на другие имена. Их имена и пути, на которые
они указывают, не должны ничего значить. В предыдущем примере каталог /home/
origdir может даже не существовать.

70   Глава 2. Основные команды и иерархия каталогов
На самом деле, если /home/origdir не существует, любая программа, которая обращается к somedir, возвращает сообщение об ошибке, информирующее, что somedir
не существует (за исключением ls somedir — команды, которая только и сообщает
вам, что somedir — это somedir). Это может сбить с толку, потому что само название
somedir у вас перед глазами.
Символические ссылки могут вас запутать не только этим. Другая проблема заключается в том, что вы не можете определить характеристики объекта, на который
указывает ссылка, просто взглянув на ее имя, — вы должны перейти по ссылке,
чтобы увидеть, указывает ли она на файл или каталог. В вашей системе также могут
быть ссылки, указывающие на другие ссылки, которые называются связанными
символическими ссылками и могут быть помехой при попытке отслеживания.
Чтобы создать символическую ссылку от целевого объекта target к имени ссылки
linkname, используйте команду ln -s следующим образом:
$ ln -s target linkname

Аргумент linkname — это имя символической ссылки, аргумент target — путь
к файлу или каталогу, на который указывает ссылка, а флаг -s указывает на создание
символической ссылки (см. врезку далее).
При создании символической ссылки дважды проверьте команду перед ее запуском, потому что могут возникнуть ошибки. Например, если вы случайно изменили порядок аргументов (цель ln -s linkname target), то возникнет ошибка, если
linkname — уже существующий каталог. Если это так (и это довольно часто так),
команда ln создает ссылку с именем target внутри каталога linkname, и ссылка
будет указывать на себя, если linkname не содержит абсолютный путь. Если чтото пойдет не так при создании символической ссылки на каталог, проверьте этот
каталог на наличие ошибочных символических ссылок и удалите их.
Символические ссылки могут быть той еще головной болью, если вы не знаете, что
они существуют. Например, вы можете легко отредактировать то, что посчитали
копией файла, но это оказалась символическая ссылка на оригинал.
ВНИМАНИЕ
Не забывайте добавлять параметр -s при создании символической ссылки. Без этого
команда ln создает жесткую ссылку, давая дополнительное реальное имя файла данному файлу. Новое имя файла имеет такой же статус, как и старое, оно указывает
(ссылается) непосредственно на данные файла, а не на другое имя файла, как делает
символическая ссылка. Жесткие ссылки могут быть еще более запутанными, чем символические ссылки. Пока вы не поймете материал раздела 4.6, избегайте их использования.
Учитывая все предупреждения относительно символических ссылок, вы можете
задаться вопросом: зачем вообще кому-то их использовать? Как оказалось, их
подводные камни значительно компенсируются преимуществами, которые они

2.18. Архивирование и сжатие файлов   71
обеспечивают для организации файлов, и способностью легко исправлять небольшие проблемы. Один из распространенных случаев применения — поиск программой определенного файла или каталога, который уже существует где-то еще
в вашей системе. Вы не хотите делать копию, и если не можете изменить программу,
то просто создайте символическую ссылку из нее на фактическое расположение
файла или каталога.

2.18. Архивирование и сжатие файлов
Теперь, когда вы узнали о файлах, правах доступа и возможных ошибках, вам необходимо освоить gzip и tar — две общие утилиты для сжатия и архивирования
файлов и каталогов.

2.18.1. Утилита gzip
Программа gzip (GNU Zip) — это одна из современных стандартных программ
сжатия Unix. Файл, заканчивающийся на .gz, является Zip-архивом GNU. Используйте команду gunzip file.gz, чтобы распаковать файл .gz и удалить
суффикс, а чтобы снова сжать файл, примените команду gzip file.

2.18.2. Утилита tar
В отличие от ZIP-программ для других операционных систем, gzip не создает архивы файлов, то есть не упаковывает несколько файлов и каталогов в один файл.
Чтобы создать архив из нескольких файлов и каталогов, используйте команду tar:
$ tar cvf archive.tar file1 file2 ...

Архивы, созданные командой tar, обычно имеют суффикс .tar (общепринято,
необязательное условие). Например, в предыдущей команде file1, file2 и т. д. —
это имена файлов и каталогов, которые нужно заархивировать в .tar.
Флаг c активирует режим создания архива (от create). Флаги v и f имеют более
конкретные роли.
Флаг v (от verbose — «подробный») активирует подробный диагностический вывод, заставляя tar печатать имена файлов и каталогов, находящихся в архиве, при
их обнаружении. Добавление второго флага v приводит к тому, что tar печатает
сведения о размере файла и о правах доступа к нему. Если вы не хотите, чтобы
команда tar выводила эти данные, не добавляйте флаг v.
Флаг f (от file — «файл») обозначает файл-параметр. Следующим аргументом
в командной строке после флага f должно быть имя создаваемого архива tar (в предыдущем примере это .tar). Вы должны использовать этот параметр, за
которым следует имя файла, везде, за исключением ленточных накопителей. Для использования стандартного ввода или вывода установите для имени файла дефис (-).

72   Глава 2. Основные команды и иерархия каталогов

Распаковка файлов .tar
Чтобы распаковать файл .tar с помощью команды tar, задействуйте флаг x:
$ tar xvf archive.tar

В этой команде флаг x переводит tar в режим извлечения (распаковки). Вы можете
извлечь отдельные части архива, введя имена частей в конце командной строки,
но должны знать их точные имена. (Чтобы убедиться в этом, см. раздел «Режим
содержания» далее.)
ПРИМЕЧАНИЕ
При использовании режима извлечения помните, что команда tar не удаляет архивированный файл .tar после извлечения его содержимого.

Режим содержания
Перед распаковкой обычно рекомендуется проверить содержимое файла .tar в режиме содержания, задействуя флаг t вместо флага x. Этот режим проверяет базовую
целостность архива и выводит имена всех файлов внутри него. Если не протестируете
архив перед его распаковкой, то можете в итоге добавить множество беспорядочных
файлов в текущий каталог, который может оказаться очень трудно очистить.
При проверке архива в режиме t убедитесь, что создана рациональная структура
каталогов, то есть все пути к файлам в архиве начинаются с одного и того же каталога. Если вы не уверены в этом, создайте временный каталог, перейдите в него,
а затем извлеките содержимое архива. (Всегда можно использовать команду mv * ..,
если архив не создал беспорядка.)
При распаковке вы можете указать параметр -p (от permissions) для сохранения
прав доступа. Используйте его в режиме распаковки, чтобы переопределить коман­
ду umask и добавить точные права доступа, указанные в архиве. Параметр -p применяется по умолчанию во время работы от имени суперпользователя. Если у вас
возникли проблемы с правами доступа и владельцами при распаковке архива от
имени суперпользователя, дождитесь, когда команда завершится и приглашение
оболочки появится снова. Хотя вы можете извлечь лишь небольшую часть архива,
команда tar должна пройти через весь набор файлов и вы не должны прерывать процесс, потому что он устанавливает разрешения только после проверки всего архива.
Запомните все параметры и режимы tar из этого раздела, а лучше запишите в отдельном месте. Это позволит избежать ошибок с этой командой по неосторожности.

2.18.3. Сжатые архивы (.tar.gz)
Многих новичков смущает то, что архивы обычно находятся в сжатом виде, а имена
файлов заканчиваются на .tar.gz. Чтобы распаковать сжатый архив, начинайте

2.18. Архивирование и сжатие файлов   73
с конца: сначала избавьтесь от .gz, а затем от .tar. Например, две следующие коман­
ды выполняют декомпрессию и распаковку файла .tar.gz:
$ gunzip file.tar.gz
$ tar xvf file.tar

Поначалу нормально делать по одному шагу за раз: сначала запустить команду
gunzip для декомпрессии, а затем команду tar для проверки и распаковки. Чтобы
создать сжатый архив, сделайте обратное: сначала запустите tar, а затем gzip. При
частом выполнении процесса архивации и сжатия вы быстро запомните, как он
протекает. Но даже если это происходит не так уж часто, вы поймете, насколько
утомительным может стать набор текста, и вам понадобится кратчайший путь. Посмотрим, какие есть варианты.

2.18.4. Утилита zcat
Метод, описанный ранее, — не самый быстрый или эффективный способ вызова коман­
ды tar для работы со сжатым архивом, и он тратит дисковое пространство и время
ввода-вывода ядра. Лучший способ — объединить функции архивирования и сжатия
в конвейер. Например, следующий конвейер распаковывает файл .tar.gz:
$ zcat file.tar.gz | tar xvf -

Команда zcat действует так же, как и gunzip -dc . Параметр -d распаковывает
(decompress), а параметр -c отправляет результат в стандартный вывод (в данном
случае в команду tar).
Поскольку чаще используется команда zcat, версия команды tar, поставляемая
с Linux, имеет свое общепринятое сокращение. Вы можете применять z в качестве
параметра для автоматического вызова gzip, это работает как для извлечения архива (с режимами x или t в tar), так и для его создания (с помощью параметра c).
Например, проверяйте сжатый архив с помощью команды
$ tar ztvf file.tar.gz

И все же постарайтесь запомнить: используя это сокращение, на самом деле вы
выполняете два шага.
ПРИМЕЧАНИЕ
Файл .tgz — это то же самое, что и файл .tar.gz. Суффикс предназначен для использования в файловых системах FAT (на базе MS-DOS).

2.18.5. Другие утилиты сжатия
Еще две программы сжатия — это xz и bzip2, сжатые файлы которых заканчиваются на .xz и .z2 соответственно. Хотя они немного медленнее, чем gzip, однако
сильнее сжимают текстовые файлы. Для распаковки используются программы unxz
и bunzip2, и параметры обеих довольно близки к их аналогам gzip.

74   Глава 2. Основные команды и иерархия каталогов
Большинство дистрибутивов Linux поставляются с программами zip и unzip ,
совместимыми с ZIP-архивами в системах Windows. Они работают с обычными
zip-файлами, а также с самораспаковывающимися архивами, заканчивающимися
на .exe. Но если вы столкнулись с файлом, который заканчивается на .z, знайте:
вы нашли древнюю реликвию, созданную программой сжатия compress, которая
когда-то была стандартом для систем Unix. Программа gunzip может распаковать
эти файлы, но создать не способна.

2.19. Основная иерархия каталогов Linux
Теперь, когда вы знаете, как просматривать файлы, сменять каталоги и читать страницы руководства, можете приступить к изучению системных файлов и каталогов.
Подробные сведения о структуре каталогов Linux изложены в Стандарте иерархии
файловой системы или в FHS (refspecs.linuxfoundation.org/fhs.shtml), но пока достаточно краткого пошагового руководства.
На рис. 2.2 представлен упрощенный вид иерархии, отображающий каталоги в /,
/usr и /var. Обратите внимание, что в разделе /usr содержатся некоторые из тех
же имен каталогов, что и в /.

Рис. 2.2. Иерархия каталогов в системе Linux
Вот наиболее важные подкаталоги из имеющихся в корневом каталоге.
/bin. Содержит готовые к запуску программы (также известные как исполняемые файлы), включая большинство основных команд Unix, таких как ls и cp.
Большинство программ в /bin созданы в двоичном формате на языке C, но

некоторые из них являются сценариями оболочки в современных системах.
/dev. Содержит файлы устройств. Подробнее об этом вы узнаете в главе 3.
/etc . Этот центральный каталог конфигурации системы (произносится

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

2.19. Основная иерархия каталогов Linux   75
/home. Содержит домашние (личные) каталоги для обычных пользователей.

Большинство установок Unix соответствуют этому стандарту.

/lib. Сокращение от library (библиотека). В этом каталоге находятся файлы

библиотек с кодом, который могут использовать исполняемые файлы. Существует два типа библиотек: статические и разделяемые. Каталог /lib должен
содержать только разделяемые библиотеки, но другие каталоги lib, такие
как /usr/lib, включают обе разновидности, а также другие вспомогательные
файлы. (Мы обсудим разделяемые библиотеки более подробно в главе 15.)
/proc. Предоставляет системную статистику через доступный для просмотра
интерфейс каталогов и файлов. Большая часть структуры подкаталогов /proc

в Linux уникальна, но многие другие варианты Unix имеют аналогичные
функции. Каталог /proc содержит информацию о запущенных в данный
момент процессах, а также некоторые параметры ядра.
/run. Содержит данные времени выполнения, относящиеся к системе, вклю-

чая определенные идентификаторы процессов, файлы сокетов, записи состояния и во многих случаях системный журнал. Это относительно недавнее
дополнение к корневому каталогу, в старых системах вы можете найти его
в /var/run. В более новых системах /var/run — это символическая ссылка
на /run.
/sys. Этот каталог похож на каталог /proc тем, что он предоставляет интерфейс устройствам и системе. Подробнее о /sys вы узнаете в главе 3.
/sbin. Место для системных исполняемых файлов. Программы в каталогах
/sbin связаны с управлением системой, поэтому простые пользователи
обычно не имеют компонентов /sbin в своих путях команд. Многие из утилит

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

но у пользователя может не быть доступа к файлам другого пользователя.
Многие программы задействуют этот каталог в качестве рабочей области.
Если какой-то файл важен, не помещайте его в каталог /tmp, потому что
большинство дистрибутивов очищают его при загрузке, а некоторые даже
периодически удаляют старые файлы. Кроме того, не позволяйте /tmp заполняться мусором, потому что обычно он делит пространство с важными
каталогами (например, с остальной частью каталога /).
/usr. Сокращение от user (пользователь), однако в этом подкаталоге нет

пользовательских файлов. Вместо этого он содержит большую иерархию
каталогов, включая основную часть системы Linux. Многие имена каталогов в /usr совпадают с именами в корневом каталоге (например, /usr/bin
и /usr/lib ), и они содержат файлы одного типа. (Причина, по которой
в корневом каталоге не содержится вся система, в первую очередь историческая — в прошлом это было сделано для того, чтобы снизить требования
к пространству для корневого каталога.)

76   Глава 2. Основные команды и иерархия каталогов
/var. Подкаталог переменных, куда программы записывают информацию,

которая может изменяться с течением времени. Здесь находятся системные
журналы, отслеживание активности пользователей, кэши и другие файлы,
создаваемые системными программами и управляемые ими. (Здесь также
есть каталог /var/tmp, но система не стирает его при загрузке.)

2.19.1. Другие корневые подкаталоги
В корневом каталоге есть еще несколько интересных подкаталогов.
/boot. Содержит файлы загрузчика ядра. Эти файлы относятся к самому

первому этапу запуска Linux, поэтому в этом каталоге вы не найдете информации о том, как Linux запускает свои службы. Подробнее об этом — в главе 5.
/media. Базовый каталог для съемных носителей, таких как флеш-накопители.

Этот каталог встречается во многих дистрибутивах.

/opt. Может содержать дополнительное программное обеспечение сторонних
производителей. Многие системы не используют каталог /opt.

2.19.2. Каталог /usr
Каталог /usr на первый взгляд может показаться относительно чистым, но если
взглянуть на /usr/bin и /usr/lib, можно найти множество данных: /usr — это место,
где находится большая часть пользовательских программ и данных. В дополнение
к /usr/bin, /usr/sbin и /usr/lib каталог /usr содержит следующее.
/include. Файлы заголовков, используемые компилятором языка С.
/local. Место, где администраторы могут устанавливать собственное про-

граммное обеспечение. Его структура должна выглядеть так же, как у каталогов / и /usr.
/man. Страницы руководства.
/share. Файлы, которые должны работать на других типах систем Unix без

потери функциональности. Обычно это вспомогательные файлы данных,
которые программы и библиотеки читают по мере необходимости. В прошлом сети машин совместно пользовались бы этим каталогом с файлового
сервера, но сегодня общий каталог, применяемый таким образом, встречается
редко, поскольку в современных системах нет конкретных ограничений на
пространство для таких файлов. Вместо этого в дистрибутивах Linux вы
найдете каталоги /man, /info и многие другие.

2.19.3. Местонахождение ядра
В системах Linux ядро обычно представляет собой двоичный файл /vmlinuz или
/boot/vmlinuz. Загрузчик (boot loader) загружает этот файл в память и приводит
его в действие при загрузке системы. (Подробнее о загрузчике — в главе 5.)

2.20. Запуск команд от имени суперпользователя   77
Как только загрузчик запускает ядро, основной файл ядра больше не используется запущенной системой. Однако вы найдете множество модулей, которые ядро
загружает и выгружает по требованию в ходе нормальной работы системы. Они
называются загружаемыми модулями ядра и расположены в папке /lib/modules.

2.20. Запуск команд от имени суперпользователя
Прежде чем идти дальше, вы должны научиться выполнять команды от имени
суперпользователя root. У вас может возникнуть соблазн запустить корневую оболочку, но у этого действия множество недостатков.
Нет записей о командах, изменяющих систему.
Нет записей о пользователях, которые выполняли команды, изменяющие
систему.
Нет доступа к вашей обычной среде оболочки.
Вы должны ввести пароль суперпользователя (если он у вас есть).

2.20.1. Команда sudo
Большинство дистрибутивов задействуют пакет под названием sudo, чтобы позволить администраторам запускать команды от имени суперпользователя, если они
вошли в систему от своего имени. Например, из главы 7 вы узнаете о применении
команды vipw для редактирования файла /etc/passwd. Вот как это можно сделать:
$ sudo vipw

При выполнении данной команды sudo регистрирует это действие с помощью
службы syslog в системном журнале для устройства local2. Больше о системных
журналах вы узнаете в главе 7.

2.20.2. Файл /etc/sudoers
Конечно, система не позволяет любому пользователю выполнять команды от
имени суперпользователя — вы должны настроить привилегии пользователей в ­файле /etc/sudoers. Пакет sudo имеет множество параметров (которые вы,
вероятно, никогда не будете использовать), что несколько усложняет синтаксис
в файле /etc/sudoers. Например, этот файл дает пользователям user1 и user2
возможность запускать любую команду от имени суперпользователя, не вводя
пароль:
User_Alias ADMINS = user1, user2
ADMINS ALL = NOPASSWD: ALL
root ALL=(ALL) ALL

78   Глава 2. Основные команды и иерархия каталогов
Первая строка определяет псевдоним пользователя ADMINS для двух пользователей, а вторая строка предоставляет привилегии. Часть ALL = NOPASSWD означает,
что пользователи с псевдонимом ADMINS могут применять sudo для выполнения
команд от имени суперпользователя. Второе ALL означает «любая команда». Первое
ALL означает «любой хост». (Если у вас более одной машины, можете установить
различные виды доступа для каждой машины или группы машин, но мы не будем
рассматривать эту функцию.)
Часть root ALL=(ALL) ALL просто означает, что суперпользователь может использовать sudo для выполнения любой команды на любом хосте. Дополнительный
параметр (ALL) говорит о том, что суперпользователь может выполнять команды,
как и любой другой пользователь. Вы можете расширить эту привилегию для пользователей ADMINS, добавив параметр (ALL) во вторую строку /etc/sudoers:
ADMINS ALL = (ALL) NOPASSWD: ALL

ПРИМЕЧАНИЕ
Применяйте команду visudo для редактирования файла /etc/sudoers. Эта команда проверяет файл на синтаксические ошибки после сохранения.

2.20.3. Журналы sudo
Более подробно мы рассмотрим журналы позже, однако вы уже сейчас можете
найти журналы sudo в большинстве систем с помощью команды
$ journalctl SYSLOG_IDENTIFIER=sudo

В старых системах вам нужно будет искать файл журнала в /var/log, например,
/var/log/auth.log.
На этом закончим с командой sudo. Если вам нужно задействовать еерасширенные
функции, см. страницы руководства sudoers(5) и sudo(8). (Фактическая механика
переключения пользователей рассматривается в главе 7.)

2.21. Что дальше?
Теперь вы знаете, как в командной строке запускать программы, перенаправлять вывод, взаимодействовать с файлами и каталогами, просматривать списки процессов,
страницы руководства и перемещаться по пользовательской среде системы Linux.
А еще можете выполнять команды от имени суперпользователя. Возможно, вы еще
не очень много знаете о внутренних компонентах пользовательского интерфейса
или о том, что происходит в ядре, но основы файлов и процессов вы уже изучили.
В нескольких следующих главах вы будете работать как с ядром, так и с компонентами системы пользовательского пространства, применяя только что изученные
инструменты командной строки.

3

Устройства

В этой главе описана базовая инфраструктура устройств,
обеспечиваемая ядром, в функционирующей системе
Linux.
За время существования Linux значительно изменилось
то, как именно ядро представляет устройства пользователю. Мы начнем с рассмотрения традиционной системы
файлов устройств, чтобы увидеть, как ядро предоставляет информацию о конфигурации устройства по пути sysfs. Наша цель состоит в том,
чтобы иметь возможность извлекать информацию об устройствах, имеющихся
в системе, и понять, как протекают элементарные операции. В последующих главах
будет подробнее рассмотрено взаимодействие с конкретными типами устройств.
Важно понимать, как ядро взаимодействует с пользовательским пространством при
появлении новых устройств. Система udev позволяет программам пользовательского пространства автоматически настраивать и задействовать новые устройства. Вы
увидите основные принципы того, как ядро отправляет сообщение процессу пользовательского пространства через udev, а также что именно процесс делает с ним.

3.1. Файлы устройств
Управлять большинством устройств в системе Unix легко, поскольку ядро представляет множество интерфейсов ввода-вывода устройств пользовательским процессам в виде файлов. Эти файлы устройств иногда называют узлами устройств.
Некоторые устройства доступны не только программистам, применяющим обычные
файловые операции для работы с ними, но и стандартным программам, таким как

80   Глава 3. Устройства
cat, поэтому вам не нужно быть программистом, чтобы использовать устройства.

Однако существует ограничение на то, что можно сделать с файловым интерфейсом,
поэтому не все устройства или возможности устройств доступны в стандартном
файловом вводе-выводе.
Система Linux использует тот же дизайн для файлов устройств, что и другие системы Unix. Файлы устройств находятся в каталоге /dev, и запуск команды ls /dev
отображает довольно много файлов в /dev. Итак, как же работать с устройствами?
Для начала рассмотрим команду:
$ echo blah blah > /dev/null

Как и любая другая команда с перенаправленным выводом, она отправляет данные
из стандартного вывода в файл. Однако файл является устройством /dev/null,
поэтому ядро отказывается от своих обычных файловых операций и использует
драйвер устройства для данных, записанных на это устройство. В случае с /dev/
null ядро просто принимает входные данные и отбрасывает их.
Чтобы идентифицировать устройство и просмотреть его права доступа , применяйте
команду ls -l. Вот несколько примеров:
$ ls -l
brw-rw---crw-rw-rwprw-r--r-srw-rw-rw-

1
1
1
1

root
root
root
root

disk 8, 1 Sep 6 08:37 sda1
root 1, 3 Sep 6 08:37 null
root
0 Mar 3 19:17 fdata
root
0 Dec 18 07:43 log

Обратите внимание на первый символ каждой строки (первый символ режима
файла). Если это символы b, c, p или s, то файл является устройством. Эти буквы
обозначают block (блочное устройство), character (символьное устройство), pipe
(конвейер) и socket (сокет) соответственно.
Блочное устройство. Программы получают доступ к данным с блочного
устройства фиксированными частями. Устройство sda1 в приведенном
ранее примере — это дисковое устройство, тип блочного устройства. Диски
можно легко разбить на блоки данных. Поскольку общий размер блочного
устройства фиксирован и его легко индексировать, процессы имеют быстрый
произвольный доступ к любому блоку в устройстве с помощью ядра.
Символьное устройство. Символьные устройства работают с потоками данных. Вы можете считывать символы с символьных устройств или записывать
их на символьные устройства, как было показано ранее на устройстве /dev/
null. Символьные устройства не имеют размера: когда вы читаете из одного
из них или записываете в него, ядро обычно выполняет на нем операцию
чтения или записи. Принтеры, непосредственно подключенные к компьютеру, представляются символьными устройствами. Важно отметить, что во
время взаимодействия с символьным устройством ядро не может создавать

3.2. Путь к устройству sysfs   81
резервные копии и повторно выполнять проверку после передачи данных
устройству или процессу.
Конвейер. Именованный конвейер по структуре такой же, как символьные
устройства, но с другим процессом на другом конце потока ввода-вывода
вместо драйвера ядра.
Сокет. Сокеты — это специальные интерфейсы, которые используются для
межпроцессной связи. Они часто находятся за пределами каталога /dev.
Файлы сокетов представляют собой доменные сокеты Unix, подробнее о них
вы узнаете в главе 10.
В списках файлов вывода команды ls -l с блочных и символьных устройств номера
перед датами являются основными (major) и второстепенными (minor) номерами
устройств, которые ядро использует для их идентификации. Подобные устройства
обычно имеют одинаковое основное число, например sda3 и sdb1 (оба являются
разделами жесткого диска).
ПРИМЕЧАНИЕ
Не у всех устройств есть файлы, поскольку интерфейсы ввода-вывода блочных и символьных устройств подходят не во всех случаях. Например, сетевые интерфейсы не содержат
файлов устройств. Теоретически возможно взаимодействовать с сетевым интерфейсом
с помощью одного символьного устройства, но поскольку это проблематично, ядро
предлагает другие интерфейсы ввода-вывода.

3.2. Путь к устройству sysfs
Традиционный каталог Unix /dev удобен для того, чтобы пользовательские процессы ссылались на устройства, поддерживаемые ядром, и взаимодействовали
с ними, но это очень упрощенная схема. Имя устройства в /dev немногое говорит
об устройстве. Другая проблема заключается в том, что ядро назначает устройства
в том порядке, в котором они найдены, поэтому между перезагрузками устройство
может получить другое имя.
Чтобы обеспечить единообразное представление подключенных устройств на
основе их фактических аппаратных атрибутов, ядро Linux предлагает интерфейс
sysfs для обозначения файлов и каталогов.
Базовый путь для устройств — /sys/devices. Например, жесткий диск SATA в /dev/
sda может иметь следующий путь в интерфейсе sysfs:
/sys/devices/pci0000:00/0000:00:17.0/ata3/host0/target0:0:0/0:0:0:0/block/sda

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

82   Глава 3. Устройства
для просмотра информации и управления устройством. Если вы перечислите содержимое пути устройства, как в примере, то получите подобный вывод:
alignment_offset
bdi
capability
dev
device

discard_alignment
events
events_async
events_poll_msecs
ext_range

holders
inflight
power
queue
range

removable
ro
sda1
sda2
sda5

size
uevent
slaves
stat
subsystem
trace

Файлы и подкаталоги здесь предназначены для чтения в основном программами,
а не пользователями, но вы можете получить представление о том, что они содержат
и представляют, посмотрев на пример файла /dev. Запуск команды cat dev в этом
каталоге отображает числа 8:0, которые являются основными и второстепенными
номерами устройств /dev/sda.
В каталоге /sys есть несколько ярлыков. Например, /sys/block должен содержать
все блочные устройства, доступные в системе. Однако это всего лишь символические ссылки: лучше запустить команду ls -l /sys/block, чтобы выявить истинные
sysfs пути.
Может быть трудно найти местоположение sysfs устройства в файле /dev. Используйте команду udevadm, как в следующем примере, чтобы отобразить путь и другие
интересные атрибуты:
$ udevadm info --query=all --name=/dev/sda

Более подробную информацию о команде udevadm и всей системе udev вы найдете
в разделе 3.5.

3.3. Утилита dd и устройства
Программа dd чрезвычайно полезна в работе с блочными и символьными устройствами. Ее единственная функция заключается в чтении из входного файла или потока
и записи в выходной файл или поток с выполнением некоторых преобразований
кодирования по мере необходимости. Одна особенно полезная функция команды dd
в отношении блочных устройств заключается в том, что вы можете обрабатывать
фрагмент данных в середине файла, игнорируя то, что происходит до или после.
ВНИМАНИЕ
Команда dd — очень мощный инструмент, поэтому при ее запуске убедитесь, что уверены
в своих действиях. Очень легко повредить файлы и данные на устройствах, по неосторожности допустив ошибку. Часто бывает полезно записать выходные данные в новый файл.
Команда dd копирует данные в блоках фиксированного размера. Вот пример того,
как можно использовать команду dd с символьным устройством, задействуя несколько распространенных параметров:
$ dd if=/dev/zero of=new_file bs=1024 count=1

3.4. Имена устройств   83
Как видите, формат параметров dd отличается от форматов параметров большинства
других команд Unix, он основан на старом стиле языка управления заданиями IBM
(JCL, Job Control Language). Вместо того чтобы использовать символ дефиса (-) для
обозначения параметра, вводится имя параметра и устанавливается его значение
со знаком равенства (=). В предыдущем примере один блок размером 1024 байта
копируется из /dev/zero (непрерывный поток нулевых байтов) в файл new_file.
Важные параметры команды dd.
if=file. Файл ввода. По умолчанию применяется стандартный ввод.
of=file. Файл вывода. По умолчанию используется стандартный вывод.
bs=size. Размер блока. Команда dd считывает и записывает такое количество

байтов данных за один раз. Чтобы сокращенно указать большие куски данных,
можете с помощью b и k обозначать 512 и 1024 байта соответственно. Поэтому
в предыдущем примере можно указать bs=1k вместо bs=1024.
ibs=size , obs=size . Размеры блока ввода и вывода. Если вы можете ис-

пользовать один и тот же размер блока как для ввода, так и для вывода,
применяйте параметр bs, если нет — параметры ibs и obs для ввода и вывода
соответственно.
count=num. Общее количество блоков для копирования. Во время работы

с огромным файлом (или с устройством, которое предоставляет бесконечный
поток данных, например /dev/zero) необходимо, чтобы команда dd останавливалась в фиксированной точке, в противном случае вы можете потратить
много дискового пространства, процессорного времени или и того и другого.
Используйте команду count с параметром skip для копирования небольшого
фрагмента из большого файла или устройства.
skip=num. Параметр позволяет пропустить первые несколько (num) блоков

в файле ввода или потоке и не копировать их в выходные данные.

3.4. Имена устройств
Иногда бывает трудно найти имя устройства (например, при разбиении диска на
разделы). Вот несколько способов узнать его.
Выполните udevd с помощью команды udevadm (см. раздел 3.5).
Найдите устройство в каталоге /sys.
Угадайте имя из выходных данных команды journalctl -k (которая выводит
сообщения ядра) или системного журнала ядра (см. раздел 7.1). Этот вывод
может содержать описание устройств в вашей системе.
Для дискового устройства, которое уже видно системе, можно проверить
вывод команды mount.
Запустите команду cat /proc/devices, чтобы просмотреть блочные и символьные устройства, для которых в вашей системе в настоящее время имеются

84   Глава 3. Устройства
драйверы. Каждая строка состоит из номера и имени. Этот номер является
основным номером устройства, как сказано в разделе 3.1. Если вы можете
угадать устройство по имени, найдите в каталоге /dev символьные или блочные устройства с соответствующим основным номером — и найдете файлы
устройств.
Среди этих методов надежен только первый, но он требует утилиты udev. Если вы
попадете в ситуацию, когда udev недоступна, попробуйте другие методы, но имейте
в виду, что в ядре может не быть файла устройства для вашего оборудования.
В следующих разделах рассмотрены наиболее распространенные устройства Linux
и соглашения об их именовании.

3.4.1. Жесткие диски: /dev/sd*
Большинство жестких дисков, подключенных к текущим системам Linux, соответствуют именам устройств с префиксом sd, например, /dev/sda, /dev/sdb и т. д.
Эти устройства представляют собой целые диски, ядро создает отдельные файлы
устройств /dev/sda1 и /dev/sda2 для разделов на диске.
Давайте посмотрим, откуда взялось такое название. sd — это часть имени, которое
расшифровывается как SCSI-диск. Интерфейс Small Computer System Interface
(SCSI, интерфейс малых вычислительных систем) первоначально был разработан
как стандарт аппаратного обеспечения и протокола для связи между устройствами,
например дисками и другими периферийными устройствами. Хотя традиционное
аппаратное обеспечение SCSI в большинстве современных систем не используется,
протокол SCSI широко распространен благодаря своей адаптивности. К примеру,
его применяют USB-накопители. История с дисками SATA (Serial ATA — общее
пространство хранения на Windows) немного сложнее, однако ядро Linux все еще
использует команды SCSI в определенный момент при обращении к подобным
дискам.
Чтобы перечислить устройства SCSI, имеющиеся в вашей системе, задействуйте
утилиту, которая просматривает sysfs-пути к устройствам. Одним из самых лаконичных инструментов является утилита lsscsi. Пример вывода команды lsscsi:
$ lsscsi
[0:0:0:0]❶
[2:0:0:0]

disk❷ ATA
disk FLASH

WDC WD3200AAJS-2
Drive UT_USB20

01.0 /dev/sda❸
0.00 /dev/sdb

В первом столбце ❶ указывается адрес устройства в системе, во втором ❷ описывается, что это за устройство, а в последнем (❸) говорится, где найти файл устройства.
Все остальное — это информация о поставщике устройства.
Система Linux назначает устройства файлам устройств в том порядке, в котором
их драйверы находят устройства. Таким образом, в примере ядро сначала нашло
диск, а затем флеш-накопитель.

3.4. Имена устройств   85
К сожалению, эта схема назначения устройств обычно вызывала проблемы при
перенастройке оборудования. Предположим, у вас есть система с тремя дисками:
/dev/sda, /dev/sdb и /dev/sdc. Если /dev/sdb выйдет из строя, его необходимо удалить, чтобы система снова заработала, в таком случае прежний диск /dev/sdc превратится в /dev/sdb, а диска /dev/sdc в системе больше не будет. Если при этом вы
ссылались на имена устройств непосредственно в файле fstab (см. подраздел 4.2.8),
вам пришлось бы внести изменения в него, чтобы вернуть все (практически все)
в нормальное состояние. Для решения этой проблемы многие системы Linux используют универсальный уникальный идентификатор (Universally Unique Identifier,
UUID; см. подраздел 4.2.4) и/или таблицу постоянных соответствий дисковых
устройств с помощью менеджера логических томов (Logical Volume Manager, LVM).
В этом разделе мы не говорили о том, как применять диски и другие устройства
хранения данных в системах Linux. Дополнительные сведения об использовании
дисков см. в главе 4. Позже в этой главе мы рассмотрим, как работает поддержка
SCSI в ядре Linux.

3.4.2. Виртуальные диски: /dev/xvd*, /dev/vd*
Некоторые дисковые устройства оптимизированы для виртуальных машин, таких
как AWS и VirtualBox. Система виртуализации Xen использует префиксы /dev/
xvd и /dev/vd.

3.4.3. Устройства долговременной памяти: /dev/nvme*
Некоторые системы теперь применяют интерфейс NVMe для работы с определенными типами твердотельных накопителей. В системе Linux эти устройства
отображаются в файле /dev/nvme*. Используйте команду nvme list, чтобы вывести
список этих устройств, имеющихся в вашей системе.

3.4.4. Подсистема создания виртуальных блочных устройств:
/dev/dm-*, /dev/mapper/*
В некоторых системах используется менеджер LVM, что на уровень выше по сравнению с дисками и другими прямыми блочными хранилищами. Этот менеджер
задействует систему ядра, называемую сопоставителем устройств (device mapper).
Если вы видите блочные устройства, начинающиеся с /dev/dm, и символические
ссылки в файл /dev/mapper, значит, ваша система работает с данным менеджером.
Подробнее об этом в главе 4.

3.4.5. CD- и DVD-приводы: /dev/sr*
Система Linux распознает большинство оптических накопителей как устройства
SCSI /dev/sr0, /dev/sr1 и т. д. Однако если диск использует устаревший интерфейс,

86   Глава 3. Устройства
он может отображаться как устройство PATA (описано далее). Устройства /dev/sr*
предназначены только для чтения с дисков. Для записи и перезаписи оптических
устройств вы будете применять общие устройства SCSI, например /dev/sg0.

3.4.6. Жесткие диски PATA: /dev/hd*
PATA (Parallel ATA) — это более старый тип интерфейса хранения. Блочные устройства /dev/hda, /dev/hdb, /dev/hdc и /dev/hdd распространены в более старых версиях ядра Linux и на устаревшем оборудовании. Это фиксированные назначения,
основанные на парах устройств с интерфейсами 0 и 1. Вы можете обнаружить, что
диск SATA распознается как один из этих дисков. Это означает, что диск SATA
работает в режиме совместимости, что снижает производительность. Проверьте
настройки BIOS, чтобы узнать, можно ли переключить контроллер SATA в его
собственный режим.

3.4.7. Терминалы: /dev/tty*, /dev/pts/* и /dev/tty
Терминалы — это устройства для перемещения символов между пользовательским процессом и устройством ввода-вывода (ввод текста на экран терминала).
Интерфейс терминала уходит корнями в далекие времена, когда терминалы были
устройствами на базе пишущих машинок.
Большинство терминалов сейчас — это псевдотерминальные устройства, эмулированные терминалы, которые понимают особенности ввода-вывода реальных
терминалов. Вместо того чтобы обращаться к реальному аппаратному обеспечению,
ядро представляет интерфейс ввода-вывода — окно терминала оболочки, в которое
и вводятся команды.
Двумя общими терминальными устройствами являются /dev/tty1 (первая виртуальная консоль) и /dev/pts/0 (первое псевдотерминальное устройство). Сам
каталог /dev/pts — это выделенная файловая система.
Устройство /dev/tty — управляющий терминал текущего процесса. Если программа
в настоящее время считывает и записывает данные с терминала, это устройство является синонимом этого терминала. Процесс не обязательно подключать к терминалу.

Режимы отображения и виртуальные консоли
Система Linux имеет два основных режима отображения: текстовый и графический
(в главе 14 представлены оконные системы, использующие этот режим). Хотя
системы Linux традиционно загружались в текстовом режиме, большинство дистрибутивов теперь применяют параметры ядра и промежуточные механизмы графического отображения (графические экраны загрузки, например plymouth), чтобы
полностью скрыть текстовый режим во время загрузки системы. В таких случаях
система переключается в полный графический режим ближе к концу загрузки.

3.4. Имена устройств   87
Linux поддерживает виртуальные консоли для мультиплексирования дисплея.
Каждая виртуальная консоль может работать в графическом или текстовом режиме. В текстовом режиме вы можете переключаться между консолями с помощью
комбинации функциональных клавиш и Alt. Например, сочетание Alt+F1 переводит
на терминал /dev/tty1, Alt+F2 — на терминал /dev/tty2 и т. д. Многие из этих виртуальных консолей могут быть заняты процессом getty, запускающим приглашение
консоли, как описано в разделе 7.4.
Виртуальная консоль, используемая в графическом режиме, отличается от консоли
в текстовом режиме. Вместо того чтобы получать назначение виртуальной консоли
из настроек инициализации, графическая среда задействует свободную виртуальную консоль, если не указана какая-то определенная. Например, если у вас есть
процессы getty, запущенные на терминалы tty1 и tty2, новая графическая среда
займет tty3. Кроме того, оказавшись в графическом режиме, необходимо нажать
сочетание функциональных клавиш Ctrl+Alt, чтобы переключиться на другую виртуальную консоль, вместо более простой комбинации функциональных клавиш и Alt.
Если вы хотите переключиться на текстовую консоль после загрузки системы,
нажмите сочетание клавиш Ctrl+Alt+F1. Чтобы вернуться в графическую среду, нажмите Alt+F2, Alt+F3 и т. д., пока не попадете в нужную графическую среду.
ПРИМЕЧАНИЕ
Некоторые дистрибутивы используют терминал tty1 в графическом режиме. В этом случае
вам нужно будет задействовать другие консоли.
Если у вас возникли проблемы с переключением консолей из-за неисправного механизма ввода или каких-то других обстоятельств, можете попытаться заставить
систему сменить консоли с помощью команды chvt. Например, чтобы переключиться на tty1, выполните следующую команду от суперпользователя root:
# chvt 1

3.4.8. Последовательные порты: /dev/ttyS*, /dev/ttyUSB*, /dev/ttyACM*
Устаревший тип порта RS-232 и аналогичные последовательные порты представлены как самые настоящие терминальные устройства. Количество действий в командной строке с последовательным портом ограничено из-за слишком большого
количества настроек, о которых нужно побеспокоиться, например скорости передачи данных и управлении потоком. Однако вы можете использовать команду screen
для подключения к терминалу, добавив путь к устройству в качестве аргумента.
Вам может потребоваться разрешение на чтение и запись на устройство, получить
его можно, добавив себя в определенную группу, такую как dialout.
Порт, известный в Windows как COM1, — это /dev/ttyS0, COM2 — это /dev/ttyS1
и т. д. Подключаемые последовательные адаптеры USB отображаются вместе с USB
и ACM с именами /dev/ttyUSB0, /dev/ttyACM0, /dev/ttyUSB1, /dev/ttyACM1 и т. д.

88   Глава 3. Устройства
Наиболее интересные приложения, связанные с последовательными портами, — это
платы на базе микроконтроллеров, которые вы можете подключить к своей системе
Linux для разработки и тестирования. Например, вы можете получить доступ к консоли и циклу чтения — выполнения — вывода платы CircuitPython через последовательный интерфейс USB. Нужно лишь подключить один из них, найти устройство
(обычно это /dev/ttyACM0) и подключиться к нему с помощью команды screen.

3.4.9. Параллельные порты: /dev/lp0 и /dev/lp1
Тип интерфейса, который в значительной степени был заменен USB и сетями,
где однонаправленные параллельные портовые устройства /dev/lp0 и /dev/lp1
соответствуют LPT1: и LPT2: в системе Windows. Вы можете отправлять файлы
(например, файл для печати) непосредственно на параллельный порт с помощью
команды cat, но может потребоваться дополнительно подать страницу или перезагрузить принтер. Сервер печати, такой как CUPS, гораздо эффективнее справляется
при взаимодействии с принтером.
Двунаправленными параллельными портами являются /dev/parport0 и /dev/
parport1.

3.4.10. Аудиоустройства: /dev/snd/*, /dev/dsp, /dev/audio и другие
Linux имеет два набора аудиоустройств. Существуют отдельные устройства для
системного интерфейса Advanced Linux Sound Architecture (продвинутая звуковая архитектура, ALSA) и более старой открытой звуковой системы (Open Sound
System, OSS). Устройства ALSA находятся в каталоге /dev/snd, но с ними трудно работать напрямую. Системы Linux, использующие ALSA, поддерживают устройства
с обратной совместимостью OSS, если при этом загружена поддержка OSS ядром.
Некоторые элементарные операции возможны с драйвером dsp OSS и аудио­
устройствами. Например, компьютер воспроизводит любой WAV-файл, который
вы отправляете в каталог /dev/dsp. Однако с воспроизведением могут возникнуть
проблемы из-за несоответствия частоты. Кроме того, войдя в большинство систем,
вы обнаруживаете, что устройство уже занято.
ПРИМЕЧАНИЕ
Звуки в системе Linux — это целая запутанная история из-за множества задействованных
слоев. Мы только что говорили об устройствах уровня ядра, но обычно в пользовательском пространстве существуют серверы, такие как pulse-audio, которые управляют
звуком из разных источников и выступают посредниками между звуковыми устройствами
и другими процессами пользовательского пространства.

3.4.11. Создание файла устройства
В любой относительно новой системе Linux вы не создаете собственные файлы
устройств — они создаются с помощью утилит devtmpfs и udev (см. раздел 3.5).

3.5. Менеджер устройств udev   89
Однако полезно узнать, как это сделать: хоть и редко, но вам может потребоваться
создать именованный конвейер или файл сокета.
Команда mknod создает одно устройство. Необходимо знать его имя, а также основные и второстепенные номера. Например, создать файл /dev/sda1 можно с помощью
следующей команды:
# mknod /dev/sda1 b 8 1

Параметр b 8 1 указывает на блочное устройство с основным номером 8 и второстепенным номером 1. Для устройств символьных или именованных каналов используйте параметр carp вместо b (опустите главные и второстепенные числа для
именованных конвейеров).
В более старых версиях систем Unix и Linux поддерживать каталог /dev было
сложной задачей. С каждым значительным обновлением ядра или добавлением
драйверов ядро может поддерживать больше типов устройств, а это означает, что
для имен файлов устройств будет назначен новый набор основных и второстепенных номеров. Для решения этой проблемы в каждой системе в каталоге /dev была
программа MAKEDEV для создания групп устройств. После обновления системы необходимо было обновить и программу MAKEDEV, а затем запустить ее, чтобы создать
новые устройства.
В конце концов эта статическая система стала неэффективной, так что ей начали
искать замену. Первой попыткой стала утилита devfs — реализация каталога /dev
в пространстве ядра, которая содержала все устройства, поддерживаемые текущим
ядром. Однако существовал ряд ограничений, которые привели в итоге к разработке
udev и devtmpfs.

3.5. Менеджер устройств udev
Мы уже говорили о том, что неоправданная сложность процессов в ядре опасна,
потому что она может легко сделать всю систему нестабильной. Например, управление файлами устройств: вы можете создавать файлы устройств в пользовательском пространстве, так зачем вам делать это в ядре? Ядро Linux может отправлять
уведомления процессу пользовательского пространства, называемому udevd, при
обнаружении нового устройства в системе (например, когда кто-то подключает
USB-накопитель). Процесс udevd может изучить характеристики нового устройства,
создать его файл, а затем выполнить любую инициализацию устройства.
ПРИМЕЧАНИЕ
Скорее всего, вы увидите, что утилита udevd в вашей системе работает как systemd-udevd,
потому что это часть механизма запуска, который описан в главе 6.
Это все работает в теории. К сожалению, с этим подходом есть проблема — файлы
устройств необходимы на ранних этапах загрузки, поэтому утилита udevd также

90   Глава 3. Устройства
должна запускаться в это время. Но чтобы создавать файлы устройств, udevd не
может зависеть от каких-либо устройств, которые она должна создать, поэтому ей
необходимо очень быстро выполнить первоначальный запуск, чтобы остальная
часть системы не простаивала в ожидании запуска udevd.

3.5.1. Файловая система devtmpfs
Файловая система devtmpfs была разработана для решения проблемы доступности
устройства во время загрузки (более подробную информацию о файловых системах
см. в разделе 4.2). Эта файловая система похожа на старую devfs, но она более простая. Ядро создает файлы устройств по мере необходимости, а также уведомляет
udevd о наличии нового устройства. Получив этот сигнал, udevd не создает файлы
устройств, но выполняет инициализацию устройства вместе с настройкой прав доступа и уведомлением других процессов о наличии новых устройств. Кроме того,
она создает ряд символических ссылок в каталоге /dev для дальнейшей идентификации устройств. Примеры можно найти в каталоге /dev/disk/by-id, где каждому
подключенному диску соответствует одна запись или несколько.
Например, рассмотрим ссылки на типичный диск (подключенный в /dev/sda) и его
разделы в /dev/disk/by-id:
$ ls -l /dev/disk/by-id
lrwxrwxrwx 1 root root 9 Jul 26 10:23 scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671 ->
../../sda
lrwxrwxrwx 1 root root 10 Jul 26 10:23 scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671part1 -> ../../sda1
lrwxrwxrwx 1 root root 10 Jul 26 10:23 scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671part2 -> ../../sda2
lrwxrwxrwx 1 root root 10 Jul 26 10:23 scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671part5 -> ../../sda5

Процесс udevd называет ссылки по типу интерфейса, а затем по информации о производителе и модели, серийному номеру и разделу (если есть).
ПРИМЕЧАНИЕ
Часть tmp в devtmpfs указывает на то, что файловая система находится в оперативной
памяти с возможностью чтения/записи процессами пользовательского пространства.
Данная характеристика позволяет udevd создавать эти символические ссылки. Более
подробную информацию мы рассмотрим в подразделе 4.2.12.
Но как udevd узнает, какие символические ссылки создавать, и как именно она их
создает? В следующем разделе описывается, как udevd выполняет свою работу.
Однако нет необходимости знать наизусть материал этой главы, чтобы продолжать
изучать книгу. На самом деле, если вы впервые сталкиваетесь с устройствами Linux,
настоятельно рекомендую перейти к следующей главе, чтобы начать изучать, как
использовать диски.

3.5. Менеджер устройств udev   91

3.5.2. Работа и настройка менеджера udevd
Демон udevd работает следующим образом.
1. Ядро отправляет udevd уведомление о событии, называемое uevent, по внутренней сетевой ссылке.
2. Демон udevd загружает все атрибуты события uevent.
3. Демон udevd анализирует свои правила, на их основе фильтрует и обновляет
событие uevent, а также выполняет соответствующие действия или устанавливает дополнительные атрибуты.
Входящее событие uevent, которое демон udevd получает от ядра, может выглядеть
следующим образом (вы узнаете, как получить этот вывод с помощью команды
udevadm monitor --property, в подразделе 3.5.4):
ACTION=change
DEVNAME=sde
DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/host4/
target4:0:0/4:0:0:3/block/sde
DEVTYPE=disk
DISK_MEDIA_CHANGE=1
MAJOR=8
MINOR=64
SEQNUM=2752
SUBSYSTEM=block
UDEV_LOG=3

Это конкретное событие — смена устройства. После получения события uevent демон udevd узнает имя устройства, путь к устройству sysfs и ряд других атрибутов,
связанных со свойствами, и теперь он готов начать обработку правил.
Файлы правил находятся в каталогах /lib/udev/rules.d и /etc/udev/rules.d.
Правила в каталоге /lib являются значениями по умолчанию, а правила в /etc —
переопределениями. Полное объяснение правил и больше информации можно
найти на странице руководства udev(7). Приведем некоторые основные сведения
о том, как демон udevd читает правила.
1. udevd считывает правила от начала до конца файла правил.
2. После чтения правила и, возможно, его выполнения udevd продолжает чтение
текущего файла правил для получения следующих применимых правил.
3. Существуют директивы (например, GOTO), позволяющие при необходимости
пропускать части правил в файле. Они обычно помещаются в верхней части
файла правил, чтобы пропустить весь файл, если он не имеет отношения
к конкретному устройству, которое настраивает udevd.
Рассмотрим символические ссылки из примера /dev/sda, приведенного в подразделе 3.5.1. Они были определены правилами в файле /lib/udev/rules.d/60persistent-storage.rules. Внутри вы увидите следующие строки:

92   Глава 3. Устройства
# ATA
KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi",
ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode"
# ATAPI devices (SPC-3 or later)
KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi",
ATTRS{type}=="5",ATTRS{scsi_ level}=="[6-9]*", IMPORT{program}="ata_id
--export $devnode"

Эти правила соответствуют дискам ATA и оптическим носителям, представленным
через подсистему SCSI ядра (см. раздел 3.6). Из примера видно, что есть несколько
правил для различных способов представления устройств, но суть заключается
в том, что udevd попытается подобрать устройство, начинающееся с sd или sr,
но без номера (с выражением KERNEL=="sd*[!0-9]|sr*"), а также подсистему
(SUBSYSTEMS=="scsi") и, наконец, некоторые другие атрибуты в зависимости от
типа устройства. Если все эти условные выражения истинны в любом из правил,
udevd переходит к следующему и последнему выражению:
IMPORT{program}="ata_id --export $tempnode"

Это не условие, а директива для импорта переменных из команды /lib/udev/ata_id.
Если у вас есть похожий диск, попробуйте выполнить команду самостоятельно
в командной строке. Это будет выглядеть так:
# /lib/udev/ata_id --export /dev/sda
ID_ATA=1
ID_TYPE=disk
ID_BUS=ata
ID_MODEL=WDC_WD3200AAJS-22L7A0
ID_MODEL_ENC=WDC\x20WD3200AAJS22L7A0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
\x20\x20\x20\x20\x20\x20\x20\x20\x20
ID_REVISION=01.03E10
ID_SERIAL=WDC_WD3200AAJS-22L7A0_WD-WMAV2FU80671
--пропуск--

Импорт теперь настраивает среду таким образом, чтобы все имена переменных
в этом выводе были установлены в указанные значения. Например, любое следующее правило теперь распознает ENV{ID_TYPE} как диск disk.
В двух правилах, которые мы видели ранее, особое внимание уделяется параметру
ID_SERIAL. В каждом правиле это условие следует вторым:
ENV{ID_SERIAL}!="?*"

Это выражение принимает истинное значение true, если значение ID_SERIAL не задано. Поэтому, если значение ID_SERIAL уже задано, условие будет ложным (false),
все текущее правило не применяется и udevd переходит к следующему правилу.
Почему так происходит? Цель этих двух правил — запустить процесс ata_id, чтобы найти серийный номер дискового устройства, а затем добавить эти атрибуты
в текущую рабочую копию uevent. Вы можете найти эту общую закономерность
во многих правилах udev.

3.5. Менеджер устройств udev   93
Если значение переменной ENV{ID_SERIAL} установлено, udevd может оценить это
правило позже в файле правил, который ищет любые подключенные диски SCSI:
KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_
SERIAL}=="?*",SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"

Из примера видно, что это правило требует установки ENV{ID_SERIAL} и у него есть
одна директива:
SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"

Эта директива указывает демону udevd добавить символическую ссылку для обнаруженного устройства. Итак, теперь вы знаете, откуда берутся символические
ссылки на устройства!
Возможно, вам интересно, как отличить условное выражение от директивы. Условные выражения обозначаются двумя знаками равенства (==) или восклицательным
знаком и знаком равенства и (!=), а директивы — одним знаком равенства (=),
плюсом и знаком равенства (+=) или двоеточием и знаком равенства (:=).

3.5.3. Команда udevadm
Программа udevadm — это инструмент администрирования udevd. С помощью нее
вы можете перезагрузить правила udevd и инициировать события, однако самыми
мощными функциями udevadm являются возможность поиска и изучения системных
устройств, а также возможность мониторинга событий uevents при получении их из
ядра. Однако синтаксис команд может быть довольно сложным. Для большинства
вариантов существуют длинные и короткие формы, в книге мы будем применять
длинные.
Начнем с изучения системного устройства. Возвращаясь к примеру из раздела 3.5.2,
чтобы просмотреть все атрибуты udev, используемые и генерируемые в сочетании
с правилами для устройства, такого как /dev/sda, выполните следующую команду:
$ udevadm info --query=all --name=/dev/sda

Вывод команды выглядит так:
P: /devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
N: sda
S: disk/by-id/ata-WDC_WD3200AAJS-22L7A0_WD-WMAV2FU80671
S: disk/by-id/scsi-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671
S: disk/by-id/wwn-0x50014ee057faef84
S: disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0
E: DEVLINKS=/dev/disk/by-id/ata-WDC_WD3200AAJS-22L7A0_WD-WMAV2FU80671 /dev/
disk/by-id/scsi
-SATA_WDC_WD3200AAJS-_WD-WMAV2FU80671 /dev/disk/by-id/wwn-0x50014ee057faef84 / dev/
disk/by
-path/pci-0000:00:1f.2-scsi-0:0:0:0
E: DEVNAME=/dev/sda
E: DEVPATH=/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/ sda

94   Глава 3. Устройства
E: DEVTYPE=disk
E: ID_ATA=1
E: ID_ATA_DOWNLOAD_MICROCODE=1
E: ID_ATA_FEATURE_SET_AAM=1
--пропуск--

Префикс в каждой строке указывает на атрибут или другую характеристику
устройства. В данном случае P: — это путь к устройству в sysfs, N: — узел устройства (то есть имя, данное файлу /dev), S: — указание на символическую ссылку
на узел устройства, который демон udevd поместил в /dev в соответствии с его
правилами, и E: — дополнительная информация об устройстве, извлеченная
в правилах udevd. (В этом примере отображено гораздо больше выходных данных,
чем описано, попробуйте выполнить команду самостоятельно, чтобы понять, как
она работает.)

3.5.4. Отслеживание устройств
Чтобы отслеживать события uevents с помощью утилиты udevadm, используйте
команду monitor:
$ udevadm monitor

Вывод (например, при использовании устройства флеш-накопителя) выглядит
следующим образом:
KERNEL[658299.569485] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb)
KERNEL[658299.569667] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0
(usb)
KERNEL[658299.570614] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/21.2:1.0/host15 (scsi)
KERNEL[658299.570645] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/21.2:1.0/host15/scsi_host/host15 (scsi_host)
UDEV [658299.622579] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2 (usb)
UDEV [658299.623014] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0
(usb)
UDEV [658299.623673] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/
host15 (scsi)
UDEV [658299.623690] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/
host15/scsi_host/host15 (scsi_host)
--пропуск--

В этом выводе по две копии каждого сообщения, поскольку поведение по умолчанию заключается в печати как входящего сообщения из ядра (помеченного KERNEL),
так и обрабатываемых сообщений из udevd. Чтобы видеть только события ядра, добавьте параметр --kernel, а чтобы видеть только события, обрабатываемые udevd,
используйте --udev. Чтобы просмотреть все входящие события, включая атрибуты,
как показано в подразделе 3.5.2, добавьте параметр --property. Параметры --udev
и --property вместе показывают событие после обработки.

3.6. Подробнее об интерфейсе SCSI и ядре Linux   95
Вы также можете фильтровать события по подсистемам. Например, чтобы просмотреть только сообщения ядра, относящиеся к изменениям в подсистеме SCSI,
используйте команду
$ udevadm monitor --kernel --subsystem-match=scsi

Дополнительные сведения об udevadm см. на странице руководства udevadm(8).
Возможности udev этим не ограничиваются — их гораздо больше. Например, существует демон под названием udisksd, который прослушивает события, чтобы автоматически присоединять диски и уведомлять другие процессы о наличии новых дисков.

3.6. Подробнее об интерфейсе SCSI и ядре Linux
В этом разделе мы рассмотрим поддержку SCSI в ядре Linux как способ изучения
части архитектуры ядра Linux. Чтобы начать работать с дисками и устройствами,
знать это не обязательно, поэтому, если торопитесь, перейдите к главе 4. Кроме того,
материал этого раздела более продвинутого уровня и имеет менее практический
характер, чем то, о чем мы говорили прежде, поэтому, если не хотите забираться
в дебри теории Linux, вам определенно стоит перейти к следующей главе.
Начнем с небольшой предыстории. Традиционная настройка оборудования SCSI
представляет собой адаптер хоста, связанный с цепочкой устройств по шине SCSI,
как показано на рис. 3.1. Адаптер хоста подключается к компьютеру. Адаптер хоста
и устройства имеют идентификатор SCSI ID, и на шину может приходиться 8 или
16 идентификаторов в зависимости от версии SCSI. Некоторые администраторы
используют термин «цель SCSI» для обозначения устройства и его идентификатора
SCSI ID, поскольку один конец сеанса в протоколе SCSI называется целью.

Рис. 3.1. Шина SCSI с хост-адаптером и устройствами
Любое устройство может взаимодействовать с другим устройством на равных с помощью команд SCSI. Компьютер не подключен непосредственно к цепочке устройств,
поэтому он должен проходить через адаптер хоста, чтобы взаимодействовать

96   Глава 3. Устройства
с дисками и другими устройствами. Как правило, компьютер отправляет команды
SCSI адаптеру хоста для ретрансляции на устройства, а устройства ретранслируют
ответы обратно через адаптер хоста.
Более новые версии SCSI, такие как Serial Attached SCSI (SAS, интерфейс SCSI
с последовательным подключением), обеспечивают исключительную производительность, но вы, вероятно, не найдете реальных устройств SCSI на большинстве
машин. Вы чаще будете сталкиваться с USB-накопителями, применяющими коман­
ды SCSI. Кроме того, устройства, поддерживающие ATAPI (например, приводы
CD/DVD-ROM), используют версию набора команд SCSI.
Диски SATA также отображаются в вашей системе как устройства SCSI, но они
немного отличаются, поскольку большинство их взаимодействуют через уровень
трансляции в библиотеке libata (см. подраздел 3.6.2). Некоторые контроллеры
SATA (особенно высокопроизводительные RAID-контроллеры) выполняют этот
перевод через аппаратное обеспечение.
Как все это сочетается и работает вместе? Рассмотрим устройства, показанные
в следующей системе:
$ lsscsi
[0:0:0:0]
[1:0:0:0]
[2:0:0:0]
[2:0:0:1]
[2:0:0:2]
[2:0:0:3]
[3:0:0:0]

disk
cd/dvd
disk
disk
disk
disk
disk

ATA
Slimtype
USB2.0
USB2.0
USB2.0
USB2.0
FLASH

WDC WD3200AAJS-2
DVD A DS8A5SH
CardReader CF
CardReader SM XD
CardReader MS
CardReader SD
Drive UT_USB20

01.0
XA15
0100
0100
0100
0100
0.00

/dev/sda
/dev/sr0
/dev/sdb
/dev/sdc
/dev/sdd
/dev/sde
/dev/sdf

Цифры в квадратных скобках — это (слева направо) номер адаптера хоста SCSI,
номер шины SCSI, идентификатор SCSI-устройства и LUN (Logical Unit Number,
номер логического блока, дополнительное подразделение устройства). В этом
примере имеется четыре подключенных адаптера (scsi0, scsi1, scsi2 и scsi3),
каждый из которых имеет одну шину (все с номером 0) и только одно устройство
на каждой шине (все с целевым номером 0). Считыватель USB-карт в 2:0:0 имеет
четыре логических блока — по одному для каждого типа флеш-накопителя. Ядро
назначило каждому логическому блоку отдельный файл устройства.
Несмотря на то что они не являются устройствами SCSI, устройства NVMe иногда
могут отображаться на выходе lsscsi с N в качестве номера адаптера.
ПРИМЕЧАНИЕ
Если вы хотите самостоятельно попробовать работать с утилитой lsscsi, установите ее
в качестве дополнительного пакета.
На рис. 3.2 показана иерархия драйверов и интерфейсов внутри ядра для конкретной конфигурации системы, начиная с отдельных драйверов устройств и заканчивая
драйверами блоков. Она не включает в себя обобщенные драйверы SCSI generic (sg).

3.6. Подробнее об интерфейсе SCSI и ядре Linux   97

Рис. 3.2. Схема подсистемы SCSI Linux

98   Глава 3. Устройства
Это большая структура, и на первый взгляд она может показаться ошеломляющей,
однако поток данных на рисунке крайне линейный. Начнем с анализа подсистемы
SCSI и ее трех уровней драйверов.
Верхний уровень обрабатывает операции для класса устройств. Например,
драйвер sd (SCSI disk) находится на этом уровне, он знает, как переводить
запросы из интерфейса устройства блока ядра в команды для конкретного
диска в протоколе SCSI и наоборот.
Средний уровень модерирует и направляет сообщения SCSI между верхним
и нижним уровнями, а также отслеживает все шины SCSI и устройства, подключенные к системе.
Нижний уровень обрабатывает действия, зависящие от оборудования. Драйверы здесь отправляют исходящие сообщения протокола SCSI определенным
адаптерам хоста или оборудованию и принимают входящие сообщения из
оборудования. Причина такого отделения от верхнего уровня заключается
в том, что хотясообщения SCSI одинаковы для класса устройств (например,
класса дисков), различные типы адаптеров хоста имеют различные процедуры отправки одних и тех же сообщений.
Верхний и нижний уровни содержат множество различных драйверов, но важно
помнить, что для любого файла устройства в системе ядро почти всегда использует
по одному драйверу верхнего и нижнего уровней. Для диска в каталоге /dev/sda
в нашем примере ядро применяет драйвер верхнего уровня sd и драйвер нижнего
уровня моста ATA.
Бывают случаи, когда вы можете задействовать более одного драйвера верхнего
уровня для одного аппаратного устройства (см. раздел 3.6.3). Для реальных аппаратных устройств SCSI, таких как диск, подключенный к адаптеру хоста SCSI или
аппаратному RAID-контроллеру, драйверы нижнего уровня напрямую связываются
с нижестоящим оборудованием. Однако для большинства аппаратных средств,
подключенных к подсистеме SCSI, все работает иначе.

3.6.1. USB-накопитель и SCSI
Для того чтобы подсистема SCSI могла взаимодействовать с обычными USBнакопителями, как показано на рис. 3.2, ядру требуется нечто большее, чем просто
драйвер SCSI нижнего уровня. Флеш-накопитель USB, представленный файлом
/dev/sdf, понимает команды SCSI, но для того чтобы фактически взаимодействовать с накопителем, ядро должно знать, как общаться через систему USB.
Теоретически, система USB очень похожа на систему SCSI — у нее есть классы
устройств, шины и контроллеры хостов. Поэтому неудивительно, что ядро Linux
включает в себя трехуровневую подсистему USB, схожую с подсистемой SCSI,
с драйверами класса устройств вверху, ядром управления шиной посередине
и драйверами хост-контроллера внизу. Подобно тому как подсистема SCSI передает

3.6. Подробнее об интерфейсе SCSI и ядре Linux   99
команды SCSI между своими компонентами, подсистема USB передает сообщения
USB между своими. Есть даже команда lsusb, похожая на команду lsscsi.
Компонент, который нас интересует, — это драйвер USB-накопителя в верхней
части иерархии. Он выступает в роли переводчика. На одном конце драйвер говорит на «языке» SCSI, а на другом — на «языке» USB. Поскольку оборудование
для хранения данных включает команды SCSI в свои USB-сообщения, драйвер
выполняет относительно простую работу: он в основном переупаковывает данные.
Благодаря наличию подсистем SCSI и USB у вас есть почти все необходимое для
работы с флеш-накопителями. Последним недостающим звеном является драйвер
нижнего уровня в подсистеме SCSI, поскольку драйвер USB-накопителя — это
часть подсистемы USB, а не подсистемы SCSI. (По организационным причинам две
подсистемы не должны совместно использовать один драйвер.) Чтобы заставить
подсистемы взаимодействовать друг с другом, простой драйвер SCSI-моста нижнего
уровня подключается к драйверу хранения USB-подсистемы.

3.6.2. Интерфейсы SCSI и ATA
Жесткий диск SATA и оптический привод, показанные на рис. 3.2, работают
с одним и тем же интерфейсом SATA. Для подключения специфичных для SATA
драйверов ядра к подсистеме SCSI ядро использует драйвер-мост, как и в случае
с USB-накопителями, но с другим механизмом и дополнительными сложностями.
Оптический привод задействует ATAPI — версию команд SCSI, закодированных
в протоколе ATA. Однако жесткий диск не использует ATAPI и не кодирует никаких команд SCSI!
Ядро Linux использует часть библиотеки libata для согласования дисков SATA
(и ATA) с подсистемой SCSI. Для оптических приводов, говорящих на языке ATAPI,
упаковка команд SCSI в протокол ATA и извлечение из него — это относительно
простая задача. Но для жесткого диска задача намного усложняется, потому что
библиотека должна выполнить полный перевод команд.
Работа оптического привода похожа на ввод текста книги на английском языке
в компьютер. Вам не нужно понимать, о чем она, чтобы выполнить эту задачу, и даже
не нужно понимать английский язык. Но задача для жесткого диска больше похожа
на чтение немецкой книги и перевод ее текста на английский язык. В этом случае
вам нужно понимать оба языка, а также само содержание книги.
Несмотря на сложность этой задачи, libata выполняет ее и позволяет подключать
интерфейсы ATA/SATA и устройства к подсистеме SCSI. (Обычно задействовано
больше драйверов, чем один драйвер хоста SATA, показанный на рис. 3.2.)

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

100   Глава 3. Устройства
службу ядра, которая находится поверх драйвера класса устройств SCSI (например,
sd или sr). Другими словами, большинству пользовательских процессов ничего не
нужно знать об устройствах SCSI или их командах.
Однако пользовательские процессы могут обходить драйверы классов устройств
и передавать команды протокола SCSI непосредственно устройствам через их обобщенные устройства. Например, рассмотрим систему, описанную в разделе 3.6, но
на этот раз взгляните на то, что происходит, если добавить параметр -g в команду
lsscsi, чтобы отобразить обобщенные устройства:
$ lsscsi -g
[0:0:0:0] disk
[1:0:0:0] cd/dvd
[2:0:0:0] disk
[2:0:0:1] disk
[2:0:0:2] disk
[2:0:0:3] disk
[3:0:0:0] disk

ATA
Slimtype
USB2.0
USB2.0
USB2.0
USB2.0
FLASH

WDC WD3200AAJS-2
DVD A DS8A5SH
CardReader CF
CardReader SM XD
CardReader MS
CardReader SD
Drive UT_USB20

01.0
XA15
0100
0100
0100
0100
0.00

/dev/sda
/dev/sr0
/dev/sdb
/dev/sdc
/dev/sdd
/dev/sde
/dev/sdf

❶/dev/sg0
/dev/sg1
/dev/sg2
/dev/sg3
/dev/sg4
/dev/sg5
/dev/sg6

В дополнение к обычному файлу блочного устройства каждая запись содержит
обобщенный файл устройства SCSI в последнем столбце (❶). Например, обобщенным устройством для оптического привода в /dev/sr0 является /dev/sg1.
Почему может понадобиться обобщенное устройство? Это обусловлено слож­
ностью кода ядра: когда задачи становятся особенно сложными, лучше их вывести
за пределы ядра. Представьте запись и чтение на CD/DVD. Чтение оптического диска — это довольно простая операция, и для нее есть специальный драйвер
ядра.
Однако запись на оптический диск значительно сложнее, чем чтение, и никакие
критические системные службы не зависят от действия записи. Нет никаких причин использовать пространство ядра для этой задачи. Поэтому для записи на оптический диск в Linux вы запускаете программу пользовательского пространства,
которая взаимодействует с обобщенным устройством SCSI, таким как /dev/sg1.
Эта программа может быть менее эффективной, чем драйвер ядра, но ее гораздо
проще создавать и поддерживать.

3.6.4. Методы множественного доступа к одному устройству
На рис. 3.3 показаны две точки доступа (sr и sg) для оптического привода из
пространства пользователя для подсистемы SCSI Linux (любые драйверы ниже
нижнего уровня SCSI не рассматриваются). Процесс A считывает данные с диска
с помощью драйвера sr, а процесс Б записывает данные на диск с помощью драйвера sg. Однако подобные процессы обычно не запускаются одновременно для
доступа к одному и тому же устройству.

3.6. Подробнее об интерфейсе SCSI и ядре Linux   101

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

4

Диски и файловые системы

В главе 3 мы рассмотрели дисковые устройства верхнего
уровня, доступные ядру. Здесь же мы подробно обсудим,
как работать с дисками в системе Linux. Вы узнаете, как
разбивать диски на разделы, создавать и поддерживать
файловые системы, которые находятся внутри разделов
диска, а также работать с пространством подкачки.
Напомню, что дисковые устройства имеют такие имена, как
/dev/sda, первый диск подсистемы SCSI. Этот тип блочного устрой-

ства представляет собой весь диск, но внутри самого диска есть множество различных компонентов и слоев.
На рис. 4.1 отображена схема простого диска Linux (рисунок не масштабирован).
В этой главе вы узнаете, где находится каждая его часть.
Разделы являются более мелкими частями всего диска. В Linux они обозначаются
цифрой после блочного устройства, поэтому у них есть такие имена, как /dev/
sda1 и /dev/sdb3. Ядро представляет каждый раздел как блочное устройство, как
представляло бы весь диск. Разделы определяются на небольшой области диска,
называемой таблицей разделов (также метка диска).
Ядро позволяет одновременно получать доступ как ко всему диску, так и к одному из
его разделов, но обычно в этом нет необходимости, если вы не копируете диск целиком.
Менеджер логических томов Linux (Logical Volume Manager, LVM) позволяет более
гибко управлять традиционными дисковыми устройствами и разделами и в настоящее время применяется во многих системах. Мы рассмотрим менеджер LVM
в разделе 4.4.

Диски и файловые системы   103

Рис. 4.1. Типичная схема диска Linux
ПРИМЕЧАНИЕ
Несколько разделов данных когда-то были распространены в системах с большими
дисками, поскольку старые компьютеры могли загружаться только с определенных
частей диска. Кроме того, администраторы задействовали части для резервирования
определенного объема пространства для областей операционной системы. Например,
они не хотели, чтобы пользователи могли заполнять всю систему и препятствовать работе
критически важных служб. Эта практика не уникальна для систем Unix — вы найдете
множество новых систем Windows с несколькими разделами на одном диске. Кроме
того, большинство систем имеют отдельный раздел подкачки.
Следующий уровень разбиения на разделы — это файловая система, база данных
файлов и каталогов, с которыми вы привыкли взаимодействовать в пользовательском пространстве. Файловые системы рассмотрим в разделе 4.2.
Как видно на рис. 4.1, если вы хотите получить доступ к данным в файле, вам необходимо использовать соответствующее расположение раздела из таблицы разделов, а затем выполнить поиск в базе данных файловой системы в этом разделе.
Для доступа к данным на диске ядро Linux и применяет систему слоев, показанную
на рис. 4.2. Подсистема SCSI и все остальное, описанное в разделе 3.6, представлены одним блоком. Обратите внимание: вы можете работать с диском как через

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

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

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

4.1. Разбиение дисков на разделы   105
Традиционная таблица, относящаяся к временам Windows, находится внутри
главной загрузочной записи (Master Boot Record, MBR), и у нее есть множество ограничений. Большинство новых систем используют глобальную таблицу разделов
с уникальными идентификаторами GPT (Globally Unique Identifier Partition Table).
Инструменты разделения в Linux:
parted (сокращение от PARTition EDitor, редактор разделов) — текстовый

инструмент, поддерживающий таблицы как MBR, так и GPT;
графическая версия инструмента parted;

fdisk — традиционный текстовый инструмент разбиения дисков Linux на
разделы. Последние версии fdisk поддерживают MBR, GPT и многие другие

типы таблиц разделов, но более старые версии ограничены поддержкой MBR.

Поскольку инструмент parted уже некоторое время поддерживает как MBR, так
и GPT и позволяет легко запускать отдельные команды для получения меток разделов, мы будем использовать его для отображения таблиц разделов. Однако при
создании и изменении таблиц разделов задействуем fdisk. Так мы проиллюстрируем оба интерфейса. Многие пользователи предпочитают интерфейс fdisk из-за
его интерактивного характера и того факта, что он не вносит никаких изменений
на диск, пока вы их не просмотрите (мы обсудим это в ближайшее время).
ПРИМЕЧАНИЕ
Существует огромная разница между созданием разделов и управлением файловой
системой: таблица разделов определяет простые границы на диске, в то время как
файловая система — это гораздо более сложная система данных. По этой причине мы
будем использовать отдельные инструменты для работы с разделами и создания файловых систем (см. подраздел 4.2.2).

4.1.1. Просмотр таблицы разделов
Вы можете просмотреть таблицу разделов своей системы с помощью команды
parted -l. В выводе далее показаны два дисковых устройства с двумя различными
типами таблиц разделов:
# parted -l
Model: ATA KINGSTON SM2280S (scsi)
Disk /dev/sda: 240GB ❶
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:
Number
1
2
5

Start
1049kB
223GB
223GB

End
223GB
240GB
240GB

Size
223GB
17.0GB
17.0GB

Type
File system Flags
primary ext4
boot
extended
logical linux-swap(v1)

106   Глава 4. Диски и файловые системы
Model: Generic Flash Disk (scsi)
Disk /dev/sdf: 4284MB ❷
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End
Size
File system Name
Flags
1
1049kB 1050MB 1049MB
myfirst
2
1050MB 4284MB 3235MB
mysecond

Первое устройство (/dev/sda) (❶) использует традиционную таблицу разделов
MBR, которую команда parted назвала msdos, а второе (/dev/sdf) (❷) содержит GPT.
Обратите внимание на то, что два типа таблиц хранят разные наборы параметров.
В частности, в таблице MBR нет столбца Name, поскольку в этой схеме не существует
имен. (В GPT произвольно выбраны имена myfirst и mysecond.)
ПРИМЕЧАНИЕ
Следите за размерами блоков при чтении таблиц разделов. Вывод команды parted показывает приблизительный размер, который легче всего читать. В то же время команда fdisk -l
показывает точное число, но в большинстве случаев единицы измерения представляют собой
512-байтовые секторы, что может сбить с толку, поскольку может показаться, что фактические размеры вашего диска и разделов были удвоены. При внимательном изучении таблицы
разделов fdisk вы увидите, что в ней отображается также информация о размере сектора.

Основы MBR
Таблица MBR в этом примере содержит основной, расширенные и логические разделы. Основной раздел — это обычный раздел диска, например раздел 1. Базовый
MBR имеет ограничение в четыре основных раздела, поэтому, если вам нужно
больше четырех, необходимо назначить один из них расширенным разделом. Расширенный раздел разбивается на логические разделы, которые операционная система
затем может использовать, как и любой другой раздел. В этом примере раздел 2
представляет собой расширенный раздел, содержащий логический раздел 5.
ПРИМЕЧАНИЕ
Тип файловой системы, выводимый командой parted, не обязательно совпадает с полем
идентификатора системы в его записях MBR. Идентификатор системы MBR — это просто
число, определяющее тип раздела: например, 83 — это раздел Linux, а 82 — область
подкачки Linux. Кроме того, parted самостоятельно определяет, какая файловая система
находится в этом разделе. Если вам необходимо знать идентификатор системы для MBR,
используйте команду fdisk -l.

Разделы LVM: краткий обзор
Если при просмотре таблицы разделов вы видите разделы, помеченные как LVM
(код 8e в качестве типа раздела), устройства с именем /dev/dm -* или ссылки
на device mapper, значит, ваша система использует менеджер LVM. Мы начнем

4.1. Разбиение дисков на разделы   107
с традиционного прямого разделения дисков, которое выглядит немного иначе,
чем в системе, применяющей LVM.
Чтобы вы знали, чего ожидать, взглянем на некоторые примеры вывода команды
parted -l в системе с менеджером LVM (свежеустановленная система Ubuntu с использованием LVM в VirtualBox). Во-первых, есть описание фактической таблицы
разделов, которая выглядит ожидаемо, за исключением флага lvm:
Model: ATA VBOX HARDDISK (scsi)
Disk /dev/sda: 10.7GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:
Number Start End
Size
Type
File system Flags
1
1049kB 10.7GB 10.7GB primary
boot, lvm

Затем есть некоторые устройства, которые выглядят так, как будто они должны
быть разделами, но называются дисками:
Model: Linux device-mapper (linear) (dm)
Disk /dev/mapper/ubuntu--vg-swap_1: 1023MB
Sector size (logical/physical): 512B/512B
Partition Table: loop
Disk Flags:
Number Start End
Size
File system
Flags
1
0.00B 1023MB 1023MB linux-swap(v1)
Model: Linux device-mapper (linear) (dm)
Disk /dev/mapper/ubuntu--vg-root: 9672MB
Sector size (logical/physical): 512B/512B
Partition Table: loop
Disk Flags:
Number Start End
Size
File system Flags
1
0.00B 9672MB 9672MB ext4

Простой способ представить процесс — это понять, что разделы отделены от таблицы разделов. Что происходит на самом деле, вы узнаете из раздела 4.4.
ПРИМЕЧАНИЕ
При использовании команды fdisk -l вывод окажется менее подробным: в приведенном
ранее примере вы не увидите ничего, кроме одного физического раздела с меткой LVM.

Первичное чтение ядром
При первоначальном чтении таблицы MBR ядро Linux выдает вывод отладки, как
в следующем примере (можно просмотреть его с помощью команды journalctl -k):
sda: sda1 sda2 < sda5 >

108   Глава 4. Диски и файловые системы
Часть вывода sda2 < sda5 > указывает на то, что /dev/sda2 — это расширенный раздел, содержащий один логический раздел /dev/sda5. Обычно сам расширенный
раздел игнорируется, поскольку необходим доступ именно к логическим разделам,
которые он содержит.

4.1.2. Редактирование таблиц разделов
Просмотр таблиц разделов — довольно простая и безвредная операция. Изменение
таблиц разделов также относительно несложная задача, но внесение такого рода
изменений на диск сопряжено с риском. Помните:
изменение таблицы разделов затрудняет восстановление любых данных
в разделах, которые вы удаляете или переопределяете, поскольку это может
привести к удалению расположения файловых систем на этих разделах. Если
диск содержит важные данные, убедитесь, что у вас есть резервная копия;
никакие разделы на целевом диске в момент изменения не должны использоваться. Это важно, поскольку большинство дистрибутивов Linux автоматически монтируют любую обнаруженную файловую систему. (Дополнительные
сведения о монтировании и демонтировании см. в подразделе 4.2.3.)
Когда будете готовы, выберите программу разбиения на разделы. Если хотите использовать parted, примените утилиту parted командной строки или графический
интерфейс, например gparted, а с fdisk довольно легко работать в командной строке.
У всех этих утилит есть онлайн-руководства, и сами они просты в применении. (Попробуйте использовать их на флеш-накопителе, если у вас нет запасных дисков.)
Тем не менее существует большая разница в том, как работают fdisk и parted. С помощью fdisk вы создаете новую таблицу разделов перед внесением фактических
изменений на диск, и она реализует изменения только при выходе из программы.
Но при использовании parted разделы создаются, изменяются и удаляются по мере
выполнения команд. У вас нет возможности просмотреть таблицу разделов перед
ее изменением. Эти различия — ключ к пониманию того, как эти две утилиты взаимодействуют с ядром. Утилита fdisk, как и parted, полностью изменяет разделы
в пользовательском пространстве — нет необходимости предоставлять поддержку
ядра для перезаписи таблицы разделов, поскольку пользовательское пространство
может считывать и изменять все блочные устройства.
Однако в какой-то момент ядро должно прочитать таблицу разделов, чтобы представить разделы как блочные устройства, которые можно использовать. Утилита fdisk
делает это довольно простым способом. После изменения таблицы разделов fdisk выдает один системный вызов, чтобы сообщить ядру, что оно должно перечитать таблицу
разделов диска (далее будет приведен пример взаимодействия ядра с fdisk). Затем
ядро генерирует вывод отладки, который можно просмотреть с помощью коман­ды
journalctl -k. Например, создав два раздела в /dev/sdf, вы увидите следующее:
sdf: sdf1 sdf2

4.1. Разбиение дисков на разделы   109
Инструменты parted не используют один системный вызов для всего диска, вместо этого они сигнализируют ядру, когда изменяются отдельные разделы. После
обработки одного изменения раздела ядро не выдает предыдущий вывод отладки.
Перечислим несколько способов увидеть изменения раздела.
Используйте udevadm для просмотра изменений событий ядра. Например,
команда udevadm monitor --kernel покажет, как удаляются старые устройства
разделов и добавляются новые.
Проверьте файл /proc/partitions для получения полной информации о разделах.
Проверьте файл /sys/block/device/ на наличие измененных системных
­ нтерфейсов разделов или /dev на наличие измененных устройств рази
делов.
ПРИНУДИТЕЛЬНАЯ ПЕРЕЗАГРУЗКА ТАБЛИЦЫ РАЗДЕЛОВ
Если вам необходимо удостовериться в правильности вносимых изменений
в таблицу разделов, можете использовать команду blockdev для выполнения
системного вызова в старомодном стиле, который выдает fdisk. Например,
чтобы заставить ядро перезагрузить таблицу разделов в /dev/sdf, выполните
следующую команду:
# blockdev --rereadpt /dev/sdf

4.1.3. Создание таблицы разделов
Давайте применим только что полученные знания и создадим новую таблицу
разделов на новом пустом диске. В следующем примере мы исходим из таких
условий:
диск размером 4 Гбайт (небольшое неиспользуемое USB-флеш-устройство —
возьмите устройство любого размера, которое есть под рукой);
таблица разделов MBR;
два раздела, предназначенные для файловой системы ext4: 200 Мбайт
и 3,8 Гбайт;
дисковое устройство в /dev/sdd (вам нужно будет найти местоположение
устройства с помощью команды lsblk).
Для выполнения этой задачи используйте утилиту fdisk. Напомним, что это интерактивная команда, поэтому, убедившись, что к диску ничего не примонтировано,
начните с команды
# fdisk /dev/sdd

110   Глава 4. Диски и файловые системы
Появится вводное сообщение, а затем командная строка:
Command (m for help):

В первую очередь выведите текущую таблицу с помощью команды p (команды
утилиты fdisk довольно короткие). Вот как это будет выглядеть:
Command (m for help): p
Disk /dev/sdd: 4 GiB, 4284481536 bytes, 8368128 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x88f290cc
Device
Boot Start
End Sectors Size Id Type
/dev/sdd1
2048 8368127 8366080
4G c W95 FAT32 (LBA)

Большинство устройств уже содержат один раздел в стиле FAT, как в примере
в /dev/sdd1. Поскольку вы хотите создать новые разделы для системы Linux, то
можете удалить существующие разделы (конечно, если уверены, что важных данных в них нет), например:
Command (m for help): d
Selected partition 1
Partition 1 has been deleted.

Помните, что утилита fdisk не вносит изменений, пока вы не сохраните таблицу
разделов, поэтому диск остается неизменным до последнего. Если вы допустили
ошибку, которую не можете исправить, с помощью команды q выйдите из редактора
fdisk, не сохраняя изменений. Теперь создадим первый раздел размером 200 Мбайт
с помощью команды n:
Command (m for help): n
Partition type
P
primary (0 primary, 0 extended, 4 free)
E
extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-8368127, default 2048): 2048
Last sector, +sectors or +size{K,M,G,T,P} (2048-8368127, default 8368127): +200M
Created a new partition 1 of type 'Linux' and of size 200 MiB.

В примере fdisk предлагает раздел MBR, номер раздела, начало раздела и его конец
(или размер). Значения по умолчанию довольно часто отображают то, что нужно.
Единственное, что здесь изменилось, — это конец/размер раздела с синтаксисом +
для указания размера и единицы измерения.
Создание второго раздела происходит почти так же, за исключением того, что вы будете использовать все значения по умолчанию, поэтому мы не будем рассматривать

4.1. Разбиение дисков на разделы   111
этот пример. Закончив разделение, введите команду p (print, вывод) для просмотра
содержимого:
Command (m for help): p
[--пропуск--]
Device
Boot Start
End Sectors Size Id Type
/dev/sdd1
2048 411647 409600 200M 83 Linux
/dev/sdd2
411648 8368127 7956480 3.8G 83 Linux

Когда будете готовы сохранить таблицу разделов, используйте команду w (write,
запись):
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

Обратите внимание, что утилита fdisk не спрашивает вас, уверены ли вы в безопасности своих действий, — она просто выполняет свою работу.
Если вас интересует дополнительная диагностика, задействуйте команду jour­
nalctl -k, чтобы просмотреть сообщения о чтении ядром, упомянутые ранее, но
помните, что они появятся только при использовании утилиты fdisk.
На данный момент мы рассмотрели все основы, и можно приступать к разбиению
дисков на разделы, однако если вы хотите больше узнать о дисках, продолжайте
читать следующий раздел. В противном случае переходите к разделу 4.2, где рассказывается о размещении файловой системы на диске.

4.1.4. Геометрия дисков и разделов
Любое устройство с движущимися частями вносит сложность в систему, поскольку
существуют физические элементы, которые не поддаются абстрагированию. Жесткий диск — не исключение: даже если представлять его как блочное устройство
с произвольным доступом к любому блоку, если система не будет внимательна
к тому, как она размещает данные на диске, это может иметь серьезные последствия
для производительности. Рассмотрим физические свойства простого диска с одной
пластиной (рис. 4.3).
Диск состоит из вращающейся пластины на шпинделе с головкой, прикрепленной
к движущемуся коромыслу, которое может перемещаться по радиусу диска. Когда
диск вращается под головкой, последняя считывает данные. Когда коромысло
находится в одном положении, головка может считывать данные только с фиксированного круга, называемого цилиндром. Большие диски имеют более одного
«блина» (жесткого магнитного диска), которые вращаются вокруг одного и того
же шпинделя. Каждый «блин» может иметь одну или две головки в верхней и/
или нижней части, все головки прикреплены к одному шпинделю (коромыслу)
и движутся согласованно. Шпиндель смещается, а на диске находится множество

112   Глава 4. Диски и файловые системы
цилиндров, от маленьких в его центре до больших на периферии. Наконец, вы
можете разделить цилиндр на доли, называемые секторами. Такой подход к геометрии диска кратко называется CHS (cylinder-head-sector — цилиндр, головка,
сектор), в старых системах вы могли бы найти любую часть диска, используя
эти три параметра.

Рис. 4.3. Вид на жесткий диск сверху
ПРИМЕЧАНИЕ
Дорожка — это часть цилиндра, к которой обращается одна головка, поэтому на рис. 4.3
цилиндр также является дорожкой.
Ядро и различные программы разбиения на разделы могут показать, какое
количество цилиндров имеет диск. Однако на любом из современных жестких
дисков эти значения не правдивы! Традиционная схема передачи, применяемая
системой CHS, не масштабируется на современном дисковом оборудовании
и не учитывает тот факт, что появилась возможность поместить во внешние
цилиндры больше данных, чем во внутренние. Аппаратное обеспечение диска
поддерживает логическую блочную адресацию (Logical Block Addressing, LBA) для
передачи местоположения на диске по номеру блока (это гораздо более простой
интерфейс), но остатки CHS еще видны. Например, таблица разделов MBR содержит информацию о CHS, а также эквиваленты LBA, и некоторые загрузчики
все еще доверяют значениям CHS (не волнуйтесь, большинство загрузчиков
Linux используют значения LBA).
ПРИМЕЧАНИЕ
Слово «сектор» может сбивать с толку, потому что программы разделения Linux также
применяют его, но для обозначения другого инструмента.

4.1. Разбиение дисков на разделы   113

ВАЖНЫ ЛИ ГРАНИЦЫ ЦИЛИНДРА?
Сущность цилиндров когда-то имела решающее значение для работы с разделами, потому что цилиндры сами по себе — это идеальные границы для
разделов. Считывание потока данных с цилиндра происходит очень быстро,
потому что головка может непрерывно собирать данные по мере вращения
диска. Раздел, выполненный в виде набора соседних цилиндров, также
обес­печивает быстрый непрерывный доступ к данным, поскольку головке
не нужно далеко перемещаться между цилиндрами.
Хотя внешне диски практически не изменились, понятие точного разбиения
на разделы устарело. Некоторые старые программы разбиения уведомляют,
если вы не размещаете свои разделы точно на границах цилиндра. Игнорируйте это предупреждение — с этим мало что можно сделать, потому что сообщаемые CHS параметры современных дисков просто не соответствуют действительности. Схема LBA наряду с улучшенной логикой в новых утилитах
разбиения гарантирует, что ваши разделы будут расположены эффективно.

4.1.5. Чтение твердотельных дисков
Устройства хранения данных без движущихся частей, такие как твердотельные
накопители (SSD), радикально отличаются от вращающихся дисков с точки зрения
характеристик доступа. Для них произвольный доступ к разделам — не проблема,
потому что нет головки, которая двигалась бы по «блину», но некоторые характеристики все же меняют работу SSD.
Один из наиболее существенных факторов, влияющих на производительность
SSD, — это выравнивание разделов. Считывая данные с SSD, вы читаете их частями
(они называются страницами, не путайте со страницами виртуальной памяти),
например, 4096 байт или 8192 байта за раз, и чтение должно начинаться с части,
кратной этому значению. Это означает, что если ваш раздел и его данные не лежат
на границе, вам придется выполнять два считывания вместо одного для небольших
распространенных операций, например чтения содержимого каталога.
Новые версии утилит разбиения включают логику, позволяющую помещать вновь
созданные разделы под соответствующие порядковые номера от начала дисков,
поэтому вам не нужно беспокоиться о выравнивании разделов. Инструменты разбиения в настоящее время не производят никаких вычислений — они просто выравнивают разделы по границам 1 Мбайт, или, точнее, 2 048 512-байтовых блоков. Это
довольно консервативно, потому что граница совпадает с размерами страниц 4096,
8192 и т. д. вплоть до 1 048 576. Однако, если вам интересно или вы хотите убедиться,
что разделы начинаются на границе, вы легко найдете эту информацию в каталоге
/sys/block. Вот пример кода для раздела /dev/sdf2:
$ cat /sys/block/sdf/sdf2/start
1953126

114   Глава 4. Диски и файловые системы
Вывод в примере — это смещение раздела от начала устройства в единицах 512 байт
(система Linux также называет их секторами). Если этот твердотельный накопитель
использует 4096-байтовые страницы, на них находятся восемь таких секторов. Вам
нужно лишь посмотреть, сможете ли вы равномерно разделить порядковые номера
раздела на 8. В приведенном примере этого сделать нельзя, а значит, раздел не достигнет оптимальной производительности.

4.2. Файловые системы
Последним связующим звеном между ядром и пользовательским пространством
для дисков обычно является файловая система — то, с чем вы взаимодействуете
при выполнении таких команд, как ls и cd. Как упоминалось ранее, файловая система — это форма базы данных, она обеспечивает структуру для преобразования
простого блочного устройства в сложную иерархию файлов и подкаталогов, понятную пользователям.
В свое время все файловые системы располагались на дисках и других физических носителях, предназначенных исключительно для хранения данных. Однако
древовидная структура каталогов и интерфейс ввода-вывода файловых систем
довольно универсальны, поэтому файловые системы теперь выполняют множество задач, к примеру, роль системных интерфейсов, которые отображаются
в каталогах в /sys и /proc. Файловые системы традиционно реализуются в ядре,
но инновационный протокол 9P из системы Plan9 (en.wikipedia.org/wiki/9P_(protocol)) вдохновил разработчиков на создание файловых систем пользовательского
пространства. Функция файловой системы в пользовательском пространстве (File
System in User Space, FUSE) позволяет создавать файловые системы в пользовательском пространстве в Linux.
Уровень абстракции виртуальной файловой системы (Virtual File System, VFS) завершает реализацию файловой системы. Подобно тому как подсистема SCSI стандартизирует связь между различными типами устройств и командами управления
ядром, VFS гарантирует, что все реализации файловой системы поддерживают
стандартный интерфейс, чтобы приложения пользовательского пространства могли
одинаково обращаться к файлам и каталогам. Поддержка VFS позволила Linux
поддерживать чрезвычайно большое количество файловых систем.

4.2.1. Типы файловых систем
Поддержка файловой системы Linux включает в себя собственные проекты, оптимизированные для Linux, а также внешние типы файловых систем, например
Windows FAT, универсальные файловые системы, такие как ISO 9660, и многие
другие. Далее перечислены наиболее распространенные типы файловых систем
для хранения данных. Имена типов, распознанные Linux, заключены в круглые
скобки рядом с именами файловых систем.

4.2. Файловые системы   115

ЭВОЛЮЦИЯ ФАЙЛОВОЙ СИСТЕМЫ LINUX
Большинство пользователей давно применяют серию расширенных файловых систем (Extended filesystem), и тот факт, что она так долго оставалась
стандартом, свидетельствует не только о ее эффективности, но и об адаптивности. Сообщество разработчиков Linux имеет тенденцию полностью
заменять компоненты, которые не отвечают сиюминутным потребностям,
но каждый раз, когда расширенной файловой системе чего-то не хватает,
кто-то ее обновляет. Тем не менее в технологии файловых систем есть много
того, что даже ext4 не может использовать из-за требований обратной совместимости. Это связано в первую очередь с улучшением масштабируемости, относящимся к очень большому количеству файлов, большим файлам
и аналогичным сценариям.
На момент написания этого текста система Btrfs является стандартом по
умолчанию для одного основного дистрибутива Linux. Если этот выбор окажется успешным, вполне вероятно, что Btrfs заменит серию расширенных
файловых систем.

Четвертая расширенная файловая система (Fourth Extended filesystem,
ext4) — это текущая итерация линейки файловых систем, родных для Linux.
Вторая расширенная файловая система (ext2) долго использовалась по
умолчанию для систем Linux, вдохновленных традиционными файловыми
системами Unix, такими как файловая система Unix (Unix File System,UFS)
и быстрая файловая система (Fast File System, FFS). Третья расширенная
файловая система (ext3) добавила функцию журналирования (небольшой
кэш вне обычной структуры данных файловой системы) для повышения
целостности данных и ускорения загрузки. Файловая система ext4 является
дальнейшим улучшением и поддерживает файлы большего размера, чем ext2
или ext3, а также большее количество подкаталогов.
В расширенной серии файловых систем существует определенная обратная
совместимость. Например, вы можете монтировать файловые системы ext2
и ext3 взаимозаменяемо и монтировать файловые системы ext2 и ext3 как
ext4, но не можете монтировать ext4 как ext2 или ext3.
Файловая система Btrfs (B-tree filesystem) — это новейшая файловая система, родная для Linux и расширяющая возможности файловой системы ext4.
Файловые системы FAT (msdos, vfat, exfat) относятся к системам Microsoft.
Простой тип msdos поддерживает очень примитивное однообразное множество
систем MS-DOS. Большинство съемных флеш-носителей, таких как SD-карты
и USB-накопители, по умолчанию содержат разделы vfat (до 4 Гбайт) или
exfat (4 Гбайт и более). Системы Windows могут использовать либо файловую
систему на основе FAT, либо более продвинутую файловую систему NT (ntfs).

116   Глава 4. Диски и файловые системы
XFS — это высокопроизводительная файловая система, применяемая по
умолчанию некоторыми дистрибутивами, такими как Red Hat Enterprise
Linux 7.0 и более высоких версий.
HFS+ (hfsplus) — это стандарт файловой системы Apple, используемый
в большинстве систем Macintosh.
Файловая система ISO9660 (iso9660) — это стандарт CD-ROM. Большинство
CD-приводов применяют разновидности стандарта ISO 9660.

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

ЧТО ТАКОЕ MKFS
Это всего лишь внешний интерфейс для ряда программ создания файловых
систем, mkfs.fs, где fs — тип файловой системы. Поэтому, когда вы запускаете команду mkfs -t ext4, mkfs в свою очередь запускает mkfs.ext4. Но здесь
еще больше неочевидного. Проверьте файлы mkfs.*, стоящие после команд,
и вы увидите следующее:
$ ls -l /sbin/mkfs.*
-rwxr-xr-x 1 root root 17896 Mar 29 21:49 /sbin/mkfs.bfs
-rwxr-xr-x 1 root root 30280 Mar 29 21:49 /sbin/mkfs.cramfs
lrwxrwxrwx 1 root root
6 Mar 30 13:25 /sbin/mkfs.ext2 -> mke2fs
lrwxrwxrwx 1 root root
6 Mar 30 13:25 /sbin/mkfs.ext3 -> mke2fs
lrwxrwxrwx 1 root root
6 Mar 30 13:25 /sbin/mkfs.ext4 -> mke2fs
lrwxrwxrwx 1 root root
6 Mar 30 13:25 /sbin/mkfs.ext4dev -> mke2fs
-rwxr-xr-x 1 root root 26200 Mar 29 21:49 /sbin/mkfs.minix
lrwxrwxrwx 1 root root
7 Dec 19 2011 /sbin/mkfs.msdos -> mkdosfs
lrwxrwxrwx 1 root root
6 Mar 5 2012 /sbin/mkfs.ntfs -> mkntfs
lrwxrwxrwx 1 root root
7 Dec 19 2011 /sbin/mkfs.vfat -> mkdosfs

Как видно из примера, mkfs.ext4 — это просто символическая ссылка на
mke2fs. Это важно помнить, если вы столкнулись с системой без определенной команды mkfs или просматриваете документацию конкретной файловой
системы. Утилиты создания каждой файловой системы имеют собственную
страницу руководства, например mke2fs(8). В большинстве систем это не
проблема, потому что доступ к странице руководства mkfs.ext4(8) должен
перенаправить вас на страницу руководства mke2fs(8), просто имейте это
в виду.

4.2. Файловые системы   117
Утилита mkfs может создавать множество типов файловых систем. Например, вы
можете создать раздел ext4 в /dev/sda2 с помощью команды
# mkfs -t ext4 /dev/sdf2

Программа mkfs автоматически определяет количество блоков в устройстве и устанавливает подходящие значения по умолчанию. Если вы не представляете, что
делаете, и не хотите подробно ознакомиться с документацией, не меняйте их.
При создании файловой системы mkfs по ходу работы осуществляет диагностический вывод, включая относящийся к суперблоку. Суперблок — ключевой компонент
верхнего уровня базы данных файловой системы, и он настолько важен, что mkfs
создает несколько резервных копий на случай проблем с оригиналом. Запишите
несколько номеров резервных копий суперблоков при запуске mkfs на случай, если
вам потребуется восстановить суперблок после сбоя диска (см. подраздел 4.2.11).
ВНИМАНИЕ
Создание файловой системы — это задача, которую следует выполнять только после добавления нового диска или повторного разбиения старого. Вы должны создать файловую
систему только один раз для каждого нового раздела, в котором нет ранее существовавших данных (или данные есть, но их можно удалить). Создание новой файловой системы
поверх существующей уничтожает старые данные.

4.2.3. Монтирование файловой системы
В системах Unix процесс присоединения файловой системы к работающей системе
называется монтированием. Когда система загружается, ядро считывает некоторые
данные конфигурации и на их основе монтирует корневой каталог (/).
Чтобы примонтировать файловую систему, необходимо знать:
устройство, местоположение или идентификатор файловой системы (например, раздел диска, в котором хранятся ее фактические данные). Некоторые
файловые системы специального назначения, такие как proc и sysfs, не имеют
местоположения;
тип файловой системы;
точку монтирования (mountpoint) — место в иерархии каталогов текущей
системы, к которому будет присоединена файловая система. Точка монтирования всегда является обычным каталогом. Например, вы можете использовать каталог /music в качестве точки монтирования для файловой системы,
содержащей музыку. Точка монтирования не обязательно должна находиться
непосредственно под корневым каталогом / — она может располагаться
в любом месте системы.
Общепринято, что для монтирования файловой системы необходимо смонтировать
устройство в точке монтирования. Чтобы выяснить текущее состояние файловой

118   Глава 4. Диски и файловые системы
системы, запустите команду mount. Вывод (который может быть довольно длинным)
должен выглядеть следующим образом:
$ mount
/dev/sda1 on / type ext4 (rw,errors=remount-ro)
proc on /proc type proc (rw,noexec,nosuid,nodev)
sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
fusectl on /sys/fs/fuse/connections type fusectl (rw)
debugfs on /sys/kernel/debug type debugfs (rw)
securityfs on /sys/kernel/security type securityfs (rw)
udev on /dev type devtmpfs (rw,mode=0755)
devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620)
tmpfs on /run type tmpfs (rw,noexec,nosuid,size=10%,mode=0755)
--пропуск--

Каждая строка соответствует одной смонтированной в данный момент файловой
системе, элементы которой расположены в таком порядке.
1. Устройство, например /dev/sda3. Обратите внимание на то, что некоторые из
устройств не реальные (например, proc), но зарезервированы для реальных
имен устройств, поскольку эти файловые системы специального назначения
не нуждаются в устройствах.
2. Слово on.
3. Точка монтирования.
4. Слово type.
5. Тип файловой системы, обычно в виде короткого идентификатора.
6. Параметры монтирования (в скобках). Более подробную информацию см.
в подразделе 4.2.6.
Чтобы смонтировать файловую систему вручную, используйте команду mount
следующим образом с указанием типа файловой системы, устройства и желаемой
точки монтирования:
# mount -t type device mountpoint

Например, чтобы смонтировать четвертую расширенную файловую систему, найденную на устройстве /dev/sdf2 в /home/extra, применяйте команду
# mount -t ext4 /dev/sdf2 /home/extra

Обычно не нужно указывать параметр типа -t, потому что команда mount применяет
его сама. Однако иногда необходимо различать два похожих типа систем, например
различные файловые системы в стиле FAT.
Чтобы демонтировать (отсоединить) файловую систему, используйте команду
umount следующим образом:
# umount mountpoint

4.2. Файловые системы   119
Вы также можете демонтировать файловую систему через ее устройство вместо
точки монтирования.
ПРИМЕЧАНИЕ
Почти все системы Linux включают временную точку монтирования /mnt, которая обычно
используется для тестирования. Задействуйте ее при экспериментах с системой, но если
собираетесь монтировать файловую систему для расширенного применения, найдите
или создайте для нее другое местоположение.

4.2.4. Идентификатор UUID файловой системы
Способ монтирования файловых систем, рассмотренный в предыдущем разделе,
зависит от имен устройств. Однако имена устройств могут изменяться, поскольку
они зависят от порядка, в котором ядро находит устройства. Чтобы решить эту
проблему, можете идентифицировать и монтировать файловые системы по их универсально-уникальному идентификатору UUID (Universally Unique Identifier) — стандарту уникальных «серийных номеров» идентификации объектов в компьютерной
системе. Программы создания файловой системы, такие как mke2fs, генерируют
UUID при инициализации структуры данных файловой системы.
Для просмотра списка устройств и соответствующих файловых систем и UUID
в своей системе используйте программу blkid (идентификатор блока, Block Id):
# blkid
/dev/sdf2:UUID="b600fe63-d2e9-461c-a5cd-d3b373a5e1d2" TYPE="ext4"
/dev/sda1: UUID="17f12d53-c3d7-4ab3-943e-a0a72366c9fa" TYPE="ext4"
PARTUUID="c9a5ebb0-01"
/dev/sda5: UUID="b600fe63-d2e9-461c-a5cd-d3b373a5e1d2" TYPE="swap"
PARTUUID="c9a5ebb0-05"
/dev/sde1: UUID="4859-EFEA" TYPE="vfat"

В этом примере программа blkid обнаружила четыре раздела с данными: два с файловыми системами ext4, один с сигнатурой пространства подкачки (см. раздел 4.3)
и один с файловой системой на основе FAT. Все собственные разделы Linux имеют
стандартные UUID, лишь у раздела FAT его нет. Вы можете ссылаться на раздел
FAT через его серийный номер тома FAT (в данном случае 4859-EFEA).
Чтобы смонтировать файловую систему по ее идентификатору UUID, используйте
параметр монтирования UUID. Например, чтобы смонтировать первую файловую
систему из предыдущего списка в /home/extra, введите:
# mount UUID=b600fe63-d2e9-461c-a5cd-d3b373a5e1d2 /home/extra

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

120   Глава 4. Диски и файловые системы
монтирования файловых систем, отличных от LVM, в /etc/fstab во время загрузки
(см. подраздел 4.2.8). Кроме того, многие дистрибутивы используют UUID в качестве точки монтирования при вставке съемных носителей. В предыдущем примере
файловая система FAT находится на флеш-карте. Система Ubuntu смонтирует этот
раздел в /media/user/4859-EFEA после вставки носителя. Демон udevd, описанный
в главе 3, обрабатывает начальное событие для вставки устройства.
При необходимости вы можете изменить UUID файловой системы (например,
если скопировали полную файловую систему откуда-то еще и теперь вам нужно
отличить ее от оригинала). См. страницу руководства tune2fs(8), чтобы узнать,
как это сделать в файловой системе ext2/ext3/ext4.

4.2.5. Буферизация диска, кэширование и файловые системы
Система Linux, как и другие варианты Unix, буферизует запись на диск. Это означает, что ядро обычно не сразу записывает изменения в файловые системы, когда
процессы запрашивают изменения. Вместо этого она сохраняет эти изменения
в оперативной памяти до тех пор, пока ядро не определит подходящее время для их
фактической записи на диск. Эта система буферизации невидима для пользователя
и обеспечивает значительный прирост производительности.
Когда вы размонтируете файловую систему с помощью команды umount, ядро автоматически синхронизируется с диском, записывая изменения из своего буфера
на диск. Вы можете заставить ядро сделать это в любое время, выполнив команду
sync, которая по умолчанию синхронизирует все диски в системе. Если по какойто причине вы не можете размонтировать файловую систему перед выключением
системы, обязательно сначала запустите команду sync.
Кроме того, ядро использует оперативную память для кэширования блоков по
мере их чтения с диска. Поэтому, если один или несколько процессов повторно
обращаются к файлу, ядру не нужно снова и снова обращаться к диску, оно может
просто считывать данные из кэша и экономить время и ресурсы.

4.2.6. Параметры монтирования файловой системы
Существует множество способов изменить поведение команды монтирования, что
часто требуется в ходе работы со съемными носителями или при обслуживании
системы. На самом деле общее количество вариантов монтирования ошеломляет.
У команды mount(8) есть обширная страница руководства, но даже прочитав ее,
трудно понять, с чего начать и что можно без опаски игнорировать. Рассмотрим
наиболее полезные параметры в этом разделе.
Параметры делятся на две основные категории: общие и подходящие для конкретной файловой системы. Общие параметры обычно работают во всех типах файловых
систем и включают параметр -t для указания типа файловой системы, как было

4.2. Файловые системы   121
показано ранее. Напротив, параметр, специфичный для файловой системы, относится только к определенным типам файловых систем.
Чтобы активировать параметр для файловой системы, используйте переключатель
-o, за которым следует параметр. Например, команда -o remount,rw перемонтирует
файловую систему, уже смонтированную как доступную только для чтения, в режим
чтения и записи.

Основные короткие параметры
Общие параметры имеют короткий синтаксис. Наиболее важными из них являются
следующие.
-r — монтирует файловую систему в режиме только для чтения. Это полезно

для ряда задач, от защиты от записи до начальной загрузки. Вам не нужно
указывать его при доступе к устройству, которое уже доступно только для
чтения, например к CD-ROM, система сделает это за вас (а также сообщит
о состоянии только для чтения).
-n — гарантирует, что команда mount не попытается обновить исполняемую
системную базу данных монтирования /etc/mtab. По умолчанию операция
mount завершается неудачно, если не может выполнить запись в этот файл, по-

этому данный параметр важен во время загрузки, поскольку корневой раздел
(включая системную базу данных монтирования) сначала доступен только
для чтения. Этот параметр задействуется также для устранения системной
проблемы в однопользовательском режиме, поскольку системная база данных
монтирования может быть недоступна в текущий момент.
-t. Параметр -t type указывает на тип файловой системы.

Длинные параметры
Рамки коротких вариантов параметров, таких как -r, слишком узки для постоянно
растущего числа вариантов монтирования — в алфавите недостаточно букв, чтобы
охватить все возможные варианты. К тому же короткие варианты вызывают проблемы, потому что трудно определить значение варианта на основе одной буквы.
Многие общие параметры и все параметры, относящиеся к файловой системе, используют более длинный и гибкий формат.
Чтобы задействовать длинные параметры с командой mount в командной строке,
начните с параметра -o, после которого стоят соответствующие ключевые слова,
разделенные запятыми. Вот полный пример с длинными параметрами, следующими
за параметром -o:
# mount -t vfat /dev/sde1 /dos -o ro,uid=1000

Два длинных параметра в примере — это ro и uid=1000. Параметр ro указывает режим только для чтения и совпадает с коротким параметром -r. Параметр uid=1000

122   Глава 4. Диски и файловые системы
приказывает ядру обрабатывать все файлы в файловой системе так, как если бы
владельцем был пользователь ID 1000.
Наиболее полезные длинные параметры:
exec, noexec — включает или отключает выполнение программ в файловой

системе;

suid, nosuid — включает или отключает программы setuid;
ro — монтирует файловую систему в режиме только для чтения (read-only
mode) (как и короткий параметр -r);
rw — монтирует файловую систему в режиме чтения и записи (read-write

mode).

ПРИМЕЧАНИЕ
Разница между текстовыми файлами Unix и DOS состоит главным образом в том, как
заканчиваются строки. В Unix только символ новой строки (\n, ASCII 0x0A) отмечает
конец строки, а DOS использует возврат каретки (\r, ASCII 0x0D), за которым следует
символ новой строки. Было много попыток автоматического преобразования на уровне
файловой системы, но они всегда вызывают проблемы. Текстовые редакторы, такие
как vim, могут автоматически определять стиль новой строки файла и поддерживать
его соответствующим образом. Таким образом легче сохранить единообразие стилей.

4.2.7. Повторное монтирование файловой системы
Бывают случаи, когда нужно изменить параметры монтирования для уже смонтированной файловой системы. Наиболее распространенная ситуация — вам нужно
сделать файловую систему, доступную только для чтения, доступной для записи во
время аварийного восстановления. В этом случае требуется повторно подключить
файловую систему в той же точке монтирования.
Следующая команда заново монтирует корневой каталог в режиме чтения и запи­
си (вам нужен параметр -n, потому что команда mount не может записывать в системную базу данных монтирования, когда корневой каталог доступен только для
чтения):
# mount -n -o remount /

Эта команда предполагает, что правильный список устройств для / находится в каталоге /etc/fstab (как описано в следующем разделе). Если это не так, вы должны
указать устройство в качестве дополнительного параметра.

4.2.8. Таблица файловой системы /etc/fstab
Чтобы смонтировать файловые системы во время загрузки и избавиться от необходимости использовать команду mount, системы Linux хранят постоянный список

4.2. Файловые системы   123
файловых систем и параметров в файле /etc/fstab. Это текстовый файл в очень
простом формате, как показано в листинге 4.1.
Листинг 4.1. Список файловых систем и параметров в файле /etc/fstab
UUID=70ccd6e7-6ae6-44f6-812c-51aab8036d29 / ext4 errors=remount-ro 0 1
UUID=592dcfd1-58da-4769-9ea8-5f412a896980 none swap sw 0 0
/dev/sr0 /cdrom iso9660 ro,user,nosuid,noauto 0 0

Каждая строка соответствует одной файловой системе и разбита на шесть полей.
Значения этих полей (слева направо):
Устройство или идентификатор UUID. Большинство современных систем
Linux больше не используют устройство в файле /etc/fstab, предпочитая
UUID.
Точка монтирования (mount point). Указывает, куда следует присоединить
файловую систему.
Тип файловой системы. Вам может быть незнаком параметр swap в этом
списке, это раздел подкачки (см. раздел 4.3).
Параметры. Длинные параметры, разделенные запятыми.
Информация о резервном копировании для команды dump. Команда
dump — давно устаревшая утилита резервного копирования, это поле больше
не актуально. Вы всегда должны устанавливать его равным 0.
Порядок проверки целостности файловой системы. Чтобы убедиться,
что программа fsck всегда запускается сначала в корневом каталоге, устанавливайте значение 1 для корневой файловой системы и 2 — для любых
других локально подключенных файловых систем на жестком диске или
SSD. Задавайте значение 0, чтобы отключить проверку загрузки для всех
других файловых систем, включая устройства только для чтения, подкачку
swap и файловую систему /proc (см. информацию о команде fsck в подразделе 4.2.11).
При использовании команды mount вы можете добавлять ярлыки, если файловая
система, с которой хотите работать, находится в файле /etc/fstab. Например, ориентируясь на листинг 4.1, смонтируйте CD, просто запустив команду mount /cd rom.
Можете попытаться одновременно смонтировать все записи в файле /etc/fstab,
которые не содержат параметр noauto, с помощью команды
# mount -a

В листинге 4.1 представлены некоторые новые параметры, а именно errors, noauto
и user, поскольку они не применяются за пределами файла /etc/fstab. Кроме того,
здесь чаще всего стоит параметр defaults. Вот что означают эти параметры:
defaults . Устанавливает параметры mount по умолчанию: режим чтения
и запи­си, включение файлов устройств, исполняемых файлов, бит setuid и т. д.

124   Глава 4. Диски и файловые системы
Используйте его, если не хотите предоставлять файловой системе какие-то
специальные параметры, но нужно заполнить все поля в файле /etc/fstab;
errors. Параметр, относящийся к файловым системам ext2/ext3/ext4. Он за-

дает поведение ядра, когда в системе возникают проблемы с монтированием
файловой системы. Значение по умолчанию обычно равно errors=continue, что
означает: ядро должно возвращать код ошибки и продолжать работать. Чтобы
ядро снова попыталось выполнить монтирование в режиме только для чтения,
используйте параметр errors=remount-ro. Параметр errors=panic указывает
ядру (и системе) остановиться, когда возникает проблема с монтированием;
noauto. Указывает команде mount -a игнорировать запись. Применяйте его

для предотвращения монтирования во время загрузки устройства со съемным
носителем, например устройства флеш-памяти;

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

определенных видов доступа к съемным носителям. Поскольку пользователи
могут поместить файл setuid-root на съемный носитель с другой системой,
этот параметр также устанавливает значения для nosuid, noexec и nodev (для
запрета специальных файлов устройств). Имейте в виду, что для съемных
носителей и других общих случаев этот параметр теперь используется ограниченно, поскольку большинство систем применяют ubus наряду с другими
механизмами для автоматического монтирования вставленных носителей.
Однако этот параметр может быть полезен в особых случаях, когда вы хотите
предоставить контроль над монтированием определенных каталогов.

4.2.9. Альтернативы файлу /etc/fstab
Хотя файл /etc/fstab был традиционным способом представления файловых
систем и их точек монтирования, существуют две альтернативы. Первая — это каталог /etc/fstab.d, который содержит отдельные файлы конфигурации файловой
системы (по одному для каждой файловой системы). По сути он очень похож на
многие другие каталоги конфигурации, которые встречаются в этой книге.
Второй альтернативой является настройка модулей systemd units для файловых
систем. Вы узнаете больше о системе systemd и ее блоках в главе 6. Однако конфигурация блока systemd часто генерируется из файла /etc/fstab (или на его основе),
поэтому можно обнаружить совпадения в системах.

4.2.10. Емкость файловой системы
Чтобы просмотреть размер и способы применения смонтированных в данный момент
файловых систем, используйте команду df. Вывод может быть очень обширным (и он
становится все длиннее благодаря специализированным файловым системам), но он
должен включать информацию о ваших реальных устройствах хранения:

4.2. Файловые системы   125
$ df
Filesystem
/dev/sda1
/dev/sdd2

1K-blocks
Used Available Use% Mounted on
214234312 127989560 75339204 63% /
3043836
4632
2864872
1% /media/user/uuid

Краткое описание полей в выводе команды df:
Filesystem (файловая система). Устройство, на котором расположена фай-

ловая система.

1K-blocks (1К-блоки). Общая емкость файловой системы в блоках по

1024 байта.

Used (занято). Количество занятых блоков.
Available (свободно). Количество свободных блоков.
Use% (используется). Процент используемых блоков.
Mounted on (смонтировано). Точка монтирования.

ПРИМЕЧАНИЕ
Если у вас возникли проблемы с поиском правильной строки в выводе df, соответствующей
определенному каталогу, выполните команду df dir, где dir — это каталог, который вы
хотите найти. Команда ограничит вывод файловой системы для этого каталога. Очень
часто применяется параметр df, который ограничивает вывод устройством, содержащим
ваш текущий каталог.
Из примера видно, что две файловые системы имеют размер примерно 215 Гбайт
и 3 Гбайт. Однако значения емкости могут показаться немного странными, потому что 127 989 560 плюс 75 339 204 не равно 214 234 312, а 127 989 560 — это
не 63 % от 214 234 312. В обоих случаях не учтены 5 % от общей емкости. На
самом деле пространство есть, но оно скрыто в зарезервированных (reserved) блоках. Только суперпользователь может задействовать зарезервированные блоки
файловой ­системы, когда она начинает заполняться. Эта функция предохраняет
системные серверы от немедленного сбоя, когда у них заканчивается дисковое
пространство.
ПРИМЕЧАНИЕ
Стандарт POSIX (Portable Operating System Interface for Unix, переносимый интерфейс
операционных систем Unix) определяет размер блока в 512 байт. Однако этот размер
труднее читать, поэтому по умолчанию вывод df и du в большинстве дистрибутивов Linux
выполняется в блоках размером 1024 байта. Если вы хотите отобразить числа в 512-байтовых блоках, установите переменную среды POSIXLY_CORRECT. Чтобы явно указать
блоки размером 1024 байта, примените параметр -k (обе команды его поддерживают).
Программы df и du также имеют параметр -m для перечисления емкости в блоках размером 1 Мбайт и параметр -h, который составляет вывод так, чтобы его было легче всего
читать пользователю, основываясь на общих размерах файловых систем.

126   Глава 4. Диски и файловые системы

ВЫВОД ВСЕХ ИСПОЛЬЗУЮЩИХСЯ КАТАЛОГОВ
Если ваш диск заполняется и вам нужно знать, где находятся все занимающие место медиафайлы, примените команду du. Без аргументов она выводит
использование диска для каждого каталога в иерархии каталогов, начиная
с текущего рабочего каталога. (Это может быть длинный список, для примера просто запустите команду cd/; du, чтобы понять, о чем речь. Нажмите
сочетание клавиш Ctrl+C, когда вам это наскучит.) Команда du -s включает
режим общего подсчета для вывода только итоговой суммы. Чтобы оценить
все файлы и подкаталоги в определенном каталоге, перейдите в него и запустите команду du -s *, но имейте в виду, что могут существовать каталоги
с точкой, которые эта команда не увидит.

4.2.11. Проверка и восстановление файловых систем
Оптимизация, которую предлагают файловые системы Unix, становится возможной
благодаря сложному механизму базы данных. Для бесперебойной работы файловых
систем ядро должно быть уверено в том, что смонтированная файловая система не
содержит ошибок, а оборудование надежно хранит данные. Если ошибки существуют, это может привести к потере данных и сбоям системы.
Помимо физических проблем, ошибки файловой системы обычно возникают из-за
того, что пользователь неправильно выключает систему (например, выдергивает
шнур питания). В таких случаях предыдущий кэш файловой системы в памяти
может не совпадать с данными на диске, а система в этот момент может находиться
в процессе изменения файловой системы. Хотя многие файловые системы поддерживают журналирование, но чтобы сделать их повреждения менее частыми, вы
всегда должны правильно завершать работу системы. Любую файловую систему
необходимо время от времени проверять на наличие ошибок и проблем.
Инструментом для проверки файловой системы является утилита fsck . Как
и в случае с программой mkfs, для каждого типа файловой системы, поддерживаемого Linux, существует своя версия fsck. Например, при запуске в расширенной
серии файловых систем (ext2/ext3/ext4) fsck распознает тип файловой системы
и запускает утилиту e2fsck. Поэтому, как правило, вам не нужно вводить команду
e2fsck, за исключением случаев, когда fsck не может определить тип файловой
системы или вы ищете страницу руководства e2fsck.
Информация, представленная в этом разделе, относится к серии расширенных
файловых систем и e2fsck.
Чтобы запустить команду fsck в интерактивном ручном режиме, укажите в качестве аргумента устройство или точку монтирования (как указано в файле /etc/
fstab), например:
# fsck /dev/sdb1

4.2. Файловые системы   127
ВНИМАНИЕ
Никогда не используйте fsck для смонтированной файловой системы, так как ядро может
изменить данные на диске при выполнении проверки, что приведет к несоответствиям во
время выполнения, которые в свою очередь могут вызвать сбой системы и повреждение
файлов. Существует лишь одно исключение: монтируя корневой раздел только для чтения
в однопользовательском режиме, можете задействовать на нем команду fsck.
В ручном режиме команда fsck выводит подробные отчеты о состоянии на своем
пути, которые должны выглядеть примерно так, если нет проблем:
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/sdb1: 11/1976 files (0.0% non-contiguous), 265/7891 blocks

Если команда fsck обнаруживает проблему в ручном режиме, она останавливается
и задает вопрос о ее устранении. Эти вопросы касаются внутренней структуры
файловой системы, такой как повторное подключение незакрепленных дескрипторов inode и очистка блоков (дескрипторы inodes являются строительными
блоками файловой системы, вы увидите, как они работают, в разделе 4.6). Если
fsck спрашивает вас о повторном подключении дескриптора inode, значит, она
обнаружила файл, у которого, по-видимому, нет имени. При повторном подключении такого файла fsck помещает файл в каталог lost+found (потеряно + найдено)
в файловой системе с номером в качестве имени файла. Если это произойдет, вам
нужно угадать имя на основе содержимого файла, а исходное имя файла, скорее
всего, было удалено.
В общем, бессмысленно сразу подключать процесс восстановления fsck, если вы
только что неправильно вышли из системы, потому что fsck может найти множество мелких ошибок, которые нужно исправить. К счастью, у команды e2fsck есть
параметр -p, который автоматически исправляет обычные проблемы без запроса
и прерывает работу, когда возникает серьезная ошибка. Фактически дистрибутивы
Linux запускают вариант fsck -p во время загрузки. (Вы также можете встретить
команду fsck -a, которая делает то же самое.)
Если вы подозреваете, что в системе возникла серьезная ошибка, например произошел сбой оборудования или неправильно сконфигурировано устройство, вам
необходимо решить, как действовать, потому что команда fsck действительно может
повредить файловую систему, у которой и так уже большие проблемы. (Одним из
явных признаков проблем у системы является то, что fsck задает много вопросов
в ручном режиме.)
Если вы думаете, что проблема действительно есть, попробуйте запустить команду
fsck -n, чтобы проверить файловую систему, ничего в ней не изменяя. Если возникла

128   Глава 4. Диски и файловые системы
проблема с конфигурацией устройства, которую, по вашему мнению, можно исправить (например, незакрепленные кабели или неправильное количество блоков
в таблице разделов), сделайте это перед основным запуском команды fsck, иначе
вы, скорее всего, потеряете много данных.
Если вы подозреваете, что поврежден только суперблок (например, потому что ктото добавил запись в начало раздела диска), можете восстановить файловую систему
с помощью одной из резервных копий суперблока, созданных mkfs. Используйте
команду fsck -b num для замены поврежденного суперблока на альтернативный
блок num и надейтесь на лучшее.
Если вы не знаете, где найти резервный суперблок, запустите команду mkfs -n на
устройстве, чтобы просмотреть список номеров резервных копий суперблоков,
не повреждая свои данные. (Опять же убедитесь, что вы применяете параметр -n,
иначе действительно уничтожите файловую систему.)

Проверка файловых систем ext3 и ext4
Обычно вам не нужно проверять файловые системы ext3 и ext4 вручную, поскольку
журнал обеспечивает целостность данных (напомню, что журнал (journal) представляет собой небольшой кэш данных, который еще не был записан в определенную область файловой системы). Если вы не выключили свою систему аккуратно,
журнал будет содержать часть данных. Чтобы сбросить журнал в файловой системе
ext3 или ext4 в обычную базу данных файловой системы, запустите команду e2fsck
следующим образом:
# e2fsck -fy /dev/disk_device

Однако вы можете смонтировать поврежденную файловую систему ext3 или ext4
в режиме ext2, поскольку ядро не будет монтировать эти файловые системы с непустым журналом.

Наихудший случай
Если с диском случилось что-то серьезное, у вас не так уж много способов решить
эту проблему.
Вы можете попытаться извлечь весь образ файловой системы с диска
с помощью команды dd и перенести его в раздел на другом диске того же
размера.
Можете смонтировать файловую систему в режиме только для чтения и спасти все, что успели.
Можете использовать команду debugfs.
В первых двух случаях все равно нужно восстановить файловую систему перед
ее монтированием, если вы не хотите вручную просматривать необработанные

4.2. Файловые системы   129
данные. А если хотите просмотреть их вручную, то можете ввести команду
fsck -y и ответить на все ее вопросы. Но делайте это в крайнем случае, потому что
в процессе восстановления могут возникнуть проблемы, которые лучше решить
вручную.
Инструмент debugfs позволяет просматривать файлы в файловой системе и копировать их в другое место. По умолчанию он открывает файловые системы в режиме
только для чтения. При восстановлении данных сохранить ваши файлы в целости
и сохранности, чтобы не испортить все еще больше, — это хорошая идея.
Если проблема приобретает масштаб катастрофы, нет резервных копий и вы уже
отчаялись, то необходимо обращаться к профессиональным службам и надеяться,
что они смогут «соскрести» данные с диска.

4.2.12. Файловые системы специального назначения
Не все файловые системы представляют собой хранилище на физических носителях. У большинства версий систем Unix есть файловые системы, которые служат
системными интерфейсами. То есть вместо того, чтобы быть просто средством
хранения данных на устройстве, файловая система может представлять системную
информацию, такую как идентификаторы процессов и диагностические сообщения
ядра. Идея таких файловых систем восходит к механизму /dev, который является
ранней моделью использования файлов для интерфейсов ввода-вывода. Идея
/proc возникла в восьмой версии Unix, реализованной Томом Дж. Киллианом, а ее
производство ускорилось, когда компания Bell Labs (включая многих из тех, кто
изначально разрабатывал Unix) создала Plan 9 — исследовательскую операционную систему, которая вывела абстракцию файловой системы на совершенно новый
уровень (en.wikipedia.org/wiki/Plan_9_from_Bell_Labs).
Перечислим некоторые из специальных файловых систем, широко используемых
в Linux.
proc. Монтируется в каталоге /proc. Название — сокращенная версия от слова
process (процесс). Каждый пронумерованный каталог внутри /proc ссылается

на идентификатор текущего процесса в системе, файлы в каждом каталоге
представляют различные аспекты этого процесса. Каталог /proc/self представляет текущий процесс. Файловая система proc в Linux содержит большое
количество дополнительной информации о ядре и оборудовании в таких
файлах, как /proc/cpuinfo. Имейте в виду, что в руководстве по созданию
ядра рекомендуется перемещать информацию, не связанную с процессами,
из /proc в /sys, поэтому системная информация в /proc может представлять
не самый актуальный интерфейс.
sysfs. Монтируется в каталоге /sys (о нем рассказывалось в главе 3).
tmpfs. Монтируется в каталоге /run и в других местах. С помощью tmpfs

вы можете использовать физическую память и пространство подкачки

130   Глава 4. Диски и файловые системы
swap в качестве временного хранилища. Также можете монтировать tmpfs
там, где вам нужно, применяя параметры size и nr_blocks для управления
максимальным размером. Тем не менее будьте осторожны, чтобы постоянно
не добавлять данные в папку tmpfs, потому что в системе в конечном итоге
закончится память и программы начнут сбоить.
squashfs. Тип файловой системы, доступной только для чтения, в которой

содержимое хранится в сжатом формате и извлекается по требованию с помощью устройства обратной связи. Одним из примеров использования
является система управления пакетами snap, которая монтирует пакеты
в каталоге /snap.
overlay. Файловая система, которая объединяет каталоги в составную систе-

му. Контейнеры часто задействуют файловые системы overlay (наслоения)
(вы узнаете, как они работают, в главе 17).

4.3. Область подкачки swap
Не каждый раздел на диске содержит файловую систему. А еще можно увеличить
объем оперативной памяти на машине за счет дискового пространства. Если у вас
заканчивается реальная память, система виртуальной памяти Linux может автоматически перемещать фрагменты памяти в дисковое хранилище и обратно. Это
называется подкачкой, потому что части бездействующих программ отправляются
на диск в обмен на активные части, находящиеся на диске. Область диска, используемая для хранения страниц памяти, называется областью подкачки (или просто
подкачкой, swap).
Выходные данные команды free подключают использование подкачки в килобайтах
следующим образом:
$ free

total
--пропуск-Swap:
514072

used

free

189804

324268

4.3.1. Использование раздела диска в качестве области подкачки
Чтобы задействовать весь раздел диска в качестве области подкачки, выполните
следующие действия.
1. Убедитесь, что раздел пуст.
2. Запустите команду mkswap dev, где dev — устройство раздела. Эта команда
помещает сигнатуру области подкачки на раздел, помечая его (а не файловую
систему или что-либо другое) как область подкачки.
3. Запустите команду swapon dev, чтобы зарегистрировать область в ядре.

4.3. Область подкачки swap   131
После создания раздела подкачки вы можете поместить новую запись подкачки
в файл /etc/fstab, чтобы система задействовала область подкачки сразу после загрузки компьютера. Вот пример записи, которая использует /dev/sda5 в качестве
раздела подкачки:
/dev/sda5 none swap sw 0 0

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

4.3.2. Использование файла в качестве области подкачки
Вы можете использовать обычный файл в качестве области подкачки, если необходимо переделать разбиение диска, чтобы создать основной раздел подкачки, и при
этом не должно быть никаких проблем.
Примените следующие команды, чтобы создать пустой файл, инициализировать
его как область подкачки и добавить в пул подкачки:
# dd if=/dev/zero of=swap_file bs=1024k count=num_mb
# mkswap swap_file
# swapon swap_file

В примере swap_file — это имя нового файла подкачки, а num_mb — его желаемый
размер в мегабайтах.
Чтобы удалить раздел подкачки или файл из активного пула ядра, используйте
команду swapoff. В вашей системе должно быть достаточно свободной памяти
(реальной и подкачки вместе взятых) для размещения любых активных страниц
в удаляемой части пула подкачки.

4.3.3. Определение необходимого размера области подкачки
Уже давно в системах Unix было общепринятым резервировать по крайней мере
вдвое больший объем области подкачки, чем существует реальной оперативной
памяти. Сегодня проблема заключается не только в огромных объемах доступных
дисков и памяти, но и в способах использования системы. С одной стороны, дискового пространства так много, что возникает соблазн выделить более чем вдвое
больший объем памяти. С другой — вы можете никогда и не задействовать область
подкачки, потому что реальной памяти и так много.
Правило «удвоенного объема реальной памяти» было введено в то время, когда
несколько пользователей работали на одном компьютере. Однако не все они были
активны, поэтому было удобно иметь возможность задействовать память неактивных пользователей, если активному требовалось больше памяти.

132   Глава 4. Диски и файловые системы
То же самое работает и для компьютера с одним пользователем. Если вы запускаете
множество процессов, можно переместить в область подкачки части неактивных
процессов или даже неактивные части активных процессов. Однако если многие
активные процессы хотят использовать память одновременно и вы часто обращаетесь к области подкачки, возникнут серьезные проблемы с производительностью,
поскольку ввод-вывод диска (даже SSD) слишком медлителен и отстает от остальной системы. В таком случае можно либо докупить дополнительный объем памяти,
либо завершить некоторые процессы, либо пожаловаться.
Ядро Linux может самостоятельно перевести процесс в область подкачки, чтобы
обеспечить немного больший объем дискового кэша. Чтобы предотвратить такое
поведение, администраторы настраивают определенные системы вообще без области подкачки. Например, высокопроизводительные серверы никогда не должны
использовать пространство подкачки и должны избегать доступа к диску, если это
вообще возможно.
ПРИМЕЧАНИЕ
Опасно не добавлять пространство подкачки на компьютере общего назначения. Если
на компьютере полностью заканчивается как реальная память, так и область подкачки,
ядро Linux вызывает процесс OOM Killer (Out-Of-Memory Killer), чтобы принудительно
завершить процессы и освободить немного памяти. Очевидно, это не должно происходить с настольными приложениями. В то же время высокопроизводительные серверы
включают в себя сложные системы мониторинга, резервирования и балансировки нагрузки, гарантирующие, что они никогда не достигнут опасной зоны.
Вы узнаете гораздо больше о том, как работает система памяти, прочитав главу 8.

4.4. Менеджер логических томов LVM
До сих пор мы рассматривали только прямое управление и использование дисков
через разделы с точным указанием на устройствах хранения места, где должны
находиться определенные данные. Теперь вы знаете, что доступ к блочному устройству, такому как /dev/sda1, приведет вас к месту на определенном устройстве
в соответствии с таблицей разделов в /dev/sda, даже если точное местоположение
может быть неизвестно.
Обычно все работает нормально, но и у этого способа управления есть недостатки,
особенно когда дело доходит до внесения изменений в диски уже после установки.
Например, если вы хотите обновить диск, необходимо установить новый диск,
раздел, добавить файловые системы, возможно, внести некоторые изменения
в загрузчик, выполнить другие задачи и, наконец, переключиться на новый диск.
Этот процесс чреват ошибками и требует нескольких перезагрузок. Есть случай
еще хуже: когда вы хотите установить дополнительный диск, чтобы увеличить
емкость, вам нужно выбрать новую точку монтирования для файловой системы

4.4. Менеджер логических томов LVM   133
на этом диске и надеяться, что сможете вручную распределить свои данные между
старым и новым дисками.
Менеджер LVM решает эти проблемы, добавляя еще один уровень управления
между устройствами физического блока и файловой системой. Суть этого приема
заключается в том, что вы выбираете набор физических томов (Phisycal Volumes,
PV, обычно просто блочных устройств, таких как разделы диска), чтобы добавить
их в группу томов (Volume Groups, VG), которая действует как своего рода общий
пул данных. Затем выделяете из группы томов логические тома (Logical Volumes, LV).
На рис. 4.4 проиллюстрирована описанная выше схема для одной группы томов.
Здесь показаны несколько физических и логических томов, но многие системы
на базе LVM имеют только один физический и только два логических тома (для
корневого и подкачки).

Рис. 4.4. Сочетание физических и логических томов в группе томов
Логические тома — это просто блочные устройства, они содержат файловые системы или сигнатуры подкачки, поэтому вы можете рассматривать взаимосвязь
между группой томов и ее логическими томами как аналогичную взаимосвязь диска
и его разделов. Критическое различие заключается в том, что не вы определяете
самостоятельно, как логические тома расположены в группе томов, — это делает
менеджер LVM.
LVM позволяет выполнять мощные и чрезвычайно полезные задачи:
добавлять больше физических томов (например, другой диск) в группу томов,
увеличивая ее размер;
удалять физические тома, если для размещения существующих логических
томов внутри группы томов достаточно места;
изменять размер логических томов (и, как следствие, изменять размер файловых систем с помощью утилиты fsadm).

134   Глава 4. Диски и файловые системы
Вы можете сделать все это без перезагрузки компьютера и в большинстве случаев
без размонтирования каких-либо файловых систем. Хотя для добавления нового
оборудования на физических дисках может потребоваться завершить работу системы, облачные вычислительные среды часто позволяют добавлять новые блочные
устройства хранения данных и без этого, что делает менеджер LVM отличным
решением для систем, которым требуется такая гибкость и эффективность.
Мы рассмотрим менеджер LVM довольно подробно. Сначала изучим, как взаимодействовать с логическими томами и их компонентами и управлять ими, а затем
более детально поговорим о том, как работают LVM и драйвер ядра, на котором он
построен. Однако обсуждение менеджера LVM не слишком важно для понимания
остальной части книги, поэтому можете сразу переходить к главе 5.

4.4.1. Работа с менеджером LVM
LVM имеет ряд инструментов пользовательского пространства для управления
томами и группами томов. Большинство из них основаны на команде lvm — интерактивном инструменте общего назначения. Существуют отдельные команды
(которые являются просто символическими ссылками на LVM) для выполнения
определенных задач. Например, команда vgs имеет тот же эффект, что и ввод vgs
в командной строке lvm> инструмента lvm, и vgs (обычно в /sbin) является символической ссылкой на lvm. В этой книге мы будем применять отдельные команды.
В следующих нескольких разделах рассмотрим компоненты системы, использующей логические тома. Первые примеры взяты из стандартного дистрибутива
Ubuntu с применением параметров разделения LVM, поэтому многие имена будут
содержать слово Ubuntu. Однако все технические детали относятся не только к этому конкретному дистрибутиву.

Понятие групп томов и их перечисление
Упомянутая ранее команда vgs отображает группы томов, настроенные в настоящее
время в системе. Вывод команды довольно лаконичен. Вот что вы можете увидеть
в примере работы LVM:
# vgs
VG
#PV #LV #SN Attr
VSize
VFree
ubuntu-vg 1
2
0 wz--n- myjob

7.8. Юниты таймера обычных пользователей   231
Завершите ввод с помощью сочетания клавиш Ctrl+D. (Утилита at считывает коман­
ды со стандартного ввода.)
Чтобы проверить, что задание было запланировано, используйте команду atq. Чтобы
удалить его, примените команду atrm. Вы также можете запланировать для этой задачи дни в будущем, добавив дату в формате ДД.ММ.ГГ, например at 22:30 30.09.15.
Команда at выполняет только эти функции. Она применяется не слишком часто,
но очень эффективна, когда в ней возникает необходимость.

7.7.1. Аналоги таймера
Вы можете заменить службу at юнитами таймера systemd. Создавать их гораздо
проще, чем периодические юниты таймера, которые мы рассмотрели ранее, и можно
запускать в командной строке следующим образом:
# systemd-run --on-calendar='2022-08-14 18:00' /bin/echo this is a test
Running timer as unit: run-rbd000cc6ee6f45b69cb87ca0839c12de.timer
Will run service as unit: run-rbd000cc6ee6f45b69cb87ca0839c12de.service

Команда systemd-run создает временный юнит таймера, который можно просмотреть с помощью обычной команды systemctl list-timers. Если вас не волнует
конкретное время, укажите вместо него смещение по времени с помощью параметра
--on-active (например, --on-active=30m — действие будет выполнено в течение
30 минут в будущем).
ПРИМЕЧАНИЕ
При использовании параметра --on-calendar важно указать календарную дату в будущем, а также время. В противном случае юниты таймера и службы останутся, а таймер
будет запускать службу каждый день в указанное время, как было бы, если бы вы создали
обычный юнит таймера, описанный ранее. Синтаксис для этого параметра такой же, как
и для параметра OnCalendar.

7.8. Юниты таймера обычных пользователей
Все юниты таймера systemd, которые мы встречали до сих пор, были запущены
от имени суперпользователя. Юнит таймера можно создать и от имени обычного
пользователя. Для этого добавьте параметр --user в systemd-run.
Но если вы выйдете из системы до запуска юнита, он не запустится, а если выйдете
из нее до завершения работы юнита, его работа завершится. Это происходит потому,
что в systemd есть менеджер пользователей, связанный с каждым пользователем,
зашедшим в систему, и это необходимо для запуска юнитов таймера. Вы можете
указать systemd, чтобы он сохранял менеджер пользователя после выхода пользователя из системы, с помощью команды
$ loginctl enable-linger

232   Глава 7. Настройка системы: журналирование, системное время
Как суперпользователь, вы можете включить менеджер для другого пользователя
user:
# loginctl enable-linger user

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

7.9.1. ID пользователей и переключение пользователей
Мы обсудили, как программы setuid, такие как sudo и su, позволяют вам временно сменить пользователя, а также рассмотрели системные компоненты,
например login, которые контролируют доступ пользователей. Возможно, вас
заинтересовало, как работают эти части и какую роль ядро играет в переключении
пользователей.
Когда вы временно переключаетесь на другого пользователя, реально происходит
смена идентификатора пользователя. Есть два способа сделать это, и ядро обрабатывает оба. Первый — с помощью исполняемого файла setuid, который был описан
в разделе 2.17. Второй — с помощью семейства системных вызовов setuid(). Существует несколько версий этого системного вызова для охвата различных идентификаторов пользователей, связанных с процессом, о чем вы узнаете в подразделе 7.9.2.
В ядре есть основные правила, регламентирующие, что может и чего не может делать
процесс, и вот три основных принципа, которых придерживаются исполняемые
файлы setuid и setuid():
Процесс может запускать исполняемый файл setuid, если у него есть соответствующие права доступа к файлам.
Процесс, запущенный от имени суперпользователя (ID пользователя 0),
может задействовать setuid (), чтобы стать любым другим пользователем.
Процесс, запущенный не от имени суперпользователя, имеет ограничения на
то, как он может применять setuid(), — в большинстве случаев это запрещено.
Как следствие из этих правил: если вы хотите переключить идентификаторы
пользователей с обычного пользователя на другого пользователя, потребуется
применить несколько методов. Например, исполняемый файл sudo имеет права
доступа root для setuid, и после запуска он может вызвать setuid(), чтобы стать
другим пользователем.

7.9. Доступ пользователя   233
ПРИМЕЧАНИЕ
По сути, переключение пользователей не имеет ничего общего с паролями или именами
пользователей. Это исключительно концепции пользовательского пространства, как вы
впервые увидели в подразделе 7.3.1, когда рассказывалось о файле /etc/passwd. Более
подробно о том, как это работает, говорится в подразделе 7.9.4.

7.9.2. Владельцы процессов, действующий UID, реальный UID
и сохраненный UID
До сих пор мы поверхностно касались темы ID пользователей. На самом деле
каждый процесс имеет более одного ID пользователя. Ранее мы изучили действующие идентификаторы (действующий UID, effective user ID, или euid),
которые определяют права доступа для процесса (что наиболее важно, права
доступа к файлам). Второй идентификатор пользователя — реальный идентификатор пользователя (реальный UID, real user ID, или ruid) указывает, кто
инициировал процесс. Обычно эти идентификаторы идентичны, но, когда вы
запускаете программу setuid, Linux устанавливает euid для владельца программы во время выполнения и при этом сохраняет ваш исходный идентификатор
пользователя в ruid.
Разница между действующим и реальными UID настолько запутанна, что множество документации, касающейся владения процессом, содержит неверную
информацию.
Представьте, что euid — это исполнитель, а ruid — владелец. ruid определяет
пользователя, который можетвзаимодействовать с запущенным процессом и, что
наиболее важно, завершать процессы и отправлять сигналы процессу. Например,
если пользователь A запускает новый процесс, который выполняется от имени
пользователя Б (на основе разрешений setuid), то пользователь A по-прежнему
владеет процессом и может его принудительно завершить.
Мы видели, что большинство процессов имеют одинаковые euid и ruid. В результате
выходные данные по умолчанию для ps и других программ диагностики системы
показывают только идентификатор euid. Вы можете просмотреть оба идентификатора пользователей в своей системе с помощью команды
$ ps -eo pid,euser,ruser,comm

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

234   Глава 7. Настройка системы: журналирование, системное время
Еще большую путаницу создает то, что помимо реальных и действующих идентификаторов пользователей существует также сохраненный идентификатор пользователя
(saved user ID, который обычно не сокращается). Процесс во время выполнения может
переключить свой идентификатор euid на идентификатор ruid или сохраненный
идентификатор пользователя. (Еще больше усложняет ситуацию наличие в Linux
еще одного, к счастью, редко применяемого идентификатора пользователя — идентификатора пользователя файловой системы (file system user ID), или fsuid, который
определяет пользователя, получающего доступ к файловой системе.)

Типичное поведение программы setuid
Концепция идентификатора ruid может отличаться от того, что вы знаете о работе
с идентификаторами. Почему же вам прежде не приходилось иметь дело с другими идентификаторами пользователей? Например, если нужно принудительно
завершить процесс, запущенный с помощью команды sudo, вы все равно должны
применять sudo и не можете убить процесс от имени обычного пользователя. И разве в этом случае обычный пользователь не должен иметь идентификатора ruid,
который дает подходящие разрешения и права?
Причина в том, что sudo и многие другие программы setuid явно изменяют euid
и ruid с помощью одного из системных вызовов setuid(). Это происходит потому,
что часто возникают непредвиденные побочные эффекты и проблемы с доступом,
когда все идентификаторы пользователей не совпадают.
ПРИМЕЧАНИЕ
Если вас интересуют подробности и правила, касающиеся переключения идентификаторов пользователей, прочитайте страницу руководства setuid(2) и проверьте другие
страницы руководства, перечисленные в разделе SEE ALSO («См. также»). Существует
множество вариантов системных вызовов для различных ситуаций.
Некоторые программы не любят идентификатор ruid суперпользователя. Чтобы
предотвратить изменение правил sudo, добавьте в свой файл /etc/sudoers следующую строку (остерегайтесь побочных эффектов для других программ, которые
будете запускать от имени суперпользователя!):
Defaults

stay_setuid

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

7.9. Доступ пользователя   235
Если вы создадите копию оболочки bash с root setuid, любой локальный пользователь сможет выполнить ее и полностью запустить систему. Да, это действительно
так просто. Однако даже специальная программа с root setuid может представлять
опасность, если в ней есть ошибки. Слабые места в программах, запущенных от
имени суперпользователя, — основной путь вторжения в системы, а вторжения
случаются часто.
Поскольку существует множество способов взлома системы, предотвращение этого — дело многогранное и масштабное. Один из наиболее важных способов избежать
нежелательной активности в вашей системе — принудительно идентифицировать
пользователей с помощью имен и надежных паролей.

7.9.3. Идентификация пользователя, аутентификация и авторизация
Многопользовательская система должна обеспечивать базовую поддержку безо­
пасности пользователей в трех областях: идентификации, аутентификации и авторизации. Идентификация отвечает на вопрос о том, кем являются пользователи.
Аутентификация просит их доказать, что они именно те, кем назвались. Наконец,
авторизация применяется для определения и ограничения разрешенной деятельности пользователей.
В процессе идентификации пользователя ядро Linux задействует только числовые идентификаторы пользователей для процесса и владельца файла. Ядро знает
правила авторизации для запуска исполняемых файлов setuid и для того, как
идентификаторы пользователей могут запускать семейство системных вызовов
setuid() для перехода от одного пользователя к другому. Однако ядро ничего не
знает об аутентификации — именах пользователей, паролях и пр. Практически
все, что связано с аутентификацией, происходит уже в пространстве пользователя.
Мы обсудили сопоставление идентификаторов пользователей и паролей в подразделе 7.3.1, теперь же рассмотрим, как пользовательские процессы получают доступ
к этой процедуре. Начнем с упрощенного варианта, когда пользовательский процесс хочет знать свое имя пользователя username (имя, соответствующее идентификатору euid). В традиционной системе Unix процесс, чтобы получить свое имя
пользователя, может выполнить следующие действия:
1. Запросить у ядра свой идентификатор euid с помощью системного вызова
getuid().
2. Открыть файл /etc/passwd и начать чтение с начала.
3. Считать строку файла /etc/passwd. Если считывать больше нечего, процесс
не смог найти имя пользователя.
4. Разобрать строку на поля, разбивая их двоеточиями. Третье поле — это идентификатор пользователя для текущей строки.

236   Глава 7. Настройка системы: журналирование, системное время
5. Сравнить идентификатор шага 4 с идентификатором шага 1. Если они
идентичны, значит, первое поле на шаге 4 — это желаемое имя пользователя,
и процесс может прекратить поиск и применить его.
6. Перейти к следующей строке в файле /etc/passwd и возвратиться к шагу 3.
Это длительная процедура, а ее реализация, как правило, еще более сложна.

7.9.4. Применение библиотек для получения информации о пользователе
Если бы каждый разработчик, которому нужно знать текущее имя пользователя,
должен был написать весь код из приведенного ранее примера, система была бы
ужасно распределенной, раздутой и непригодной для работы. К счастью, существуют стандартные библиотеки, которые мы можем использовать для выполнения
повторяющихся задач, подобных данной. В этом случае для получения имени пользователя нужно всего лишь вызвать функцию getpwuid() в стандартной библиотеке
после получения ответа от geteuid(). (Подробнее о том, как они работают, см. на
страницах руководства для этих вызовов.)
Стандартная библиотека для исполняемых файлов в вашей системе разделяемая,
таким образом, вы можете внести существенные изменения в реализацию аутентификации без изменения какой-либо программы. Например, можете отказаться
от применения /etc/passwd для своих пользователей, а вместо этого взять сетевую
службу, такую как LDAP, изменив только конфигурацию системы.
Этот подход хорошо работает для идентификации имен пользователей, связанных
с идентификаторами пользователей, но для паролей неэффективен. В подразделе 7.3.1
описывается, как традиционно зашифрованный пароль был частью /etc/passwd,
поэтому, если вы хотите проверить пароль, введенный пользователем, то должны зашифровать все, что ввел пользователь, и сравнить с содержимым файла /etc/passwd.
Подобная традиционная реализация имеет множество ограничений, в том числе:
не позволяет установить общесистемный стандарт для протокола шифрования;
предполагает, что у вас есть доступ к зашифрованному паролю;
предполагает, что система будет запрашивать у пользователя пароль каждый
раз, когда он хочет получить доступ к чему-то, что требует аутентификации
(а это очень раздражает);
предполагает, что вы хотите использовать пароли. Если вы захотите применять одноразовые токены, смарт-карты, биометрические данные или какуюлибо другую форму аутентификации пользователя, потребуется добавить
эту функцию самостоятельно.
Некоторые из этих ограничений способствовали разработке пакета теневых
(shadow) паролей, описанного в подразделе 7.3.3, который стал первым шагом

7.10. Подключаемые модули аутентификации (PAM)   237
в обеспечении возможности настройки паролей в масштабах всей системы. Но решение основной массы проблем пришло с разработкой и внедрением модулей PAM.

7.10. Подключаемые модули аутентификации
(PAM)
Чтобы обеспечить гибкость аутентификации пользователей, в 1995 году компания Sun Microsystems предложила новый стандарт под названием «подключаемые
модули аутентификации» (Pluggable Authentication Module, PAM) — систему разделяемых библиотек аутентификации (Open Software Foundation RFC 86.0, октябрь
1995 года). Для аутентификации пользователя приложение передает пользователя
утилите PAM, чтобы определить, может ли он успешно идентифицировать себя.
Таким образом, получилось довольно легко добавить поддержку дополнительных
методов аутентификации, таких как двухфакторные и физические ключи. В дополнение к поддержке механизма аутентификации PAM предоставляет ограниченный
объем контроля авторизации для служб (например, если вы хотите запретить определенную службу, такую как cron, определенным пользователям).
Поскольку существует множество сценариев аутентификации, PAM задействует ряд
динамически загружаемых модулей аутентификации. Каждый модуль выполняет
определенную задачу и является разделяемым объектом, который процессы могут
динамически загружать и запускать в своем исполняемом пространстве. Например,
pam_unix.so — это модуль, который может проверить пароль пользователя.
На самом деле эта технология довольно сложна. Интерфейс программирования
непрост, и до сих пор неясно, действительно ли PAM решает все существующие проблемы. Тем не менее PAM поддерживается почти в каждой программе, требующей
аутентификации в системе Linux, большинство дистрибутивов также применяют
PAM. И поскольку PAM работает поверх существующего API аутентификации
Unix, интеграция поддержки в клиент требует небольшой дополнительной работы
(если вообще потребует).

7.10.1. Конфигурация PAM
Изучать основы работы модулей PAM начнем с их конфигурации. Файлы конфигурации приложений PAM находятся в каталоге /etc/pam.d (более старые системы
могут использовать один файл /etc/pam.conf). Большинство установок содержат
много файлов, поэтому может быть неясно, с чего начать. Некоторые имена файлов,
такие как cron и passwd, соответствуют частям системы, с которыми вы уже знакомы.
Поскольку конкретная конфигурация этих файлов значительно варьируется в разных дистрибутивах, довольно трудно найти общий пример. Рассмотрим пример
строки конфигурации, которая работает для chsh (программа изменения оболочки — CHange SHell):
auth

requisite

pam_shells.so

238   Глава 7. Настройка системы: журналирование, системное время
В этой строке говорится, что оболочка пользователя должна быть указана в файле
/etc/shells, чтобы пользователь мог успешно пройти аутентификацию в программе
chsh. Давайте рассмотрим, как это сделать. Каждая строка конфигурации содержит
три поля в следующем порядке: тип функции, аргумент управления и модуль. Их
значения для этого примера таковы:
Тип функции. Функция, которую пользовательское приложение запрашивает
у PAM. В этом случае auth — аутентификация пользователя.
Аргумент управления. Этот параметр определяет, что делает PAM после
успешного или неудачного выполнения своего действия для текущей строки
(requisite в примере). Мы еще вернемся к этой теме.
Модуль. Модуль аутентификации, который запускается для строки, определяет, что она делает. В примере модуль pam_shells.so проверяет, указана
ли текущая оболочка пользователя в файле /etc/shells.
Конфигурация PAM подробно описана на странице руководства pam.conf(5). Рассмотрим несколько основных моментов.

Типы функции
Пользовательское приложение может попросить PAM выполнить одну из следующих четырех функций:
auth — аутентифицировать пользователя (узнать, является ли он тем, за кого

себя выдает);

account — проверить статус учетной записи пользователя (например, имеет

ли он право что-то делать);

session — выполнить что-то только для текущего сеанса пользователя (на-

пример, отобразить «сообщение дня» (message of the day, motd));

password — изменить пароль пользователя или другие учетные данные.

Для любой строки конфигурации модуль и функция вместе определяют действие
модулей PAM. Модуль может иметь несколько типов функций, поэтому при определении назначения строки конфигурации не забывайте, что функция и модуль
работают в паре. Например, модуль pam_unix.so проверяет пароль при выполнении
функции auth, но устанавливает пароль при выполнении функции password.

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

7.10. Подключаемые модули аутентификации (PAM)   239
Существует два вида управляющих аргументов: с простым синтаксисом и более
сложным. Вот три основных управляющих аргумента с простым синтаксисом,
которые вы можете найти в правиле:
sufficient — если это правило выполняется, аутентификация проходит успеш-

но и PAM больше не нужно просматривать какие-либо правила. Если правило
не выполняется, PAM переходит к дополнительным правилам;

requisite — если это правило выполняется успешно, PAM переходит к до-

полнительным правилам. Если правило не выполняется, аутентификация
завершается неудачно и PAM больше не нужно просматривать какие-либо
правила;
required — если это правило выполняется успешно, PAM переходит к допол-

нительным правилам. Если правило не выполняется, PAM переходит к дополнительным правилам, но всегда возвращает неудачную аутентификацию
независимо от конечного результата применения дополнительных правил.
Пример стека для функции аутентификации chsh:
auth
auth
auth
auth

sufficient
requisite
sufficient
required

pam_rootok.so
pam_shells.so
pam_unix.so
pam_deny.so

При такой конфигурации, когда команда chsh запрашивает у PAM функцию аутентификации, PAM выполняет следующее (см. блок-схему на рис. 7.4):
1. Модуль pam_rootok.so проверяет, не пытается ли суперпользователь пройти
аутентификацию. Если это так, процесс немедленно выполнится успешно
и не будет пытаться выполнить дальнейшую аутентификацию. Это сработает,
потому что аргумент управления имеет значение sufficient, что означает: выполнения этого действия достаточно для того, чтобы модуль PAM немедленно
сообщил об успехе chsh. В противном случае он переходит к шагу 2.
2. Модуль pam_shells.so проверяет, указана ли оболочка пользователя в файле
/etc/shells. Если ее там нет, модуль возвращает ошибку и аргумент управления requisite указывает, что модуль PAM должен немедленно сообщить
об этом сбое chsh и не пытаться выполнить аутентификацию. В противном
случае модуль успешно завершит выполнение и в соответствии с требованиями аргумента управления requisite перейдет к шагу 3.
3. Модуль pam_unix.so запрашивает у пользователя пароль и проверяет его.
Аргументу управления присвоено значение sufficient, поэтому успеха данного
модуля (правильного пароля) достаточно, чтобы PAM сообщил об этом оболочке chsh. Если пароль неверен, PAM переходит к шагу 4.
4. Модуль pam_deny.so никогда не выполняется, и поскольку аргумент управления установлен в значение required, PAM сообщает об ошибке в оболоч-

240   Глава 7. Настройка системы: журналирование, системное время
ку chsh. Это значение используется по умолчанию для случаев, когда больше
нет вариантов действий. (Обратите внимание, что аргумент управления
required не приводит к немедленному сбою функции PAM: он будет запускать все строки, оставшиеся в его стеке, но при этом модуль всегда будет
сообщать приложению о сбое.)

Рис. 7.4. Диаграмма выполнения правила PAM

7.10. Подключаемые модули аутентификации (PAM)   241
ПРИМЕЧАНИЕ
Не путайте термины «функция» и «действие», работая с модулями PAM. Функция является
целью высокого уровня — тем, чего требует пользовательское приложение (например,
аутентифицировать пользователя). Действие — это конкретный шаг, который PAM предпринимает для достижения этой цели. Просто помните, что сначала пользовательское
приложение вызывает функцию, а потом PAM применяет действия для достижения
нужной цели.
Расширенный синтаксис аргументов управления, указанный в квадратных скобках
([]), позволяет вручную управлять реакцией на основе конкретного возвращаемого значения модуля (а не только успеха или неудачи). Дополнительные сведения
см. на странице руководства pam.conf(5): когда вы поймете, как работает простой
синтаксис, у вас не возникнет проблем с расширенным синтаксисом.

Аргументы модуля
Модули PAM могут принимать аргументы, стоящие после имени модуля. Вы часто
будете сталкиваться с подобным вариантом для модуля pam_unix.so:
auth

sufficient

pam_unix.so

nullok

Аргумент nullok в примере сообщает, что у пользователя может не быть пароля (по
умолчанию то, что у него нет пароля, будет провоцировать ошибку).

7.10.2. Советы по синтаксису конфигурации PAM
Благодаря возможностям потока управления и синтаксису аргументов модуля синтаксис конфигурации PAM обладает многими функциями языка программирования
и определенной степенью мощности. Ранее мы лишь слегка коснулись этой темы,
рассмотрим еще несколько советов по поводу конфигурации PAM.
Чтобы узнать, какие модули PAM присутствуют в вашей системе, введите
команду man -k pam_ (обратите внимание на подчеркивание). Могут возникнуть трудности при поиске местоположения модулей. Введите команду
locate pam_unix.so и посмотрите, какой результат она выведет.
Страницы руководства содержат функции и аргументы для каждого модуля.
Многие дистрибутивы автоматически генерируют определенные файлы
конфигурации PAM, поэтому не стоит изменять их непосредственно в файле
/etc/pam.d. Прочитайте комментарии в ваших файлах /etc/pam.d перед их
редактированием: если файлы сгенерированы, комментарии в них сообщат,
откуда они взялись.
Файл конфигурации /etc/pam.d/other определяет конфигурацию по умолчанию для любого приложения, в котором нет собственного файла конфигурации. По умолчанию чаще всего конфигурация запрещает любые действия.

242   Глава 7. Настройка системы: журналирование, системное время
Существуют различные способы включения дополнительных файлов конфигурации в файл конфигурации PAM. Синтаксис @include загружает весь файл
конфигурации, но вы также можете задействовать аргумент управления для
загрузки только конфигурации для определенной функции. Использование
варьируется в зависимости от дистрибутивов.
Конфигурация PAM не ограничивается аргументами модуля. Некоторые
модули могут получать доступ к дополнительным файлам в /etc/security,
обычно для настройки ограничений для каждого пользователя.

7.10.3. Модули PAM и пароли
В связи с развитием проверки паролей Linux на протяжении многих лет существует ряд артефактов конфигурации паролей, которые могут вызывать путаницу.
Во-первых, файл /etc/login.defs. Это файл конфигурации для исходного набора
shadow-паролей. Он содержит информацию об алгоритме шифрования, используемом для файла пароля /etc/shadow, но редко применяется в системе с установленным PAM, поскольку конфигурация PAM уже содержит эту информацию.
Тем не менее алгоритм шифрования в /etc/login.defs должен соответствовать
конфигурации PAM в тех редких случаях, когда вы запускаете приложение, не
поддерживающее PAM.
Откуда PAM получает информацию о схеме шифрования паролей? Помните, что
у PAM есть два способа взаимодействия с паролями: функция auth (для проверки
пароля) и функция password (для установки пароля). Проще всего отследить параметр установки пароля с помощью команды grep:
$ grep password.*unix /etc/pam.d/*

Соответствующие строки должны содержать pam_unix.so и выглядеть примерно так:
password

sufficient

pam_unix.so obscure sha512

Аргументы obscure и sha512 указывают PAM, что делать при установке пароля.
Сначала PAM проверяет, достаточно ли надежен пароль (среди прочего — не похож
ли новый пароль на старый), а затем применяет алгоритм SHA512 для шифрования
нового пароля.
Но это происходит только тогда, когда пользователь устанавливает пароль, а не
когда PAM проверяет пароль. Итак, как PAM узнает, какой алгоритм применять при
аутентификации? К сожалению, конфигурация вам ничего не сообщит, для этого
нет аргументов шифрования pam_unix.so для функции auth. Страницы руководства
также ничего не расскажут.
Оказывается, модуль pam_unix.so просто пытается угадать алгоритм, обычно передавая библиотеке libcrypt грязную работу, перепробовав целую кучу вещей, пока
что-то не сработает или не останется ничего, что можно было бы попробовать (так

7.11. Что дальше?   243
было на момент написания книги). Таким образом, вам не нужно беспокоиться об
алгоритме шифрования проверки.

7.11. Что дальше?
Сейчас мы находимся примерно в середине книги и уже успели рассмотреть многие
жизненно важные блоки системы Linux. Обсуждение журналирования и пользователей в системе Linux показало, как можно разделить службы и задачи на небольшие независимые фрагменты, которые все еще могут в определенной степени
взаимодействовать друг с другом. В этой главе речь шла почти исключительно
о пользовательском пространстве, и теперь нам необходимо конкретизировать свое
представление о процессах пользовательского пространства и потребляемых ими
ресурсах. Для этого в главе 8 мы вернемся к обсуждению ядра.

8

Процессы и использование
ресурсов

В этой главе мы глубже изучим взаимосвязи между процессами, ядром и системными ресурсами. Существуют
три основных вида аппаратных ресурсов: процессор, память и устройства ввода-вывода. Процессы соперничают
за эти ресурсы, и задача ядра заключается в справедливом
их распределении. Само ядро также является ресурсом —
программным ресурсом, который используется для выполнения таких задач, как создание новых процессов и взаимодействие
с другими процессами.
Многие из инструментов, которые встретятся нам в этой главе, считаются инструментами мониторинга производительности. Они особенно полезны, если работа вашей
системы замедлилась и вы хотите понять почему. Однако особо отвлекаться на производительность не стоит. Попытка оптимизировать систему, которая уже работает
правильно, — пустая трата времени. Настройки по умолчанию в большинстве систем
оптимальны, поэтому вы должны изменять их только в том случае, если в этом появилась крайняя необходимость. Вместо этого давайте сосредоточимся на том, что на
самом деле измеряют инструменты, как ядро работает и взаимодействует с процессами.

8.1. Отслеживание процессов
В разделе 2.16 мы узнали, как с помощью команды ps получить список процессов,
запущенных в вашей системе в определенное время. Команда ps выводит текущие процессы и статистику их применения, но мало что рассказывает о том, как

8.2. Поиск открытых файлов с помощью команды lsof   245
процессы меняются с течением времени, потому она не сразу поможет вам определить, какой процесс использует слишком много процессорного времени или памяти.
Команда top предоставляет интерактивный интерфейс отображения информации
из команды ps. Она показывает текущее состояние системы, а также поля из списка
ps и обновляется каждую секунду. Возможно, самое важное заключается в том, что
top выводит наиболее активные процессы (по умолчанию те, которые в настоящее
время занимают больше всего процессорного времени) в верхней части экрана.
Вы можете отправлять команды в интерфейс top с помощью горячих клавиш. Наиболее часто используемые команды относятся к изменению порядка сортировки
или фильтрации списка процессов. Вот соответствующие им клавиши:
Пробел — немедленно обновляет отображение;
М — сортирует по текущему использованию памяти;
T — сортирует по общей (совокупной) загрузке процессора;
P — сортирует по текущей загрузке процессора (по умолчанию);
u — отображает только процессы одного пользователя;
f — выбирает другую статистику для отображения;
? — отображает краткую инструкцию по применению для всех основных
команд top.

ПРИМЕЧАНИЕ
Горячие клавиши команды top чувствительны к регистру.
У двух аналогичных утилит, atop и htop, расширенный набор отображений и функций. Большинство дополнительных их функций добавляют функциональность из
других инструментов. Например, htop использует многие возможности команды
lsof, описанные в следующем разделе.

8.2. Поиск открытых файлов с помощью
команды lsof
Команда lsof выводит список открытых файлов и использующих их процессов.
Поскольку Unix уделяет большое внимание файлам, lsof является одним из наиболее полезных инструментов для поиска проблемных мест. Но lsof не ограничивается обычными файлами, она может выводить сетевые ресурсы, динамические
библиотеки, конвейеры и многое другое.

8.2.1. Считывание вывода команды lsof
Запуск команды lsof в командной строке обычно приводит к огромному количеству выходных данных. Далее приведен фрагмент того, что вы можете увидеть.

246   Глава 8. Процессы и использование ресурсов
Этот вывод (слегка скорректированный для удобства чтения) включает открытые
файлы из процесса systemd (init), а также запущенный процесс vi:
# lsof
COMMAND PID
systemd
1
systemd
1
systemd
1
systemd
1
systemd
1

USER
root
root
root
root
root

FD TYPE DEVICE SIZE/OFF NODE
cwd DIR 8,1
4096
2
rtd DIR 8,1
4096
2
txt REG 8,1 1595792 9961784
mem REG 8,1 1700792 9961570
mem REG 8,1
121016 9961695

--пропуск-vi
1994
vi
1994

juser cwd
juser 3u

DIR 8,1
REG 8,1

4096
12288

NAME
/
/
/lib/systemd/systemd
/lib/x86_64-linux-gnu/libm-2.27.so
/lib/x86_64-linux-gnu/libudev.so.1

4587522 /home/juser
786440 /tmp/.ff.swp

--пропуск--

В выводе в верхней строке перечислены следующие поля:
COMMAND — имя команды для процесса, содержащего файловый дескриптор;
PID — идентификатор процесса;
USER — пользователь, выполняющий процесс;
FD — это поле может содержать два вида элементов. В большей части представленного вывода команды столбец FD показывает назначение файла.
В поле FD также может быть указан файловый дескриптор (File Descriptor)

открытого файла — число, которое процесс использует вместе с системными
библиотеками и ядром для идентификации файла и управления им. В последней строке показан файловый дескриптор 3;
TYPE — тип файла (обычный файл, каталог, сокет и т. д.);
DEVICE — основной и второстепенный номера устройства, на котором хра-

нится файл;

SIZE/OFF — размер файла;
NODE — номер inode файла;
NAME — имя файла.

Страница руководства lsof(1) содержит полный список того, что можно встретить
в каждом из полей, но вывод должен быть очевидным. Например, посмотрите на
записи с cwd в поле FD. Эти строки указывают текущие рабочие каталоги процессов.
Другой пример — самая последняя строка, в которой показан временный файл,
используемый процессом vi пользователя (PID 1994).
ПРИМЕЧАНИЕ
Вы можете запустить команду lsof и от имени суперпользователя, и от имени обычного
пользователя, но больше информации получите, заходя от имени суперпользователя.

8.3. Отслеживание выполнения программ и системных вызовов   247

8.2.2. Использование команды lsof
Существует два основных способа использования команды lsof:
перечислить все и передать вывод в команду типа less, а затем найти то, что
нужно. Это может занять некоторое время из-за большого объема данных;
сузить список, выводимый командой lsof, с помощью параметров командной
строки.
Вы можете использовать параметры командной строки, чтобы указать имя файла
в качестве аргумента, и в lsof будут перечислены только те записи, которые соответствуют аргументу. Например, следующая команда отображает записи для
файлов, открытых в /usr и всех его подкаталогах:
$ lsof +D /usr

Чтобы просмотреть список открытых файлов для определенного идентификатора
процесса, запустите следующую команду:
$ lsof -p pid

Чтобы увидеть краткое описание множества параметров lsof, запустите команду
lsof -h. Большинство параметров относятся к формату вывода (см. главу 10, в которой будет рассказано о сетевых функциях lsof).
ПРИМЕЧАНИЕ
Команда lsof сильно зависит от информации о ядре. Если вы выполните обновление
дистрибутива как для ядра, так и для команды lsof, обновленная lsof может не работать
до тех пор, пока вы не перезагрузите ядро.

8.3. Отслеживание выполнения программ
и системных вызовов
Инструменты, которые мы встречали ранее, исследуют активные процессы. Однако
если вы понятия не имеете, почему программа завершается почти сразу после запуска, команда lsof вам не поможет. На самом деле будет трудно даже запустить
lsof одновременно с неудачной командой.
Команды strace (system call trace — отслеживание системных вызовов) и ltrace
(library trace — отслеживание библиотек) помогут определить, что пытается сделать
программа. Эти инструменты выдают чрезвычайно большой вывод, но как только
вы узнаете, что искать, в вашем распоряжении будет больше информации для отслеживания проблем.

8.3.1. Команда strace
Напомним, что системный вызов — это привилегированная операция, которую
процесс пользовательского пространства запрашивает у ядра, например открытие

248   Глава 8. Процессы и использование ресурсов
файла и чтение данных из него. Утилита strace выводит все системные вызовы,
выполняемые процессом. Чтобы увидеть ее в действии, выполните команду
$ strace cat /dev/null

По умолчанию strace отправляет свой вывод в стандартную ошибку stderr. Если
хотите сохранить выходные данные в файле, используйте параметр -o save_file,
где save_file — имя файла. Вы можете перенаправить данные, добавив параметр
2> save_file в командную строку, а также зафиксировать любую стандартную
ошибку в исследуемой команде.
В главе 1 мы узнали, что когда один процесс хочет запустить другой процесс, он
делает системный вызов fork(), чтобы создать свою копию, а затем она использует
один из вызовов exec() для запуска новой программы. Команда strace начинает
работу над новым процессом (копией исходного процесса) сразу после вызова
функции fork(). Поэтому в первых строках вывода этой команды должна отображаться функция execve() в действии, за которой следует вызов инициализации
памяти brk(), как показано далее:
execve("/bin/cat", ["cat", "/dev/null"], 0x7ffef0be0248 /* 59 vars */) = 0
brk(NULL)
= 0x561e83127000

Следующая часть вывода посвящена в основном загрузке разделяемых библиотек.
Эту информацию можно пропустить, если только вы действительно не хотите
углубляться в систему разделяемых библиотек:
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=119531, ...}) = 0
mmap(NULL, 119531, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa9db241000
close(3)
= 0
--пропуск-openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"...,
832) = 832

Кроме того, пропустите вывод mmap, пока не дойдете до строк в конце вывода, которые выглядят следующим образом:
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 1), ...}) = 0
openat(AT_FDCWD, "/dev/null", O_RDONLY) = 3
fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(0x1, 3), ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x7fa9db21b000
read(3, "", 131072)
= 0
munmap(0x7fa9db21b000, 139264)
= 0
close(3)
= 0
close(1)
= 0

8.3. Отслеживание выполнения программ и системных вызовов   249
close(2)
exit_group(0)
+++ exited with 0 +++

= 0
= ?

Эта часть вывода отображает команду в действии. Сначала посмотрите на вызов
openat() (еще один вариант — open()), который открывает файл. Значение 3 —
это результат, означающий, что задача успешно выполнена (3 — это файловый
дескриптор, который ядро возвращает после открытия файла). Потом вы можете
увидеть, где cat считывает данные из /dev/null (вызов read(), который также содержит 3 в качестве файлового дескриптора). Далее считывать больше нечего, поэтому программа закрывает файловый дескриптор и завершает работу с помощью
exit_group().
Что происходит, когда команда обнаруживает ошибку? Используйте команду
strace cat not_a_file и изучите вызов open() в следующем выводе:
openat(AT_FDCWD, "not_a_file", O_RDONLY) = -1 ENOENT (No such file or directory)

Поскольку вызов open() не смог открыть файл, он вернул значение -1, чтобы сообщить об ошибке. Из примера видно, что strace сообщает о точной ошибке и выдает
ее краткое описание (No such file or directory — нет такого файла или каталога).
Наиболее распространенной проблемой в программах Unix являются отсутствие
файлов, поэтому, если системный журнал и другая информация в журнале не помогли и вы не находите отсутствующий файл, команда strace может быть очень
полезна. Вы даже можете использовать ее на демонах, которые разветвляются или
отсоединяются от процессов. Например, чтобы отследить системные вызовы вымышленного демона под названием crummyd, введите
$ strace -o crummyd_strace -ff crummyd

В этом примере параметр -o для strace регистрирует действия любого дочернего
процесса, который crummyd породил в crummyd_strace.pid, где pid — идентификатор
дочернего процесса.

8.3.2. Команда ltrace
Команда ltrace отслеживает вызовы разделяемых библиотек. Вывод аналогичен
выводу команды strace, но ltrace ничего не отслеживает на уровне ядра. Имейте
в виду, что вызовов разделяемых библиотек намного больше, чем системных вызовов. Вам определенно нужно будет отфильтровать вывод, и в самом ltrace есть
множество встроенных параметров, которые в этом вам помогут.
ПРИМЕЧАНИЕ
Дополнительные сведения о разделяемых библиотеках см. в подразделе 15.1.3. Команда
ltrace не работает со статически связанными двоичными файлами.

250   Глава 8. Процессы и использование ресурсов

8.4. Потоки
В Linux некоторые процессы разделены на части, называемые потоками. Поток очень
похож на процесс: у него есть идентификатор (thread ID, TID — идентификатор
потока), и ядро планирует и запускает потоки точно так же, как процессы. Однако,
в отличие от отдельных процессов, которые обычно независимо используют системные ресурсы, такие как память и подключения к вводу-выводу, все потоки внутри
одного процесса совместно задействуют свои системные ресурсы и часть памяти.

8.4.1. Однопоточные и многопоточные процессы
Многие процессы имеют только один поток. Процесс с одним потоком является
однопоточным, а процесс с более чем одним потоком — многопоточным. Все процессы начинаются с однопоточных. Этот начальный поток обычно называется основным потоком. Он может запускать новые потоки, делая процесс многопоточным,
подобно тому как процесс может вызывать fork() для запуска нового процесса.
ПРИМЕЧАНИЕ
О потоках обычно не упоминается, когда процесс однопоточный. В этой книге не упоминаются потоки, если только многопоточные процессы не влияют на то, что вы видите или делаете.
Основное преимущество многопоточного процесса заключается в следующем:
когда у процесса много задач, потоки могут выполняться одновременно на нескольких процессорах, что потенциально ускоряет вычисления. Хотя вы также
можете выполнять одновременные вычисления с несколькими процессами, потоки запускаются быстрее, чем процессы, и часто потокам проще или эффективнее
взаимодействовать через их общую память, чем процессам — по каналу (сетевое
соединение или конвейер).
Некоторые программы используют потоки для решения проблем, связанных
с управлением несколькими ресурсами ввода-вывода. Традиционно процесс иногда
использует fork() для запуска нового подпроцесса, чтобы начать работу с новым
входным или выходным потоком. Потоки предлагают аналогичный механизм без
дополнительных затрат на запуск нового процесса.

8.4.2. Просмотр потоков
По умолчанию в выводе команд ps и top отображаются только процессы. Чтобы
отобразить информацию о потоке в ps, добавьте параметр m. В листинге 8.1 показан
пример вывода.
Листинг 8.1. Просмотр потоков с помощью команды ps m
$ ps m
PID TTY
3587 pts/3

STAT


TIME COMMAND
0:00 bash❶

8.4. Потоки   251

3592

12534





pts/4
tty7
-

Ss
0:00 –

0:00 bash❷
Ss
0:00 –

668:30 /usr/lib/xorg/Xorg -core :0❸
Ssl+ 659:55 –
Ssl+
0:00 –
Ssl+
0:00 –
Ssl+
8:35 -

В листинге показаны процессы вместе с потоками. Каждая строка с номером
в столбце PID (❶, ❷ и ❸) представляет процесс, как и в обычном выводе ps. Строки
со знаками «дефис» в столбце PID представляют потоки, связанные с процессом.
В этом выводе процессы ❶ и ❷ имеют по одному потоку каждый, а процесс 12534
в ❸ — многопоточный (с четырьмя потоками).
Если вы хотите просматривать идентификаторы потоков с помощью команды ps,
используйте специальный формат вывода. В листинге 8.2 показаны только идентификаторы PID, TID и команды.
Листинг 8.2. Отображение идентификаторов PID и TID с помощью команды ps m
$ ps m -o pid,tid,command
PID
TID
COMMAND
3587

bash
– 3587

3592

bash
– 3592

12534

/usr/lib/xorg/Xorg -core :0
– 12534

– 13227

– 14443

– 14448


Пример вывода здесь соответствует потокам, показанным в листинге 8.1. Обратите внимание на то, что потоки однопоточных процессов идентичны PIDS — это
основной поток. Для многопоточного процесса 12534 поток 12534 также является
основным потоком.
ПРИМЕЧАНИЕ
Скорее всего, вы не будете взаимодействовать с отдельными потоками, как с процессами.
Чтобы влиять на отдельный поток в какой-то момент, необходимо много знать о том, как была
написана многопоточная программа, и даже тогда такое действие — не самая хорошая идея.
Потоки могут сбивать с толку, когда дело доходит до мониторинга ресурсов, поскольку отдельные потоки в многопоточном процессе могут потреблять ресурсы
одновременно. Например, top по умолчанию не отображает потоки, вам нужно будет
нажать клавишу H, чтобы включить отображение. Для большинства инструментов
мониторинга ресурсов, которые мы рассмотрим в дальнейшем, придется проделать
небольшую дополнительную работу, чтобы включить отображение потока.

252   Глава 8. Процессы и использование ресурсов

8.5. Введение в мониторинг ресурсов
Теперь обсудим некоторые темы мониторинга ресурсов, включая процессорное
(CPU) время, память и дисковый ввод-вывод. Мы рассмотрим его применение
в масштабах всей системы, а также на основе каждого процесса.
Многие пользователи начинают вникать во внутреннюю работу ядра Linux в интересах повышения производительности. Однако большинство систем Linux хорошо
работают в соответствии с настройками дистрибутива по умолчанию, и вы можете
потратить часы и даже дни, пытаясь повысить производительность своей машины, особенно если не знаете, что именно настраивать. Поэтому вместо того, чтобы
думать о производительности, экспериментируя с инструментами, описанными
в этой главе, изучите действия ядра при распределении ресурсов между процессами.

8.5.1. Измерение процессорного времени
Чтобы отслеживать один или несколько конкретных процессов с течением времени,
используйте параметр -p в команде top, как показано далее:
$ top -p pid1 [-p pid2 ...]

Чтобы узнать, сколько процессорного времени команде требуется в течение выполнения, задействуйте команду time. К сожалению, здесь возникает некоторая
путаница, потому что в большинстве оболочек есть встроенная команда time, которая не предоставляет обширной статистики, а в /usr/bin/time есть своя системная
утилита. Вероятно, вы сначала столкнетесь со встроенной оболочкой bash, поэтому
попробуйте выполнить time с помощью команды ls:
$ time ls

После завершения ls команда time отобразит вывод, как показано далее:
real
user
sys

0m0.442s
0m0.052s
0m0.091s

Пользовательское время (user time, user) — это количество секунд, потраченных
процессором на выполнение собственного кода программы. Некоторые команды
выполняются так быстро, что процессорное время приближается к нулю. Системное
время (system time, sys или system) — это количество времени, которое ядро тратит
на выполнение работы процесса, например чтение файлов и каталогов. Наконец,
реальное время (real time, real) (также называется затраченным временем) — это
общее время, затраченное на запуск процесса от начала до конца, включая время,
затраченное процессором на выполнение других задач. Это число не очень помогает
измерить производительность, но если вычесть пользовательское и системное время
из реального, вы получите общее представление о том, сколько времени процесс
тратит на ожидание системных и внешних ресурсов. Например, время, затраченное

8.5. Введение в мониторинг ресурсов   253
на ожидание ответа сетевого сервера на запрос, будет отображаться как реальное
время, а не как пользовательское или системное.

8.5.2. Настройка приоритетов процесса
Вы можете изменить распорядок процесса в ядре, чтобы предоставить определенному процессу больше или меньше процессорного времени, чем другим. Ядро
запускает каждый процесс в соответствии с назначенным ему приоритетом, который обозначен числом от –20 до 20, причем –20 — это высший приоритет. (Да, это
может сбить с толку.)
Команда ps -l отображает текущий приоритет процесса, но с помощью команды
top проще увидеть приоритеты в действии, как показано далее:
$ top
Tasks: 244 total, 2 running, 242 sleeping, 0 stopped, 0 zombie
Cpu(s): 31.7%us, 2.8%sy, 0.0%ni, 65.4%id, 0.2%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 6137216k total, 5583560k used,
553656k free, 72008k buffers
Swap: 4135932k total, 694192k used, 3441740k free, 767640k cached
PID
28883
1175
4022
4029
3971
5378
3821
4117
4138
4274
4267
2327

USER
bri
root
bri
bri
bri
bri
bri
bri
bri
bri
bri
bri

PR
20
20
20
20
20
20
20
20
20
20
20
20

NI VIRT RES SHR S %CPU %MEM
TIME+ COMMAND
0 1280m 763m 32m S
58 12.7 213:00.65 chromium-browse
0 210m 43m 28m R
44 0.7 14292:35 Xorg
0 413m 201m 28m S
29 3.4
3640:13 chromium-browse
0 378m 206m 19m S
2 3.5 32:50.86 chromium-browse
0 881m 359m 32m S
2 6.0 563:06.88 chromium-browse
0 152m 10m 7064 S
1 0.2 24:30.21 xfce4-session
0 312m 37m 14m S
0 0.6 29:25.57 soffice.bin
0 321m 105m 18m S
0 1.8 34:55.01 chromium-browse
0 331m 99m 21m S
0 1.7 121:44.19 chromium-browse
0 232m 60m 13m S
0 1.0 37:33.78 chromium-browse
0 1102m 844m 11m S
0 14.1 29:59.27 chromium-browse
0 301m
43m 16m S
0 0.7 109:55.65 xfce4-panel

В выводе команды top в столбце PR (priority — приоритет) указан текущий прио­
ритет ядра для процесса. Чем больше число, тем меньше вероятность того, что
ядро запланирует процесс, если другимпотребуется процессорное время. Однако
приоритет запуска сам по себе не определяет решение ядра выделить процессорное
время процессу, и ядро также может изменять приоритет во время выполнения
программы в зависимости от количества процессорного времени, потребляемого
процессом.
Рядом со столбцом приоритета находится столбец NI (nice, значение относительного приоритета), который дает подсказку планировщику ядра. Именно через
это значение можно повлиять на решение ядра. Ядро добавляет значение nice
к текущему приоритету, чтобы определить следующий временной интервал для
процесса. Устанавливая более высокое значение nice, вы становитесь «добрее»
к другим процессам, потому что ядро повышает их приоритет.

254   Глава 8. Процессы и использование ресурсов
По умолчанию значение nice равно 0. Теперь предположим, что вы выполняете
большие вычисления в фоновом режиме и не хотите затягивать свой сеанс работы.
Чтобы этот процесс отошел на второй план по сравнению с другими процессами
и выполнялся только тогда, когда другим задачам нечего делать, можете изменить
значение nice на 20 с помощью команды renice, где pid — идентификатор процесса,
который вы хотите изменить:
$ renice 20 pid

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

8.5.3. Измерение производительности процессора с помощью
среднего значения загрузки
Среднее значение загрузки — это среднее количество процессов, готовых к запуску
в данный момент. То есть это оценка количества процессов, которые способны использовать процессор в любой момент времени: сюда входят запущенные процессы
и ожидающие возможности задействовать процессор. Имейте в виду, что большинство процессов в вашей системе обычно ждут ввода (например, с клавиатуры,
мыши или из сети), что означает: они не готовы к запуску и не должны вносить
какие-либо данные в среднее значение нагрузки. На среднюю нагрузку влияют
только активные в данный момент процессы.

Команда uptime
Команда uptime сообщает три средних значения загрузки дополнительно ко времени всей работы ядра:
$ uptime
... up 91 days, ... load average: 0.08, 0.03, 0.01

Три цифры, выделенные жирным шрифтом, являются средними значениями загрузки за последние 1 минуту, 5 и 15 минут соответственно. Как вы можете видеть, эта
система не очень загружена: в среднем за последние 15 минут на всех процессорах
выполнялось всего 0,01 процесса. Другими словами, если бы у вас был только один
процессор, он запускал бы приложения для пользовательского пространства только
в течение 1 % от последних 15 минут.
Раньше на большинстве ПК средняя загрузка составляла значение, близкое к нулю,
если в работе находились не компиляция программы или игра. Среднее значение

8.5. Введение в мониторинг ресурсов   255
загрузки, равное нулю, — хороший знак, так как это означает, что процессор не перегружен и вы экономите энергию.
Однако компоненты пользовательского интерфейса в современных ПК, как правило, занимают больше ресурсов процессора, чем в прошлом. В частности, некоторые
веб-сайты (особенно их реклама) делают браузеры ресурсоемкими.
Если средняя загрузка достигает значения 1, это значит, что один процесс, скорее
всего, задействует процессор практически постоянно. С помощью команды top
идентифицируйте этот процесс — обычно он находится в верхней части вывода.
Большинство современных систем имеют несколько процессорных ядер или процессоров, поэтому несколько процессов могут легко выполняться одновременно.
Если у вас два ядра, среднее значение загрузки 1 означает, что в любой момент
времени, вероятно, активно только одно из ядер, а среднее значение загрузки 2 —
что оба ядра заняты.

Управление высокими нагрузками
Высокая средняя загрузка не всегда означает, что у вашей системы возникли проблемы. Система с достаточным объемом памяти и количеством ресурсов ввода-вывода
может легко обрабатывать множество запущенных процессов. Если средняя загрузка
высока, а система по-прежнему хорошо откликается, не паникуйте: в ней просто много
процессов, совместно задействующих процессор. Процессы должны конкурировать
друг с другом за процессорное время, в результате им потребуется больше времени
для выполнения своих вычислений, чем если бы каждому из них было разрешено
постоянно использовать процессор. Другой случай, когда среднее значение высокой загрузки считается нормальным, связан с веб-сервером или вычислительным
сервером, где процессы могут запускаться и завершаться так быстро, что механизм
измерения среднего значения нагрузки не может эффективно функционировать.
Однако, если средняя загрузка очень высока и вы чувствуете, что система замедляется,
у вас могут возникнуть проблемы с производительностью памяти. Если в системе
мало памяти, ядро может начать быстро подкачивать память с диска. Когда это произойдет, многие процессы будут готовы к запуску, но для них память может оказаться
недоступной, поэтому они останутся в состоянии готовности к запуску (способствуя
увеличению значения средней загрузки) гораздо дольше, чем обычно. Далее мы
рассмотрим, почему это может произойти, подробнее изучив оперативную память.

8.5.4. Мониторинг состояния памяти
Один из простейших способов проверить состояние памяти системы в целом — это
запустить команду free или просмотреть каталог /proc/meminfo, чтобы узнать,
сколько реальной памяти затрачивается для кэширования и буферизации. Как
упоминалось ранее, проблемы с производительностью могут возникать из-за нехватки памяти. Если используется не так много кэш-памяти/буферной памяти

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

Как работает память
Как объяснялось в главе 1, центральный процессор имеет блок управления памятью
(MMU) для обеспечения гибкости доступа к ней. Ядро помогает MMU, разбивая
память, используемую процессами, на более мелкие фрагменты, называемые страницами. Ядро поддерживает структуру данных, называемую таблицей страниц,
которая сопоставляет адреса виртуальных страниц процесса с реальными адресами
страниц в памяти. Когда процесс обращается к памяти, MMU преобразует виртуальные адреса, задействуемые процессом, в реальные адреса на основе таблицы
страниц ядра.
На самом деле пользовательскому процессу не нужно, чтобы все его страницы
памяти немедленно оказывались доступными для запуска. Ядро обычно загружает и выделяет страницы по мере необходимости процесса, эта система известна
как загрузка страниц по требованию, или просто загрузка по требованию. Чтобы
понять, как это работает, рассмотрим, как программа стартует и запускается как
новый процесс.
1. Ядро загружает начало кода инструкции программы на страницы памяти.
2. Ядро может выделить несколько страниц рабочей памяти для нового процесса.
3. По мере выполнения процесс может достичь точки, когда следующая инструкция в его коде не будет содержаться ни на одной из страниц, изначально
загруженных ядром. В этот момент ядро берет на себя управление, загружает
необходимую страницу в память, а затем позволяет программе возобновить
выполнение.
4. Аналогично, если программе требуется больше рабочей памяти, чем было
выделено изначально, ядро обрабатывает ее, находя свободные страницы
(или выделяя место) и назначая их процессу.
Вы можете узнать размер страницы системы, просмотрев конфигурацию ядра:
$ getconf PAGE_SIZE
4096

Это число выражено в байтах, и значение 4K типично для большинства систем
Linux.
Ядро неявно сопоставляет страницы реальной памяти с виртуальными адресами, то
есть оно не помещает все доступные страницы в один большой пул и не выделяет их
оттуда. Реальная память имеет множество разделов, которые зависят от аппаратных

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

Ошибки страницы
Если страница памяти не готова, а процесс уже хочет ее использовать, он вызывает
ошибку страницы. В этом случае ядро берет на себя управление процессором из
процесса, чтобы подготовить страницу. Существует два вида ошибок страницы —
незначительные и серьезные.
Незначительная ошибка страницы (minor page fault) возникает, когда нужная
страница действительно находится в основной памяти, но MMU не знает, где
именно. Это может произойти, когда процесс запрашивает больше памяти или
когда в MMU недостаточно места для хранения всех расположений страниц
для процесса (внутренняя таблица сопоставления MMU обычно довольно
мала). В этом случае ядро сообщает MMU о странице и разрешает продолжить процесс. О таких ошибках страницы беспокоиться не стоит, многие из
них возникают во время выполнения процесса.
Серьезная ошибка страницы (major page fault) возникает, когда нужной
страницы вообще нет в основной памяти. Это означает, что ядро должно
загрузить ее с диска или из какого-то другого медленного места хранения.
Множество серьезных сбоев страниц приведет к сбою системы, потому что
ядро должно выполнить значительный объем работы для предоставления
страниц, лишая обычные процессы возможности запуска.
Некоторые серьезные ошибки страницы неизбежны, например возникающие
при загрузке кода с диска при первом запуске программы. Самые большие проблемы появляются, если у вас близка к исчерпанию память (Out of Memory),
из-за чего ядро начинает переносить страницы рабочей памяти на диск, чтобы
освободить место для новых страниц, а это может привести к сбоям.
Вы можете перейти к ошибкам страницы для отдельных процессов с помощью
команд ps, top и time. Используйте системную версию time (/usr/bin/time) вместо
встроенной в оболочку. Далее приведен простой пример того, как команда time отображает ошибки страницы (вывод команды cal сейчас не имеет значения, поэтому
мы отбрасываем его, перенаправляя в /dev/null):
$ /usr/bin/time cal > /dev/null
0.00user 0.00system 0:00.06elapsed 0%CPU (0avgtext+0avgdata 3328maxresident)k
648inputs+0outputs (2major+254minor)pagefaults 0swaps

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

258   Глава 8. Процессы и использование ресурсов
Если вы предпочитаете видеть ошибки страниц процессов по мере их выполнения,
используйте команды top или ps. При запуске top примените параметр f для изменения отображаемых полей и выберите nMaj в качестве одного из столбцов, чтобы
отобразить количество серьезных ошибок страницы. Столбец vMj (количество
серьезных ошибок страницы с момента последнего обновления) полезен, если вы
пытаетесь отследить процесс, который, предположительно, работает неправильно.
Для команды ps можете использовать специальный формат вывода, чтобы просмотреть ошибки страницы для определенного процесса. Вот пример для процесса
PID 20365:
$ ps -o pid,min_flt,maj_flt 20365
PID MINFL MAJFL
20365 834182
23

В столбцах MINFL и MAJFL показано количество незначительных и серьезных ошибок
страницы. Конечно, вы можете объединить это с любыми другими вариантами выбора процесса, как описано на странице руководства ps(1).
Просмотр ошибок страниц по процессам может помочь вам сосредоточиться на
определенных проблемных компонентах. Но если вас интересует производительность системы в целом, потребуется инструмент для обобщения действий процессора и памяти во всех процессах.

8.5.5. Мониторинг производительности процессора и памяти
с помощью команды vmstat
Среди множества инструментов, доступных для мониторинга производительности
системы, команда vmstat является одной из старейших, и ее применение влечет за
собой минимальные накладные расходы. Она позволяет получить высокоуровневое
представление о том, как часто ядро подкачивает страницы, насколько занят процессор и как используются ресурсы ввода-вывода.
Чтобы раскрыть возможности команды vmstat, требуется понимать ее вывод. Например, вот вывод vmstat 2, который сообщает статистику каждые 2 секунды:
$ vmstat 2
procs -----------memory---------- ---swap-r b
swpd
free
buff cache
si
so
2 0 320416 3027696 198636 1072568
0
2 0 320416 3027288 198636 1072564
0
1 0 320416 3026792 198640 1072572
0
0 0 320416 3024932 198648 1074924
0
0 0 320416 3024932 198648 1074968
0
0 0 320416 3026800 198648 1072616
0

-----io---- -system-- ----cpu---bi
bo
in
cs us sy id wa
0
1
1
2
0 15 2 83
0
0 1182 407 636 1 0 99
0
0
58 281 537 1 0 99
0
0
308 318 541 0 0 99
0
0
0 208 416 0 0 99
0
0
0 207 389 0 0 100

0
0
0
1
0

0

Вывод подразделяется на категории: procs для процессов, memory для использования памяти, swap для страниц, перемещаемых в область подкачки и обратно, io для

8.5. Введение в мониторинг ресурсов   259
применения диска, system для количества переключений ядра на код ядра и cpu для
времени, затраченного различными частями системы.
Приведенный вывод типичен для системы, которая выполняет небольшое количество задач. Стоит начинать изучение с просмотра второй строки вывода — в первой
приведены средние значения за все время работы системы. Например, в примере система имеет 320 416 Кбайт памяти, перенесенной на диск (swpd), и около
3 027 000 Кбайт (3 Гбайт) реальной памяти free. Несмотря на то что используется
некоторое пространство подкачки, столбцы si с нулевым значением (swap-in, подкачка в) и so (swap-out, подкачка из) сообщают, что ядро в настоящее время ничего
не подкачивает с диска или на диск. Столбец buff указывает объем памяти, который
ядро использует для дисковых буферов (см. подраздел 4.2.5).
В крайнем правом углу, под заголовком CPU, вы можете увидеть распределение процессорного времени в столбцах us, sy, id и wa. Соответственно, в них указан процент
времени, которое процессор тратит на пользовательские задачи, системные задачи
(ядра), время простоя (idle) и ожидание (waiting) ввода-вывода. В предыдущем
примере запущено не так уж много пользовательских процессов (они используют
максимум 1 % времени процессора), ядро практически ничего не делает, а процессор
бездельничает 99 % времени.
В листинге 8.3 показано, что происходит при запуске большой программы.
Листинг 8.3. Активность памяти
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---r b
swpd
free
buff cache
si
so
bi
bo
in
cs us sy id wa
1 0 320412 2861252 198920 1106804
0
0
0
0 2477 4481 25 2 72 0 ❶
1 0 320412 2861748 198924 1105624
0
0
0
40 2206 3966 26 2 72 0
1 0 320412 2860508 199320 1106504
0
0
210
18 2201 3904 26 2 71 1
1 1 320412 2817860 199332 1146052
0
0 19912
0 2446 4223 26 3 63 8
2 2 320284 2791608 200612 1157752
202
0 4960
854 3371 5714 27 3 51 18 ❷
1 1 320252 2772076 201076 1166656
10
0 2142 1190 4188 7537 30 3 53 14
0 3 320244 2727632 202104 1175420
20
0 1890
216 4631 8706 36 4 46 14

Как здесь показано, процессор ❶ начинает применяться в течение длительного
периода, особенно в пользовательских процессах. Поскольку свободной памяти
достаточно, объем используемого кэша и буферного пространства начинает расти
по мере того, как ядро все больше задействует диск.
Позже мы увидим кое-что интересное: обратите внимание на то, что ядро ❷ извлекает в память некоторые страницы из области подкачки (столбец si). Это означает,
что только что запущенная программа, вероятно, получила доступ к некоторым
страницам, используемым совместно с другим процессом, что является обычным
явлением, так как многие процессы задействуют код в определенных разделяемых
библиотеках только при запуске.
Также обратите внимание: в столбце b несколько процессов заблокированы (blocked,
запрещены к запуску) во время ожидания страниц памяти. В целом объем свободной

260   Глава 8. Процессы и использование ресурсов
памяти уменьшается, но она еще далеко не исчерпана. Также наблюдается значительная активность на диске, о чем свидетельствуют увеличивающиеся числа
в столбцах bi (blocks in, входящие блоки) и bo (blocks out, выходящие блоки).
Вывод получается совсем другим, когда у вас заканчивается память. По мере истощения свободного пространства размеры буфера и кэша уменьшаются, поскольку
ядру требуется все больше пространства для пользовательских процессов. Как
только ничего не останется, вы увидите активность в столбце so (swapped-out,
выходящая подкачка), когда ядро начнет перемещать страницы на диск, и в этот
момент почти все остальные столбцы вывода изменятся, чтобы отразить объем
работы, выполняемой ядром. Вы увидите больше системного времени, больше
данных, поступающих на диск и выходящих с него, и больше заблокированных
процессов, потому что память, которую они хотят задействовать, недоступна (она
была перемещена в область подкачки).
Мы не изучили все столбцы вывода команды vmstat. Можете углубиться в эту тему
на странице руководства vmstat(8), но сначала стоит узнать больше об управлении памятью ядра из книги Зильбершаца, Гагне и Гэлвина (Silberschatz, Gagne and
Galvin’s) Operating System Concepts, 10-е издание (Wiley, 2018), чтобы понять их.

8.5.6. Мониторинг ввода-вывода I/O
По умолчанию команда vmstat предоставляет общую статистику ввода-вывода
I/O (Input/Output). Хотя вы можете получить очень подробную информацию
об использовании ресурсов для каждого дискового раздела с помощью команды
vmstat -d, вас может поразить размер вывода, полученного в результате применения
этого параметра. Вместо этого попробуйте инструмент только для мониторинга
ввода-вывода I/O под названием iostat.
ПРИМЕЧАНИЕ
Многие утилиты ввода-вывода, которые мы здесь обсудим, по умолчанию не встроены
в большинство дистрибутивов, но их легко можно установить.

Использование команды iostat
Как и в случае с командой vmstat, при запуске без каких-либо параметров iostat
отображает статистику за время работы вашей машины:
$ iostat
[информация о ядре]
avg-cpu: %user
%nice %system %iowait %steal
4.46
0.01
0.67
0.31
0.00
Device:
sda
sde

tps
4.67
0.00

kB_read/s
7.28
0.00

kB_wrtn/s
49.86
0.00

%idle
94.55
kB_read
9493727
1230

kB_wrtn
65011716
0

8.5. Введение в мониторинг ресурсов   261
Часть avg-cpu, расположенная вверху, сообщает ту же информацию об использовании процессора, что и другие утилиты, которые вы видели ранее в этой главе,
поэтому обратите внимание на нижнюю часть, где отображена следующая информация для каждого устройства:
tps — среднее количество передач данных в секунду;
kB_read/с — среднее количество считываемых килобайт в секунду;
kB_wrtn/с — среднее количество записываемых килобайт в секунду;
kB_read — общее количество прочитанных килобайт;
kB_wrtn — общее количество записанных килобайт.

Сходство с vmstat заключается еще и в том, что вы можете предоставить аргумент
интервала, например iostat 2, для обновления каждые 2 секунды. При использовании интервала может потребоваться отобразить только отчет об устройстве
с помощью параметра -d (например, iostat -d 2).
По умолчанию в выводе iostat отсутствует информация о разделах диска. Чтобы
отобразить ее, добавьте параметр -p ALL. Поскольку в типичной системе много разделов, вы получите много выходных данных. Вот часть того, что можно увидеть:
$ iostat -p ALL
--пропуск-Device:
--пропуск-sda
sda1
sda2
sda5
scd0
--пропуск-sde

tps

kB_read/s

kB_wrtn/s

kB_read

kB_wrtn

4.67
4.38
0.00
0.01
0.00

7.27
7.16
0.00
0.11
0.00

49.83
49.51
0.00
0.32
0.00

9496139
9352969
6
141884
0

65051472
64635440
0
416032
0

0.00

0.00

0.00

1230

0

В этом примере sda1, sda2 и sda5 являются разделами диска sda, поэтому столбцы
чтения и записи отчасти пересекаются. Однако сумма столбцов разделов не обязательно будет равна значению в столбце диска. Хотя чтение из sda1 также считается
чтением из sda, имейте в виду, что можно читать из sda напрямую, например при
чтении таблицы разделов.

Мониторинг использования ввода-вывода для каждого процесса с помощью
команды iotop
Если вы хотите копнуть еще глубже, чтобы увидеть ресурсы ввода-вывода, задействуемые отдельными процессами, изучите инструмент iotop. Команда iotop
используется аналогично команде top. Она генерирует постоянно обновляемый
дисплей, на котором отображаются процессы, задействующие наибольшее количество ресурсов ввода-вывода, с общей сводкой вверху:

262   Глава 8. Процессы и использование ресурсов
# iotop
Total DISK
TID PRIO
260 be/3
2611 be/4
2636 be/4
1329 be/4
6845 be/4
19069 be/4

READ:
USER
root
juser
juser
juser
juser
juser

4.76 K/s | Total DISK
DISK READ DISK WRITE
0.00 B/s
38.09 K/s
4.76 K/s
10.32 K/s
0.00 B/s
84.12 K/s
0.00 B/s
65.87 K/s
0.00 B/s 812.63 B/s
0.00 B/s 812.63 B/s

WRITE:
SWAPIN
0.00 %
0.00 %
0.00 %
0.00 %
0.00 %
0.00 %

333.31 K/s
IO>
COMMAND
6.98 % [jbd2/sda1-8]
0.21 % zeitgeist-daemon
0.20 % zeitgeist-fts
0.03 % soffice.b~ash-pipe=6
0.00 % chromium-browser
0.00 % rhythmbox

Обратите внимание, что наряду со столбцами user, command и read/write вместо
столбца PID появился столбец TID. Инструмент iotop — одна из немногих утилит,
которая отображает потоки вместо процессов.
Столбец PRIO (приоритет) показывает приоритет ввода-вывода. Он похож на прио­
ритет процессора, который мы встречали ранее, но влияет на то, как быстро ядро
планирует для процесса операции ввода-вывода для чтения и записи. В приоритете,
таком как be/4, часть be является классом планирования, а номер — уровнем приоритета. Как и в случае с приоритетами CPU, меньшие числа более важны, например,
ядро позволяет дать процессу с приоритетом be/3 больше времени ввода-вывода,
чем процессу с приоритетом be/4.
Ядро использует класс планирования, чтобы обеспечить больший контроль планирования ввода-вывода. Существует три класса планирования из iotop:
be — наибольшая эффективность (Best Effort). Ядро делает все возможное,

чтобы справедливо распланировать ввод-вывод для этого класса. Большинство процессов выполняются в этом классе планирования ввода-вывода;
rt — в реальном времени (Real Time). Ядро планирует любой ввод-вывод

в реальном времени перед любым другим классом ввода-вывода, несмотря
ни на что;

idle — ядро выполняет ввод-вывод для этого класса только тогда, когда нет

других операций ввода-вывода, которые необходимо выполнить. Этот класс
планирования не имеет уровня приоритета.

Вы можете проверить и изменить приоритет ввода-вывода для процесса с помощью
утилиты ionice; подробную информацию см. на странице руководства ionice(1).
Однако вам, вероятно, никогда не придется беспокоиться о приоритете ввода-вывода.

8.5.7. Мониторинг каждого процесса с помощью команды pidstat
Вы видели, как можно отслеживать определенные процессы с помощью утилит
top и iotop. Однако со временем дисплей вывода обновляется, и каждое обновление удаляет предыдущий вывод. Утилита pidstat позволяет видеть потребление
ресурсов процессом с течением времени в стиле vmstat. Вот простой пример для
процесса мониторинга 1329, обновляемого каждую секунду:

8.6. Группы управления (cgroups)   263
$ pidstat -p 1329 1
Linux 5.4.0-48-generic (duplex)
09:26:55
09:27:03
09:27:04
09:27:05
09:27:06
09:27:07
09:27:08

PM
PM
PM
PM
PM
PM
PM

UID
1000
1000
1000
1000
1000
1000

PID
1329
1329
1329
1329
1329
1329

11/09/2020

%usr %system %guest
8.00
0.00
0.00
0.00
0.00
0.00
3.00
0.00
0.00
8.00
0.00
0.00
2.00
0.00
0.00
6.00
0.00
0.00

_x86_64_
%CPU
8.00
0.00
3.00
8.00
2.00
6.00

CPU
1
3
1
3
3
2

(4 CPU)

Command
myprocess
myprocess
myprocess
myprocess
myprocess
myprocess

Вывод по умолчанию показывает процентное соотношение пользовательского
и системного времени, общий процент времени процессора и даже сообщает, на
каком процессоре выполнялся процесс. (Столбец %guest здесь несколько странный — это процент времени, в течение которого процесс выполнял что-то внутри
виртуальной машины. Если вы не работаете с виртуальной машиной, не обращайте
на него внимания.)
Хотя pidstat по умолчанию показывает использование процессора, он способен
отобразить гораздо больше полезной информации. Например, вы можете использовать параметр -r для мониторинга памяти и параметр -d для включения мониторинга диска. Попробуйте применить их, а затем просмотрите страницу руководства
pidstat(1), чтобы увидеть еще больше возможностей для потоков, переключения
контекста или всего того, о чем мы говорили в этой главе.

8.6. Группы управления (cgroups)
До сих пор вы видели, как просматривать и отслеживать использование ресурсов.
А если вы хотите ограничить количество ресурсов, которые могут потребляться сверх
того, что было показано с помощью команды nice? Для этого существует несколько
традиционных систем, таких как интерфейс POSIX rlimit, но наиболее гибким вариантом для большинства типов ограничений ресурсов в системах Linux в настоящее
время является функция ядра cgroup (control group — группа управления).
Основная идея заключается в следующем: вы помещаете несколько процессов
в группу, что позволяет управлять ресурсами, которые они потребляют, в масштабах всей группы. Например, ограничить объем памяти, который совокупно может
потреблять набор процессов, способна cgroup.
После создания cgroup вы можете добавить в нее процессы, а затем использовать контроллер, чтобы изменить поведение этих процессов. Например, существует контроллер cpu, позволяющий ограничивать процессорное время, контроллер memory и т. д.
ПРИМЕЧАНИЕ
Хотя systemd широко применяет функцию cgroup и большинство (если не все) групп
в вашей системе могут управляться systemd, группы находятся в пространстве ядра
и не зависят от systemd.

264   Глава 8. Процессы и использование ресурсов

8.6.1. Различие между версиями cgroup
Существуют две версии cgroup, 1 и 2, к сожалению, в настоящее время обе они используются и могут быть настроены в системе одновременно, что может привести
к путанице. Версии имеют различный набор функций, и структурные различия
между ними можно суммировать следующим образом:
В cgroups v1 каждый тип контроллера (cpu, memory и т. д.) имеет собственный
набор cgroups. Процесс может принадлежать одной группе на контроллер, то
есть он может принадлежать нескольким группам. Например, в v1 процесс
может принадлежать группам cpu и memory.
В cgroups v2 процесс может принадлежать только одной группе cgroup. Вы
можете настроить различные типы контроллеров для каждой группы.
Чтобы визуализировать разницу, рассмотрим три набора процессов: A, B и C. Мы
хотим использовать контроллеры cpu и memory для каждого из них. На рис. 8.1
показана схема для cgroups v1. Всего нам нужны шесть групп, потому что каждая
ограничена одним контроллером.

Рис. 8.1. cgroups v1. Процесс может принадлежать одной группе на контроллер
На рис. 8.2 показано, как это происходит в cgroups v2. Нам нужны только три
группы, потому что для каждой из них можно настроить несколько контроллеров.
Вы можете перечислить контрольные группы v1 и v2 для любого процесса, просмот­
рев его файл cgroup в /proc/. Начните с просмотра cgroups вашей оболочки
с помощью следующей команды:
$ cat /proc/self/cgroup
12:rdma:/
11:net_cls,net_prio:/

8.6. Группы управления (cgroups)   265
10:perf_event:/
9:cpuset:/
8:cpu,cpuacct:/user.slice
7:blkio:/user.slice
6:memory:/user.slice
5:pids:/user.slice/user-1000.slice/session-2.scope
4:devices:/user.slice
3:freezer:/
2:hugetlb:/testcgroup ❶
1:name=systemd:/user.slice/user-1000.slice/session-2.scope
0::/user.slice/user-1000.slice/session-2.scope

Рис. 8.2. cgroups v2. Процесс может принадлежать только одной группе
Не пугайтесь, если вывод в вашей системе значительно короче, — это просто означает, что у вас, вероятно, есть только группы управления cgroups v2. Каждая строка
вывода в примере начинается с номера и представляет собой отдельную группу. Вот
несколько советов по поводу того, как читать такой вывод:
Числа 2-12 относятся к cgroups v1. Контроллеры для них перечислены рядом
с номером.
Номер 1 также относится к версии 1, но в нем нет контроллера. Эта группа
предназначена только для целей управления (в данном случае ее настроил
systemd).
Последняя строка, номер 0, предназначена для cgroup v2. Здесь нет никаких
контроллеров. В системе, в которой нет групп управления v1 , это будет
единственная строка вывода.
Имена являются иерархическими и выглядят как части путей к файлам.
В этом примере вы можете видеть, что некоторые группы называются /user.
slice, а другие — /user.slice/user-1000.slice/session-2.scope.
Имя /testcgroup (❶) было создано, чтобы показать, что в группах управления версии 1 группы управления для процесса могут быть полностью
независимыми.

266   Глава 8. Процессы и использование ресурсов
Имена в разделе user.slice, включающие сеанс session, являются сеансами
входа в систему, назначенными systemd. Вы увидите их, когда будете просматривать группы управления оболочки. Группы управления для ваших
системных служб будут находиться в разделе system.slice.
Можно предположить, что cgroups v1 по сравнению с v2 более гибки лишь в том,
что позволяют назначать процессам различные комбинации контрольных групп.
Но на самом деле никто не использует их таким образом, и этот подход был
более сложным в настройке и реализации, чем просто наличие одной группы
на процесс.
Поскольку cgroups v1 постепенно выходят из употребления, с этого момента обсуждение будет сосредоточено на cgroups v2. Имейте в виду, что, если контроллер
задействуется в контрольных группах v1, он не может одновременно использоваться в v2 из-за потенциальных конфликтов. Это означает, что части, зависящие от
контроллера, которые мы собираемся обсудить, не будут работать правильно, если
ваша система все еще применяет версию 1, но вы все равно сможете попробовать
использовать ее, изучив похожие настройки.

8.6.2. Просмотр cgroups
В отличие от традиционного интерфейса системных вызовов Unix для взаимодействия с ядром, доступ к cgroups осуществляется полностью через файловую
систему, которая обычно монтируется как файловая система cgroup2 в /sys/fs/
cgroup. (Если вы используете также cgroups v1, она, вероятно, будет находиться
в /sys/fs/cgroup/unified.)
Давайте рассмотрим настройку cgroup оболочки. Откройте оболочку и найдите
ее cgroup в /proc/self/cgroup, как показано ранее. Затем загляните в /sys/fs/
cgroup или /sys/fs/cgroup/unified. Найдя каталог с таким именем, перейдите
в него и изучите:
$ cat /proc/self/cgroup
0::/user.slice/user-1000.slice/session-2.scope
$ cd /sys/fs/cgroup/user.slice/user-1000.slice/session-2.scope/
$ ls

ПРИМЕЧАНИЕ
Имя cgroup может быть довольно длинным в средах рабочего стола, которые любят
создавать новую группу для каждого вновь запущенного приложения.
Среди множества файлов, которые могут находиться в этом каталоге, основные
файлы интерфейса cgroup выделяются тем, что начинаются с cgroup. Начните
с просмотра cgroup.procs (можно использовать команду cat), в котором перечислены процессы в cgroup. Аналогичный файл, cgroup.threads, содержит также
потоки.

8.6. Группы управления (cgroups)   267
Чтобы увидеть контроллеры, применяемые в настоящее время для группы, посмотрите на cgroup.controllers:
$ cat cgroup.controllers
memory pids

Большинство групп управления, применяемых для оболочек, имеют эти два контроллера, которые могут управлять объемом используемой памяти и общим количеством процессов в группе. Чтобы взаимодействовать с контроллером, найдите
файлы, соответствующие префиксу контроллера. Например, если хотите увидеть
количество потоков, запущенных в контрольной группе, обратитесь к pids.current:
$ cat pids.current
4

Чтобы увидеть максимальный объем памяти, который может потреблять группа,
взгляните на memory.max:
$ cat memory.max
max

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

8.6.3. Управление и создание cgroups
Хотя вам, вероятно, никогда не понадобится изменять группы управления, делать
это легко. Чтобы поместить процесс в cgroup, запишите его PID в файл cgroup.procs
от имени суперпользователя:
# echo pid > cgroup.procs

Именно так работают многие изменения в группах. Например, если вы хотите
ограничить максимальное количество PID в группе (скажем, до 3000), сделайте это
следующим образом:
# echo 3000 > pids.max

Создавать группы управления сложнее. Технически это так же просто, как создать
подкаталог где-нибудь в дереве cgroup, при этом ядро автоматически создает файлы
интерфейса. Если в cgroup нет процессов, вы можете удалить ее с помощью команды
rmdir даже при наличии файлов интерфейса. Что может сбить вас с толку, так это
правила, регулирующие группы.
Вы можете помещать процессы только в группы управления внешнего
уровня (конечные). Например, если у вас есть группы с именами /my-cgroup
и /my-cgroup/my-subgroup, вы не можете поместить процессы в /my-cgroup —

268   Глава 8. Процессы и использование ресурсов
подойдет вариант /my-cgroup/my-subgroup. (Исключение составляет случай, когда в контрольных группах нет контроллеров, но давайте не будем
углубляться.)
В группе не может быть контроллера, который не входит в ее родительскую
группу.
Вы должны явно указать контроллеры для дочерних контрольных групп. Это
делается с помощью файла cgroup.subtree_control: например, если хотите,
чтобы дочерняя группа имела контроллеры cpu и pid, напишите +cpu +pid
в этот файл.
Исключением из этих правил является корневая контрольная группа, расположенная в нижней части иерархии. Можно поместить в нее процессы. Одна из
причин, по которой вы захотите это сделать, — возможность отключить процесс
от управления systemd.

8.6.4. Отображение использования ресурсов
В дополнение к возможности ограничения ресурсов по группам вы можете видеть,
какие ресурсы используются в данный момент всеми процессами в их группах. Даже
без включенных контроллеров можно увидеть нагрузку CPU группы, просмотрев
ее файл cpu.stat:
$ cat cpu.stat
usage_usec 4617481
user_usec 2170266
system_usec 2447215

Поскольку это накопленная нагрузка процессора за весь срок работы cgroup, можно видеть, как служба потребляет процессорное время, даже если она порождает
множество подпроцессов, которые в конечном итоге завершаются.
Вы можете просмотреть другие типы использования, если включены соответствующие контроллеры. Например, контроллер memory предоставляет доступ к файлу
memory.current для текущего использования памяти и файлу memory.stat, содержащему подробные данные о памяти за весь срок службы группы. Эти файлы
недоступны в корневой контрольной группе.
И это еще не все. Полная информация о том, как применять каждый отдельный
контроллер, а также все правила создания контрольных групп имеются в документации ядра: поискав в интернете «документацию cgroups2», вы найдете множество
дополнительной информации.
На данный момент, однако, вы должны хорошо представлять, как работают группы. Понимание основ их работы помогает объяснить, как системы организуют
процессы. Позже, прочитав о контейнерах, вы увидите, как они используются для
совершенно других целей.

8.7. Дополнительно   269

8.7. Дополнительно
Одна из причин, по которой существует так много инструментов для измерения
ресурсов и управления их использованием, заключается в том, что различные типы
ресурсов потребляются по-разному. В этой главе мы рассмотрели процессор, память
и ввод-вывод как системные ресурсы, потребляемые процессами, потоками внутри
процессов и ядром.
Другая причина заключается в том, что ресурсы ограничены, и для того чтобы
система работала хорошо, ее компоненты должны стремиться потреблять меньше
ресурсов. В прошлом многие пользователи работали на общей машине, поэтому
необходимо было убедиться, что ресурсы разделены между ними поровну. На современном настольном компьютере может и не быть нескольких пользователей,
однако на нем все еще существует множество процессов, конкурирующих за ресурсы. Аналогично, высокопроизводительные сетевые серверы требуют интенсивного
мониторинга системных ресурсов, поскольку на них выполняется множество процессов для одновременной обработки нескольких запросов.
Дополнительные темы в области мониторинга ресурсов и анализа производительности, которые вы, возможно, захотите изучить:
sar (System Activity Reporter). Пакет sar обладает множеством возможностей
непрерывного мониторинга vmstat, а также регистрирует использование
ресурсов с течением времени. С помощью sar вы можете вернуться назад

в определенное время, чтобы увидеть, что делала ваша система. Это удобно,
когда нужно проанализировать системное событие, произошедшее в прошлом;
acct (process accounting, учет процессов). Пакет acct может записывать про-

цессы и использование ими ресурсов;

Quotas. Вы можете ограничить объем дискового пространства, которое пользователь может задействовать с системой quota.

Если вас интересует настройка систем и, в частности, производительность, в книге
Брендана Грегга (Brendan Gregg) Systems Performance: Enterprise and the Cloud, 2-е издание (Addison-Wesley, 2020), вы найдете гораздо больше деталей.
Мы также еще не коснулись множества инструментов, с помощью которых можно
выполнять мониторинг использования сетевых ресурсов. Однако, прежде чем делать это, вам нужно понять, как работает сеть. Об этом мы поговорим далее.

9

Сеть и ее конфигурация

Сеть — это способ подключения компьютеров и передачи
данных между ними. Звучит довольно просто, но, чтобы
понять, как это работает, нужно задать два фундаментальных
вопроса:
Как компьютер, отправляющий данные, узнает, куда отправлять свои данные?
Когда конечный компьютер получает данные, как он узнает, что он только
что получил?
Компьютер решает эти вопросы с помощью ряда компонентов, каждый из которых
отвечает за определенный аспект отправки, получения и идентификации данных.
Компоненты входят в группы, образующие сетевые уровни (network layers), расположенные один над другим и формирующие целостную систему. Ядро Linux
обрабатывает сеть аналогично подсистеме SCSI, описанной в главе 3.
Поскольку каждый уровень, как правило, независим, можно создавать сети с множеством различных комбинаций компонентов. Именно здесь конфигурация сети
может стать очень сложной. По этой причине мы начнем эту главу с рассмотрения
уровней в очень простых сетях. Вы узнаете, как просматривать свои сетевые настройки, и, когда поймете основные принципы работы каждого уровня, будете
готовы научиться настраивать их самостоятельно. Наконец, мы перейдем к более
сложным темам, таким как создание собственных сетей и настройка брандмауэров.
(Пропустите эту информацию, если перестанете что-либо понимать, — вы всегда
можете вернуться к ней позднее.)

9.2. Пакеты   271

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

Рис. 9.1. Типичная локальная сеть LAN с маршрутизатором,
обеспечивающим доступ в интернет
Этот тип сети распространен повсеместно, большинство домашних и небольших
офисных сетей настроены подобным образом. Каждая машина, подключенная
к сети, называется хостом. Одним из них является маршрутизатор, который
может перемещать данные из одной сети в другую. Здесь четыре узла — хосты A,
Б, В и маршрутизатор — образуют локальную сеть (Local Area Network, LAN).
Подключения в такой сети могут быть проводными или беспроводными. Строгого
определения локальной сети не существует, машины, входящие в нее, обычно физически близки и имеют почти одинаковые конфигурацию и права доступа. Вскоре
мы рассмотрим конкретный пример.
Маршрутизатор подключен к интернету — облачку на рисунке. Это соединение
называется восходящим каналом или подключением к глобальной сети (Wide Area
Network, WAN), так как оно соединяет гораздо меньшую локальную сеть LAN с более крупной сетью. Поскольку маршрутизатор подключен как к LAN, так и к интернету, через него все компьютеры, входящие в сеть, также имеют доступ к интернету.
Одна из целей главы — изучить, как маршрутизатор обеспечивает этот доступ.
Мы будем рассматривать процесс с компьютера на базе Linux, такого как хост A
в локальной сети на рис. 9.1.

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

272   Глава 9. Сеть и ее конфигурация
Хост может отправлять, получать и обрабатывать пакеты в любом порядке независимо от того, откуда они пришли или куда направляются, что позволяет нескольким
хостам обмениваться данными одновременно. Например, если хосту необходимо
передать данные двум другим в одно время, он может чередовать пункты назначения
в исходящих пакетах. Разбиение сообщений на более мелкие блоки также облегчает
обнаружение и компенсацию ошибок при передаче.
По большей части, вам не нужно беспокоиться о переводе между пакетами и данными, которые использует приложение, потому что операционная система делает
это сама. Однако полезно знать о роли пакетов в сетевых уровнях, которые мы
будем изучать далее.

9.3. Сетевые уровни
Полностью функционирующая сеть включает в себя набор сетевых уровней, называемых сетевым стеком. Любая функциональная сеть имеет стек. Типичный
интернет-стек, от верхнего до нижнего уровня, выглядит следующим образом.
Прикладной уровень (application layer). Содержит язык, который приложения и серверы используют для связи, обычно это какой-то протокол высокого
уровня. Общие протоколы прикладного уровня включают HTTP (Hypertext
Transfer Protocol, применяемый для интернета), протоколы шифрования,
такие как TLS, и протокол передачи файлов (File Transfer Protocol, FTP).
Протоколы прикладного уровня часто могут быть объединены. Например,
TLS обычно задействуется в сочетании с HTTP для формирования HTTPS.
Обработка прикладного уровня происходит в пространстве пользователя.
Транспортный уровень (transport layer). Определяет характеристики передачи данных прикладного уровня. Включает проверку целостности данных,
порты источника и назначения, а также спецификации для разбиения данных
приложения на пакеты на стороне хоста (если прикладной уровень еще этого
не сделал) и их повторной сборки в месте назначения. Протокол управления
передачей (TCP, Transmission Control Protocol) и протокол пользовательских
датаграмм (UDP, User Datagram Protocol) — наиболее распространенные протоколы транспортного уровня. Этот уровень иногда называют уровнем протокола.
В Linux транспортный уровень и все нижележащие уровни в основном обрабатываются ядром, но есть некоторые исключения, когда пакеты отправляются в пространство пользователя дляобработки.
Сетевой или межсетевой уровень (networkor internet layer). Определяет,
как перемещать пакеты с исходного хоста на конечный. Конкретный набор
правил межсетевой передачи пакетов известен как интернет-протокол
(Internet Protocol, IP). Так как в этой книге мы будем говорить только о межсетевых взаимодействиях, то будем иметь в виду только межсетевой уровень.
Но поскольку сетевые уровни должны быть аппаратно независимыми, вы

9.4. Сетевой уровень   273
можете одновременно настроить на одном хосте несколько независимых
сетевых уровней, таких как IP (IPv4), IPv6, IPX и AppleTalk.
Физический уровень. Определяет способ отправки необработанных данных
через физический носитель, такой как Ethernet или модем. Его иногда называют уровнем сетевого доступа или уровнем хост-сети.
Важно понимать структуру сетевого стека, потому что ваши данные должны пройти через его уровни по крайней мере дважды, прежде чем достигнут программы
в пункте назначения. Например, если вы отправляете данные с хоста A на хост Б
(см. рис. 9.1), байты покидают прикладной уровень на хосте A, проходят через его
транспортный и сетевой уровни, затем спускаются на физический носитель, пересекают его и почти таким же образом поднимаются через различные нижние уровни
на прикладной уровень на хосте Б. Если вы отправляете что-то хосту в другой сети
через маршрутизатор, посланное пройдет через некоторые (обычно не все) уровни
маршрутизатора и все расположенное между ними.
Уровни иногда перетекают друг в друга странным образом, потому что не всегда
эффективно обрабатывать их по порядку. Например, устройства, которые исторически имели дело только с физическим уровнем, теперь иногда просматривают
данные транспортного и сетевого уровней одновременно, чтобы быстро фильтровать
и маршрутизировать данные. Кроме того, сама терминология может сбивать с толку.
Например, TLS означает «безопасность транспортного уровня» (Transport Layer
Security), но на самом деле он находится на один уровень выше, на прикладном
уровне. (На первоначальном этапе изучения такие детали не важны.)
Мы начнем с рассмотрения того, как ваша машина Linux подключается к сети, чтобы
ответить на вопрос «где?» из начала главы. Это нижняя часть стека — физический
и сетевой уровни. Позже рассмотрим два верхних слоя, которые отвечают на вопрос «что?».
ПРИМЕЧАНИЕ
Возможно, вы слышали о другом наборе уровней, известном как модель взаимодействия
открытых систем (Open Systems Interconnection, OSI). Это семиуровневая сетевая модель,
часто используемая в обучении и проектировании сетей, но мы не станем ее рассматривать, потому что будем работать непосредственно с четырьмя уровнями, описанными
в книге. Чтобы узнать гораздо больше об уровнях и сетях в целом, см. книгу Эндрю С.
Таненбаума и Дэвида Дж. Уэзералла «Компьютерные сети», 5-е издание (Питер, 2019).

9.4. Сетевой уровень
Вместо того чтобы начать с самого низкого уровня стека, вначале займемся сетевым
уровнем, потому что он проще для понимания. Межсетевое взаимодействие, каким
мы его знаем в настоящее время, основано на версиях интернет-протокола 4 (IPv4)
и 6 (IPv6). Одним из наиболее важных аспектов сетевого уровня является то, что
он должен быть программной сетью, которая не предъявляет особых требований

274   Глава 9. Сеть и ее конфигурация
к оборудованию или операционным системам. Суть этого заключается в том, что
вы можете отправлять и получать сетевые пакеты через любое оборудование, используя любую операционную систему.
Начнем обсуждение с IPv4, потому что с ним немного легче читать адреса (и понимать их ограничения), а далее рассмотрим основные отличия от него IPv6.
Топология интернета децентрализована, он состоит из небольших сетей, называемых подсетями. Все подсети каким-то образом взаимосвязаны. Например, локальная сеть (см. рис. 9.1) обычно представляет собой одну подсеть.
Хост может быть подключен к нескольким подсетям. Как сказано ранее, он называется маршрутизатором, если может передавать данные из одной подсети
в другую (еще одно название маршрутизатора — шлюз). Рисунок 9.2 уточняет
рис. 9.1, идентифицируя LAN как подсеть, а также интернет-адреса для каждого
хоста и маршрутизатора. Маршрутизатор на рисунке имеет два адреса, локальную
подсеть 10.23.2.1 и канал связи с интернетом (его адрес сейчас неважен, поэтому
он просто помечен как адрес Uplink). Сначала рассмотрим адреса, а затем обозначения подсети.

Рис. 9.2. Сеть с IP-адресами
У каждого интернет-хоста есть по крайней мере один цифровой IP-адрес, для
IPv4 — в форме a.b.c.d, например 10.23.2.37. Адресом в этой записи называется
последовательность из четырех чисел, разделенных точками. Если хост подключен
к нескольким подсетям, у него есть по крайней мере один IP-адрес на подсеть.
IP-адрес каждого хоста должен быть уникальным во всем интернете, но как вы
увидите позже, частные сети и преобразование сетевых адресов (Network Address
Translation, NAT) могут запутать процесс.
Пока не беспокойтесь об обозначении подсети на рис. 9.2, мы обсудим его в ближайшее время.

9.4. Сетевой уровень   275
ПРИМЕЧАНИЕ
Технически IP-адрес состоит из 4 байт (или 32 бит), abcd. Байты a и d — это числа от 1 до
254, а b и c — числа от 0 до 255. Компьютер обрабатывает IP-адреса в виде необработанных байтов. Однако человеку гораздо проще читать и записывать адрес с точками,
такой как 10.23.2.37, вместо чего-то наподобие шестнадцатеричного адреса 0x0A170225.
IP-адреса в некотором смысле похожи на почтовые адреса. Для связи с другим
хостом ваша машина должна знать его IP-адрес.
Давайте взглянем на адрес вашего компьютера.

9.4.1. Просмотр IP-адреса
Одна машина может иметь множество IP-адресов, включающих несколько физических интерфейсов, виртуальных внутренних сетей и многое другое. Чтобы просмотреть адреса, активные на вашем компьютере с Linux, запустите следующую команду:
$ ip address show

Вероятно, она выведет много данных, сгруппированных по физическому интерфейсу, описанному в разделе 9.10, но они должны включать что-то вроде этого:
2: enp0s31f6: mtu 1500 qdisc fq_codel state
UP group default qlen 1000
link/ether 40:8d:5c:fc:24:1f brd ff:ff:ff:ff:ff:ff
inet 10.23.2.4/24 brd 10.23.2.255 scope global noprefixroute enp0s31f6
valid_lft forever preferred_lft forever

Вывод команды ip включает в себя множество деталей из сетевого уровня(-ей)
и физического уровня. (Иногда в нем даже нет интернет-адреса!) Подробнее мы
обсудим вывод позже, а сейчас сосредоточимся на четвертой строке, в которой
сообщается, что хост имеет IPv4-адрес (обозначается inet) 10.23.2.4. Значение
/24 после адреса помогает определить подсеть, к которой принадлежит IP-адрес.
Давайте рассмотрим, как это работает.
ПРИМЕЧАНИЕ
Команда ip — стандартный инструмент настройки сети. В другой документации вы можете
встретить ifconfig. Эта устаревшая команда, которая использовалась в других версиях
Unix в течение десятилетий, но теперь она менее эффективна. Чтобы соответствовать современной рекомендуемой практике (и дистрибутивам, которые могут даже не включать
ifconfig по умолчанию), мы будем применять команду ip. Также существуют команды,
которые могут заменить ip, — это route и arp.

9.4.2. Подсети
Подсеть представляет собой подключенную группу хостов с IP-адресами в определенном диапазоне. Например, хосты в диапазоне от 10.23.2.1 до 10.23.2.254 могут

276   Глава 9. Сеть и ее конфигурация
включать подсеть, как и все хосты в диапазоне от 10.23.1.1 до 10.23.255.254. Обычно
хосты подсети находятся в одной физической сети, как показано на рис. 9.2.
Подсеть определяется на основе двух частей: префикса сети (также называемого
префиксом маршрутизации) и маски подсети (иногда называемой маской сети или
маской маршрутизации). Допустим, вы хотите создать подсеть, содержащую IPадреса между 10.23.2.1 и 10.23.2.254. Сетевой префикс — это часть, общая для всех
адресов в подсети, в данном примере это 10.23.2.0 с маской подсети 255.255.255.0.
Давайте посмотрим, откуда взялись эти цифры.
Чтобы увидеть, как префикс и маска работают вместе, предоставляя вам все возможные IP-адреса в подсети, рассмотрим двоичную форму. Маска отмечает битовые
местоположения в IP-адресе, которые являются общими для подсети. Например,
вот двоичные формы 10.23.2.0 и 255.255.255.0:
10.23.2.0:
255.255.255.0:

00001010 00010111 00000010 00000000
11111111 11111111 11111111 00000000

Теперь выделим жирным шрифтом расположение битов в 10.23.2.0, которые равны
1 в 255.255.255.0:
10.23.2.0:

00001010 00010111 00000010 00000000

Любой адрес, содержащий конфигурацию битов, выделенную жирным шрифтом,
находится в подсети. Для битов, не выделенных жирным шрифтом (последний
набор из восьми нулей), установка 1 на любую позицию приводит к получению
действительного IP-адреса в этой подсети, за исключением случаев, когда все биты
являются 0 или 1.
Собрав все это вместе, вы можете увидеть, что хост с IP-адресом 10.23.2.1 и маской
подсети 255.255.255.0 находится в той же подсети, к которой относится любой
другой компьютер, IP-адрес которого начинается с 10.23.2. Можно обозначить эту
подсеть 10.23.2.0/255.255.255.0.
Теперь посмотрим, как эти данные перевести в сокращенную нотацию (например,
в /24), которую мы встречали в выводе команды ip.

9.4.3. Распространенные маски подсети и нотация CIDR
В большинстве сетевых инструментов вы столкнетесь с другой формой представления подсети, называемой бесклассовой междоменной маршрутизацией (Classless
Inter-Domain Routing, CIDR), где подсеть, такая как 10.23.2.0/255.255.255.0, обозначается 10.23.2.0/24. Это сокращение использует преимущества простого шаблона,
которому соответствуют маски подсети.
Посмотрите на маску в двоичной форме из примера, приведенного в предыдущем
разделе. Все маски подсети являются (или должны быть согласно RFC 1812) только
одним блоком из единиц, за которым следует один блок из нулей. Например, мы

9.5. Маршруты и таблица маршрутизации ядра   277
знаем, что 255.255.255.0 в двоичной форме состоит из 24 бит единиц, за которыми
следуют 8 бит нулей. Адресация CIDR идентифицирует маску подсети по количеству ведущих единиц в ней. Поэтому комбинация 10.23.2.0/24 включает как
префикс подсети, так и ее маску подсети.
В табл. 9.1 приведены несколько примеров масок подсети и их форм CIDR. Маска
подсети /24 наиболее широко распространена в локальных сетях конечных пользователей, она часто применяется в сочетании с одной из частных сетей, которые
вы увидите в разделе 9.22.
Таблица 9.1. Маски подсети
Длинная форма

Форма CIDR

255.0.0.0

/8

255.255.0.0

/16

255.240.0.0

/12

255.255.255.0

/24

255.255.255.192

/26

ПРИМЕЧАНИЕ
Если вы не знакомы с преобразованием между десятичным, двоичным и шестнадцатеричным форматами, используйте утилиту калькулятора bc или dc для преобразования
между различными представлениями систем счисления. Например, в bc можно выполнить
команду obase=2; 240, чтобы набрать число 240 в двоичной форме (основание 2).
Вы, возможно, уже заметили, что если у вас есть IP-адрес и маска подсети, то даже
не нужно беспокоиться об отдельном определении сети. Можете объединить их,
как показано в подразделе 9.4.1, вывод ip address show включает 10.23.2.4/24.
Определение подсетей и их хостов — это первый строительный блок для понимания
того, как работает интернет. Однако вам все равно необходимо соединить подсети.

9.5. Маршруты и таблица маршрутизации ядра
Подключение интернет-подсетей — это в основном процесс отправки данных через
хосты, подключенные к нескольким подсетям. Возвращаясь к рис. 9.2, рассмотрите
хост A по IP-адресу 10.23.2.4.
Этот хост подключен к локальной сети 10.23.2.0/24 и может напрямую связываться
с хостами в ней. Чтобы связаться с хостами в остальной части интернета, он должен
взаимодействовать через маршрутизатор в 10.23.2.1.
Ядро Linux различает эти два разных типа назначений, задействуя таблицу маршрутизации для определения поведения маршрутизации. Чтобы отобразить таблицу

278   Глава 9. Сеть и ее конфигурация
маршрутизации, используйте команду ip route show. Вот что вы можете увидеть
для простого хоста, такого как 10.23.2.4:
$ ip route show
default via 10.23.2.1 dev enp0s31f6 proto static metric 100
10.23.2.0/24 dev enp0s31f6 proto kernel scope link src 10.23.2.4 metric 100

ПРИМЕЧАНИЕ
Традиционным инструментом для просмотра маршрутов является команда route, выполняемая как route -n. Параметр -n указывает route показывать IP-адреса, вместо
того, чтобы пытаться показывать имена хостов и сетей. Это важный параметр, который
следует запомнить, потому что его можно использовать и в других командах, связанных
с сетью, таких как netstat.
Этот вывод нелегко прочитать. Каждая строка является правилом маршрутизации;
давайте начнем со второй строки примера и разобьем ее на части.
Первая часть 10.23.2.0/24 является сетью назначения. Как и в предыдущих примерах, это локальная подсеть хоста. Данное правило гласит, что хост может связаться
с локальной подсетью напрямую через свой сетевой интерфейс, обозначенный
меткой механизма dev enp0s31f6 после пункта назначения. (После этого поля приводится более подробная информация о маршруте, в том числе о том, как он был
создан. Вам не нужно беспокоиться об этом сейчас.)
Затем мы можем вернуться к первой строке вывода, в которой используется сеть
назначения default. Это правило, которое соответствует любому хосту вообще,
также называется маршрутом по умолчанию (изучим в следующем разделе). Механизм via 10.23.2.1 указывает, что трафик, использующий маршрут по умолчанию,
должен быть отправлен на адрес 10.23.2.1 (в нашем примере сети это маршрутизатор), dev enp0s31f6 указывает, что физическая передача будет происходить по
этому сетевому интерфейсу.

9.6. Шлюз по умолчанию
Запись default в таблице маршрутизации имеет особое значение, поскольку она
соответствует любому адресу в интернете. В адресации CIDR это 0.0.0.0/0 для
IPv4. Это маршрут по умолчанию, и адрес, настроенный в нем в качестве посредника, является шлюзом по умолчанию. Когда никакие другие правила не подходят,
маршрут по умолчанию всегда подходит, и шлюз по умолчанию — это место, откуда
вы отправляете сообщения, если нет другого варианта. Можете настроить хост без
шлюза по умолчанию, но он не сможет связаться с хостами за пределами пунктов
назначения в таблице маршрутизации.
В большинстве сетей с маской сети /24 (255.255.255.0) маршрутизатор обычно
находится по адресу 1 подсети (например, 10.23.2.1 в 10.23.2.0/24). Это просто договоренность, и могут быть исключения.

9.7. IPv6-адреса и сети   279

КАК ЯДРО ВЫБИРАЕТ МАРШРУТ
В маршрутизации есть одна хитрая деталь. Допустим, хост хочет отправить
что-то в 10.23.2.132, что соответствует обоим правилам в таблице маршрутизации: маршруту по умолчанию и 10.23.2.0/24. Как ядро узнает, что нужно
использовать второй маршрут? Порядок в таблице маршрутизации не имеет
значения, ядро выбирает самый длинный соответствующий префикс назначения. Вот где адресация CIDR особенно полезна: 10.23.2.0/24 соответствует,
и его префикс имеет длину 24 бита; 0.0.0.0/0 также соответствует, но его
префикс имеет длину 0 бит (то есть у него нет префикса), поэтому правило
для 10.23.2.0/24 становится приоритетным.

9.7. IPv6-адреса и сети
Если вы взглянете на раздел 9.4, то увидите, что IPv4-адреса состоят из 32 бит, или
4 байтов. Это дает в общей сложности примерно 4,3 млрд адресов, что недостаточно
для нынешних масштабов интернета. Появилось несколько проблем, вызванных
отсутствием адресов в IPv4, поэтому Инженерный совет интернета (Internet
Engineering Task Force, IETF) разработал версию IPv6. Прежде чем перей­ти к другим сетевым инструментам, обсудим адресное пространство IPv6.
Адрес IPv6 имеет 128 бит — 32 байта, расположенных в восьми наборах по 4 байта.
В длинной форме адрес записывается следующим образом:
2001:0db8:0a0b:12f0:0000:0000:0000:8b6e

Представление шестнадцатеричное, каждая цифра относится к диапазону от 0 до f.
Существует несколько широко распространенных методов сокращения представления. Во-первых, можно опустить все ведущие нули (например, 0db8 превращается
в db8), и один и только один набор смежных нулевых групп может быть представлен
как :: (два двоеточия). Поэтому предыдущий адрес записывается как
2001:db8:a0b:12f0::8b6e

Подсети по-прежнему обозначаются с помощью адресации CIDR. Для конечного
пользователя они часто охватывают половину доступных битов в адресном пространстве (/64), но бывают случаи, когда используется меньше. Часть адресного
пространства, уникальная для каждого хоста, называется идентификатором интерфейса. На рис. 9.3 показан пример адреса с 64-разрядной подсетью.
ПРИМЕЧАНИЕ
В этой книге мы, как правило, рассматриваем работу системы с точки зрения среднестатистического пользователя. Этот взгляд немного отличается от взгляда поставщика услуг,
где подсеть дополнительно разделяется на префикс маршрутизации и другой идентификатор сети, иногда также называемый подсетью. Сейчас об этом беспокоиться не стоит.

280   Глава 9. Сеть и ее конфигурация

Рис. 9.3. Идентификатор подсети и интерфейса типичного IPv6-адреса
Последнее, что нужно знать на данный момент об IPv6, — это то, что хосты обычно
имеют по крайней мере два адреса. Первый, который действителен в интернете,
называется глобальным индивидуальным адресом. Второй, для локальной сети, называется локальным адресом канала. Локальные адреса канала всегда имеют префикс fe80::/10, за которым следует 54-разрядный сетевой идентификатор с нулевым
значением, и заканчиваются 64-разрядным идентификатором интерфейса. То есть
локальный адрес канала в вашей системе будет находиться в подсети fe80::/64.
ПРИМЕЧАНИЕ
Глобальные индивидуальные адреса имеют префикс 2000::/3. Поскольку первый байт
начинается с 001 с этим префиксом, он может быть завершен как 0010 или 0011. Поэтому глобальный индивидуальный адрес всегда начинается с 2 или 3.

9.7.1. Просмотр конфигурации IPv6 в системе
Если у системы есть конфигурация IPv6, вы можете получить некоторую информацию об IPv6 из команды ip, которую мы запускали ранее. Чтобы выделить IPv6,
используйте параметр -6:
$ ip -6 address show
1: lo: mtu 65536 state UNKNOWN qlen 1000
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s31f6: mtu 1500 state UP qlen 1000
inet6 2001:db8:8500:e:52b6:59cc:74e9:8b6e/64 scope global dynamic noprefixroute
valid_lft 86136sec preferred_lft 86136sec
inet6 fe80::d05c:97f9:7be8:bca/64 scope link noprefixroute
valid_lft forever preferred_lft forever

В дополнение к интерфейсу loopback (о нем мы поговорим позже) вы можете увидеть еще два адреса. Глобальный индивидуальный адрес обозначается как scope
global, а локальный адрес канала получает метку scope link.
Просмотр маршрутов аналогичен:



$ ip -6 route show
::1 dev lo proto kernel metric 256 pref medium
2001:db8:8500:e::/64 dev enp0s31f6 proto ra metric 100 pref medium

9.8. Основные инструменты ICMP и DNS   281




fe80::/64 dev enp0s31f6 proto kernel metric 100 pref medium
default via fe80::800d:7bff:feb8:14a0 dev enp0s31f6 proto ra metric 100 pref
medium

Это немного сложнее, чем настройка IPv4, поскольку настроены как локальные, так
и глобальные подсети. Строка ❶ предназначена для адресатов в локально подключенных глобальных индивидуальных адресных подсетях: хост знает, что он может
связаться с ними напрямую и локальный канал связи ❷ аналогичен. Для маршрута ❸
по умолчанию (также записывается как ::/0 в IPv6, помните, что это все, что не связано
напрямую) эта конфигурация организует передачу трафика через маршрутизатор по
локальному адресу канала fe80::800d:7bff:feb8:14a0 вместо его адреса в глобальной
подсети. Позже вы увидите, что маршрутизатор обычно не заботится о том, как он
получает трафик, — только о том, куда должен идти. Использование локального
адреса канала в качестве шлюза по умолчанию имеет то преимущество, что его не
нужно менять при изменении глобального пространства IP-адресов.

9.7.2. Настройка сетей с двумя стеками
Как вы, возможно, уже догадались, можно настроить хосты и сети для применения
как IPv4, так и IPv6. Эту возможность называют сетью с двумя стеками (dual-stack
network), хотя в данном случае использовать слово «стек» не совсем точно, поскольку один уровень типичного сетевого стека попросту дублируется (истинный
двойной стек был бы чем-то вроде IP + IPX). Если отбросить условности, протоколы IPv4 и IPv6 независимы друг от друга и могут работать одновременно. На
таком хосте приложение (например, веб-браузер) может выбрать IPv4 или IPv6
для подключения к другому хосту.
Приложение, изначально написанное для IPv4, не поддерживает IPv6 автоматически. К счастью, поскольку в стеке уровни, расположенные поверх сетевого уровня,
не изменились, можно задействовать простой код, необходимый для коммуникации
по IPv6. Большинство важных приложений и серверов теперь поддерживают IPv6.

9.8. Основные инструменты ICMP и DNS
Теперь пришло время взглянуть на базовые практические утилиты, которые помогут вам взаимодействовать с хостами. Эти инструменты применяют два протокола,
представляющих особый интерес: протокол управления сообщениями в интернете
(Internet Control Message Protocol, ICMP), который может помочь устранить проблемы с подключением и маршрутизацией, и систему доменных имен (Domain Name
Service, DNS), которая сопоставляет имена с IP-адресами, чтобы пользователю
не приходилось запоминать кучу номеров.
ICMP — это протокол транспортного уровня, применяемый для настройки и диагностики интернет-сетей, он отличается от других протоколов транспортного уровня
тем, что не содержит никаких истинных пользовательских данных, и, следовательно,

282   Глава 9. Сеть и ее конфигурация
над ним нет прикладного уровня. Для сравнения: DNS — это протокол прикладного
уровня для сопоставления удобочитаемых имен с интернет-адресами.

9.8.1. Команда ping
Команда ping (подробнее на ftp.arl.army.mil/~mike/ping.html) — один из самых простых
инструментов отладки сети. Она отправляет эхо-запросы ICMP хосту, который
просит хост-получатель вернуть пакет отправителю. Если хост-получатель получает пакет и сконфигурирован для ответа, он отправляет пакет эхо-ответа ICMP.
К примеру, запустим команду ping 10.23.2.1 и получим следующий вывод:
$ ping 10.23.2.1
PING 10.23.2.1 (10.23.2.1) 56(84) bytes of
64 bytes from 10.23.2.1: icmp_req=1 ttl=64
64 bytes from 10.23.2.1: icmp_req=2 ttl=64
64 bytes from 10.23.2.1: icmp_req=4 ttl=64
64 bytes from 10.23.2.1: icmp_req=5 ttl=64

data.
time=1.76
time=2.35
time=1.69
time=1.61

ms
ms
ms
ms

В первой строке говорится, что вы отправляете 56-байтовые пакеты (84 байта, если
включены заголовки) в 10.23.2.1 (по умолчанию один пакет в секунду), а остальные строки указывают на ответы из 10.23.2.1. Наиболее важными частями вывода
являются порядковый номер (icmp_req) и время прохождения в оба конца (time).
Количество возвращаемых байтов равно размеру отправленного пакета плюс 8.
(Содержимое пакетов для нас неважно.)
Разрыв в порядковых номерах, например, между 2 и 4, обычно означает, что появилась какая-то проблема с подключением. Пакеты не должны поступать не по
порядку, потому что ping отправляет только один пакет в секунду. Если для получения ответа требуется более секунды (1000 мс), подключение крайне медленное.
Время прохождения в оба конца — это общее время с момента отправки пакета
запроса до момента прибытия пакета ответа. Если нет способа добраться до места
назначения, последний маршрутизатор, который увидит пакет, возвращает ping
пакет ICMP о том, что хост недоступен (host unreachable).
В проводной локальной сети пакеты не должны теряться, а время прохождения
должно быть очень малым. (В предыдущем примере вывод осуществляется из беспроводной сети.) Также не должны теряться пакеты из вашей сети к интернет-провайдеру и от него, а время прохождения в оба конца должно быть довольно стабильным.
ПРИМЕЧАНИЕ
По соображениям безопасности некоторые хосты в интернете отключают ответ на пакеты эхо-запросов ICMP, поэтому вы можете обнаружить, что способны подключиться
к веб-сайту на хосте, но не получить ответ ping.
Вы можете заставить ping использовать IPv4 или IPv6 с параметрами -4 и -6 соответственно.

9.9. Физический уровень и сеть Ethernet   283

9.8.2. Служба DNS и команда host
IP-адреса трудно запомнить, и они могут быть изменены, поэтому мы обычно берем
такие имена, как www.example.com. Библиотека службы доменных имен (Domain
Name System, DNS) в вашей системе обычно переводит имя в IP-адрес автоматически, но иногда может потребоваться выполнить это вручную. Чтобы найти IP-адрес,
скрывающийся за доменным именем, используйте команду host:
$ host www.example.com
example.com has address 172.17.216.34
example.com has IPv6 address 2001:db8:220:1:248:1893:25c8:1946

Обратите внимание на то, что в этом примере есть как IPv4-адрес 172.17.216.34,
так и гораздо более длинный IPv6-адрес. Для имени хоста могут существовать несколько адресов, и вывод может содержать дополнительную информацию, такую
как почтовые обменники.
Можно использовать команду host и в обратном порядке: введите IP-адрес вместо
имени хоста, чтобы попытаться обнаружить имя хоста, скрытое за IP-адресом. Однако не ожидайте, что этот способ сработает надежно. Один IP-адрес может быть
связан с несколькими именами хостов, и DNS не знает, как определить, какое из них
должно соответствовать IP-адресу. Кроме того, администратору хоста необходимо
настроить обратный поиск вручную, а делают это нечасто.
В DNS есть возможности гораздо большие, чем просто команда host. Мы рассмотрим базовую конфигурацию клиента в разделе 9.15.
С утилитой host используются параметры -4 и -6, но они работают иначе, чем
можно ожидать. Они заставляют команду host получать информацию через IPv4
или IPv6, но поскольку она должна быть одинаковой вне зависимости от сетевого
протокола, вывод потенциально будет включать как IPv4, так и IPv6.

9.9. Физический уровень и сеть Ethernet
Один из ключевых моментов, который необходимо знать об интернете, заключается в том, что это программная сеть. Ничто из того, что мы обсуждали до сих
пор, не относится к конкретному оборудованию, и действительно, одна из причин популярности интернета заключается в том, что он работает практически на
любых компьютере, операционной системе и физической сети. Но если вы действительно хотите поговорить с другим компьютером, вам придется разместить
сетевой уровень поверх какого-либо оборудования. Этот интерфейс является
физическим уровнем.
В этой книге мы рассмотрим наиболее распространенный вид физического уровня — сеть Ethernet. Документация по стандартам семейства IEEE 802 определяет
множество различных типов сетей Ethernet, от проводных до беспроводных, но
у всех них есть несколько общих черт:

284   Глава 9. Сеть и ее конфигурация
Все устройства в сети Ethernet имеют адрес управления доступом к среде
(Media Access Control, MAC), иногда называемый аппаратным адресом.
Он не зависит от IP-адреса хоста и уникален для сети Ethernet хоста (но
не обязательно для более крупной программной сети, такой как интернет).
Пример MAC-адреса — 10:78:d2:eb:76:97.
Устройства в сети Ethernet отправляют сообщения в виде фреймов, или
кадров, которые являются обертками вокруг отправленных данных. Кадр
содержит MAC-адреса источника и назначения.
Ethernet на самом деле не пытается выйти за рамки аппаратного обеспечения в одной сети. Например, если у вас есть две разные сети Ethernet с одним хостом, подключенным к обеим (и два разных устройства сетевого интерфейса), вы не сможете
напрямую передавать кадр из одной сети Ethernet в другую, если не настроите мост
Ethernet bridge. Именно здесь вступают в действие более высокие сетевые уровни
(такие, как интернет-уровень). По соглашению каждая сеть Ethernet также обычно
является подсетью интернета. Даже если кадр не может покинуть одну физическую
сеть, маршрутизатор может извлечь из него данные, переупаковать их и отправить
на хост в другой физической сети, что и происходит в интернете.

9.10. Сетевые интерфейсы ядра
Физический и сетевой уровни должны быть соединены таким образом, чтобы
сетевой уровень мог сохранять свою не зависящую от аппаратного обеспечения
гибкость. Ядро Linux поддерживает собственное разделение между двумя уровнями и обеспечивает стандарт связи для их соединения, называемый сетевым
интерфейсом (ядра). При настройке сетевого интерфейса вы связываете параметры IP-адреса со стороны сети с идентификацией оборудования на стороне физического устройства. Сетевые интерфейсы обычно имеют имена, указывающие на
тип используемого оборудования, например enp0s31f6 (интерфейс в слоте PCI).
Такое имя называется предсказуемым именем сетевого интерфейса, потому что оно
остается неизменным после перезагрузки. Во время загрузки интерфейсы имеют
традиционные имена, такие как eth0 (первая карта Ethernet в компьютере) и wlan0
(беспроводной интерфейс), но на большинстве машин под управлением systemd
они быстро переименовываются.
В подразделе 9.4.1 мы узнали, как просмотреть настройки сетевого интерфейса
с помощью команды ip address show. Вывод сгруппирован по интерфейсам, как
встречалось ранее:
2: enp0s31f6: mtu 1500 qdisc fq_codel state \
❶ UP group default qlen 1000
❷ link/ether 40:8d:5c:fc:24:1f brd ff:ff:ff:ff:ff:ff
inet 10.23.2.4/24 brd 10.23.2.255 scope global noprefixroute enp0s31f6
valid_lft forever preferred_lft forever
inet6 2001:db8:8500:e:52b6:59cc:74e9:8b6e/64 scope global dynamic

9.11. Введение в настройки сетевого интерфейса   285
noprefixroute
valid_lft 86054sec preferred_lft 86054sec
inet6 fe80::d05c:97f9:7be8:bca/64 scope link noprefixroute
valid_lft forever preferred_lft forever

Каждый сетевой интерфейс получает номер, в данном случае 2. Интерфейс 1 почти
всегда является интерфейсом loopback, описанным в разделе 9.16. Флаг UP сообщает,
что интерфейс работает ❶. В дополнение к частям сетевого уровня, которые мы уже
рассмотрели, вы также видите MAC-адрес на физическом уровне, link/ether ❷.
Хотя команда ip показывает часть информации об оборудовании, она предназначена в основном для просмотра и настройки программных уровней, подключенных
к интерфейсам. Чтобы глубже изучить аппаратный и физический уровень сетевого
интерфейса, используйте команду ethtool для отображения или изменения настроек на картах Ethernet. (Мы кратко рассмотрим беспроводные сети в разделе 9.27.)

9.11. Введение в настройки сетевого интерфейса
Мы изучили все основные элементы, которые входят в нижние уровни сетевого
стека: физический уровень, сетевой (межсетевой) уровень и сетевые интерфейсы
ядра Linux. Чтобы объединить эти части для подключения компьютера Linux к интернету, вы или часть программного обеспечения должны выполнить следующие
действия:
1. Подключить сетевое оборудование и убедиться, что в ядре есть драйвер для
него. Если он имеется, вывод команды ip address show включает запись для
устройства, даже если оно не было настроено.
2. Выполнить любые дополнительные настройки физического уровня, такие
как выбор имени сети или введение пароля.
3. Назначить IP-адреса и подсети сетевому интерфейсу ядра, чтобы драйверы
устройств ядра (физический уровень) и подсистемы интернета (сетевой
уровень) могли взаимодействовать друг с другом.
4. Добавить любые дополнительные маршруты, включая шлюз по умолчанию.
Когда компьютеры были большими стационарными коробками, соединенными
вместе, все это было относительно просто: ядро выполняло шаг 1, шаг 2 не требовался, и вы выполняли шаг 3 старой командой ifconfig и шаг 4 старой командой
route. Мы кратко рассмотрим, как это сделать с помощью команды ip.

9.11.1. Ручная настройка интерфейсов
Сейчас рассмотрим, как настроить интерфейсы вручную, но не будем вдаваться
в подробности, потому что это редко требуется и может привести к ошибкам.
Лучше выполнять эти действия только на экспериментальной системе. Даже при

286   Глава 9. Сеть и ее конфигурация
настройке для создания конфигурации в текстовом файле вы можете использовать
инструмент Netplan вместо ряда команд, как показано далее.
Можете привязать интерфейс к сетевому уровню с помощью команды ip. Чтобы
добавить IP-адрес address и подсеть subnet для сетевого интерфейса ядра, выполните следующее:
# ip address add address/subnet dev interface

Здесь interface — это имя интерфейса, например enp0s31f6 или eth0. Эта же
команда работает для IPv6, за исключением того, что нужно добавить параметры
(например, указать статус локального канала). Если хотите просмотреть все параметры, используйте страницу руководства по ip-address(8).

9.11.2. Добавление и удаление маршрутов вручную
С настроенным интерфейсом вы можете добавлять маршруты, что обычно сводится
к настройке шлюза по умолчанию, например:
# ip route add default via gw-address dev interface

Параметр gw-address — это IP-адрес шлюза по умолчанию. Это должен быть адрес
в локально подключенной подсети, назначенный одному из ваших сетевых интерфейсов. Чтобы удалить шлюз по умолчанию, запустите следующую команду:
# ip route del default

Можно легко переопределить шлюз по умолчанию с помощью других маршрутов.
Предположим, компьютер находится в подсети 10.23.2.0/24, вы хотите подключиться к подсети в 192.168.45.0/24 и знаете, что хост в 10.23.2.44 может выступать
для нее маршрутизатором. Чтобы отправить трафик, привязанный к 192.168.45.0,
на этот маршрутизатор, выполните следующую команду:
# ip route add 192.168.45.0/24 via 10.23.2.44

Вам не нужно указывать маршрутизатор, чтобы удалить маршрут:
# ip route del 192.168.45.0/24

Прежде чем сойти с ума от количества маршрутов, вы должны знать, что настройка
маршрутов часто сложнее, чем кажется. В этом конкретном примере необходимо
убедиться также, что маршрутизация для всех хостов на 192.163.45.0/24 может
привести к 10.23.2.0/24, или же первый добавленный вами маршрут в основном
бесполезен.
Обычно все нужно делать как можно проще, настроив локальные сети так, чтобы
их хостам требовался только маршрут по умолчанию. Если вам нужны несколько
подсетей и возможность маршрутизации между ними, обычно стоит настроить

9.12. Конфигурация сети, активируемая при загрузке   287
маршрутизаторы, действующие как шлюзы по умолчанию, для выполнения всей
работы по маршрутизации между различными локальными подсетями. (Вы увидите
пример в разделе 9.21.)

9.12. Конфигурация сети, активируемая
при загрузке
Мы обсудили способы ручной настройки сети и выяснили, что традиционный способ обеспечения правильности сетевой конфигурации машины — это выполнение
сценария init для запуска ручной настройки во время загрузки. Все сводится к запуску утилиты ip где-то в цепочке событий загрузки.
В Linux было много попыток стандартизировать файлы конфигурации для работы
сети во время загрузки. Среди них инструменты ifup и ifdown: например, загрузочный скрипт может (теоретически) запускать ifup eth0 для запуска правильных
ip-команд для настройки интерфейса. К сожалению, разные дистрибутивы имеют
совершенно разные реализации ifup и ifdown, и в результате их файлы конфигурации также различаются.
Существует еще более глубокое различие из-за того, что элементы конфигурации
сети присутствуют на каждом из сетевых уровней. Следствием этого является то, что
программное обеспечение, ответственное за создание сетей, находится в нескольких
частях ядра и инструментах пользовательского пространства, написанных и поддерживаемых разными разработчиками. В Linux существует общее соглашение не
обмениваться файлами конфигурации между отдельными наборами инструментов
или библиотеками, поскольку изменения, внесенные для одного инструмента, могут
нарушить работу другого.
Работа с конфигурацией сети в нескольких разных местах затрудняет управление
системами. В результате появилось несколько различных инструментов управления сетью, у каждого из которых собственный подход к проблеме конфигурации.
Однако они, как правило, специализированы для определенного рода ролей, которые может выполнять машина Linux. Инструмент способен работать на ПК, но
не подходит для сервера.
Инструмент Netplan предлагает другой подход к проблеме конфигурации. Netplan —
не столько управление сетью, сколько унифицированный стандарт конфигурации
сети и инструмент для преобразования этой конфигурации в файлы, используемые
существующими сетевыми менеджерами. В настоящее время Netplan поддерживает
NetworkManager и systemd-networkd, о которых мы поговорим позже в этой главе.
Файлы Netplan представлены в формате YAML и находятся в /etc/netplan.
Прежде чем говорить о менеджерах конфигурации сети, давайте немного подробнее
рассмотрим проблемы, с которыми они сталкиваются.

288   Глава 9. Сеть и ее конфигурация

9.13. Проблемы с настройкой сети вручную
и при загрузке
Несмотря на то что в большинстве систем настройка сети заложена в механизме
загрузки (многие серверы поддерживают эту традицию), динамическая природа
современных сетей означает, что большинство машин не имеют статического (неизменного) IP-адреса. В IPv4 компьютер не хранит IP-адрес и другую сетевую
информацию, а получает ее откуда-то из локальной физической сети при первом
подключении к ней. Большинству обычных сетевых клиентских приложений не
особенно важно, какой IP-адрес использует машина, если он рабочий. Инструменты
протокола динамической настройки узла (Dynamic Host Configuration Protocol,
DHCP, см. раздел 9.19) выполняют базовую настройку сетевого уровня на типичных
клиентах IPv4. В IPv6 клиенты могут в определенной степени настраивать себя,
мы кратко рассмотрим эту способность в разделе 9.20.
И это еще не вся история. Например, беспроводные сети добавляют в конфигурацию
интерфейса дополнительные параметры, такие как имена сетей, методы аутентификации и шифрования. Взглянув на общую картину, можно увидеть, что системе
необходимо ответить на следующие вопросы:
Если устройство имеет несколько физических сетевых интерфейсов (например, ноутбук с проводным и беспроводным Ethernet), как выбрать, какой из
них использовать?
Как машина должна настроить физический интерфейс? Для беспроводных
сетей этот процесс включает сканирование имен сетей, выбор имени и аутен­
тификацию.
Как только физический сетевой интерфейс будет подключен, как машина
должна настроить сетевые уровни программного обеспечения, например
интернет-уровень?
Как позволить пользователю выбирать параметры подключения, например
беспроводную сеть?
Что должен делать компьютер, если подключение к сетевому интерфейсу
будет нарушено?
Ответы на эти вопросы обычно глубже, чем могут дать простые загрузочные сценарии, и все это очень сложно сделать вручную. Ответ заключается в применении
системной службы, которая может отслеживать физические сети и выбирать
(и автоматически настраивать) сетевые интерфейсы ядра на основе набора правил,
имеющих смысл для пользователя. Служба также должна иметь возможность
отвечать на запросы пользователей, которые, в свою очередь, должны иметь
возможность изменять беспроводную сеть, в которой работают, не становясь
суперпользователем.

9.14. Менеджеры настройки сети   289

9.14. Менеджеры настройки сети
Существует несколько способов автоматической настройки сетей в системах на
базе Linux. Наиболее широко используемым вариантом на ПК и ноутбуках является NetworkManager. Существует дополнение к systemd, называемое systemdnetworkd, способное выполнять базовую настройку сети и полезное для машин,
которым не требуется большая гибкость (например, серверов), но не обладающее
динамическими возможностями NetworkManager. Другие системы управления
конфигурацией сети предназначены в основном для небольших встроенных
систем, например netifd OpenWRT, служба ConnectivityManager для Android,
ConnMan и Wicd.
Мы кратко рассмотрим NetworkManager, потому что именно эту утилиту вы,
скорее всего, будете использовать. Однако не будем вдаваться в подробности,
потому что после того, как вы ознакомитесь с основными концепциями, понять
NetworkManager и другие системы конфигурации будет намного проще. Если вас
интересует systemd-networkd, изучите страницу руководства systemd.network(5),
на которой описаны настройки, или каталог конфигурации /etc/systemd/network.

9.14.1. Работа NetworkManager
NetworkManager — это демон, который система запускает при загрузке. Как и большинство демонов, он не зависит от работающего компонента рабочего стола. Его
задача состоит в том, чтобы прослушивать события, инициируемые системой
и пользователями, и изменять конфигурацию сети на основе набора правил.
Во время работы NetworkManager поддерживает два основных уровня конфигурации. Первый — это сбор информации о доступных аппаратных устройствах, которую он обычно получает из ядра и поддерживает путем мониторинга udev по шине
рабочего стола Desktop Bus (D-Bus). Второй уровень конфигурации представляет
собой более конкретный список подключений: аппаратные устройства и дополнительные параметры конфигурации физического и сетевого уровней. Например,
беспроводная сеть может быть представлена в виде подключения.
Чтобы активировать подключение, NetworkManager часто делегирует задачи другим
специализированным сетевым инструментам и демонам, таким как dhclient, для
получения конфигурации интернет-уровня из локально подключенной физической сети. Поскольку инструменты и схемы настройки сети различаются в разных
дистрибутивах, NetworkManager использует плагины для взаимодействия с ними,
а не навязывает собственный стандарт. Например, существуют плагины как для
конфигурации интерфейса Debian/Ubuntu, так и для интерфейса Red Hat.
При запуске NetworkManager собирает всю доступную информацию о сетевом
устройстве, просматривает список подключений, а затем пробует активировать
одно из них. Вот как он принимает это решение для интерфейсов Ethernet:

290   Глава 9. Сеть и ее конфигурация
1. Если доступно проводное подключение, то подключается с его помощью.
В противном случае использует беспроводные подключения.
2. Просматривает список доступных беспроводных сетей. Если доступна сеть,
к которой система ранее подключалась, NetworkManager повторит попытку.
3. Если доступны несколько беспроводных сетей, к которым демон подключался ранее, он выбирает ту, с которой соединялся в предыдущий раз.
После установления соединения NetworkManager поддерживает его до тех пор,
пока оно не нарушится, не станет доступной улучшенная сеть (например, во время
подключения по беспроводной сети вы подсоединяете сетевой кабель) или пользователь не внесет изменения.

9.14.2. Взаимодействие с NetworkManager
Большинство пользователей взаимодействуют с NetworkManager через приложение
на рабочем столе (обычно это значок в правом верхнем или нижнем углу, который
указывает состояние подключения — проводное, беспроводное или не подключено).
При нажатии на значок вы получаете ряд вариантов подключения, например выбор
беспроводных сетей и возможность отключения от текущей сети. Каждая среда
рабочего стола имеет собственную версию этого приложения, поэтому на каждом
из них оно выглядит немного по-разному.
В дополнение к приложению есть несколько инструментов, которые можно использовать для выполнения запросов и управления NetworkManager из своей
оболочки. Для быстрого получения сводки о состоянии текущего подключения
примените команду nmcli без аргументов. Вы получите список интерфейсов и параметров конфигурации. В некотором смысле это похоже на работу команды ip,
за исключением того, что в этом выводе больше деталей, особенно при просмотре
беспроводных подключений.
Команда nmcli позволяет управлять демоном NetworkManager из командной строки. Это довольно обширная команда: в дополнение к обычной странице руководства
nmcli(1) есть страница руководства с примерами использования nmcli-examples(5).
Наконец, утилита nm-online сообщит, работает сеть или нет. Если онаподключена,
команда возвращает 0 в качестве кода возврата, в противном случае он не равен
нулю. (Подробнее о том, как применять код выхода в сценарии командной оболочки, читайте в главе 11.)

9.14.3. Настройка NetworkManager
Обычно каталог общей конфигурации NetworkManager — это /etc/NetworkManager,
существует несколько различных типов его конфигурации. Общий файл конфигурации — NetworkManager.conf. Формат аналогичен формату файлов XDG-style.desktop
и Microsoft.ini с параметрами «ключ — значение», попадающими в разные секции.

9.14. Менеджеры настройки сети   291
Почти в каждом файле конфигурации есть секция [main], которая определяет задействуемые плагины (подключаемые модули). Вот простой пример, который активирует подключаемый модуль ifupdown, используемый системами Ubuntu и Debian:
[main]
plugins=ifupdown,keyfile

Другими специфичными для конкретного дистрибутива плагинами являются ifcfgrh (для семейства Red Hat) и ifcfg-suse (для SuSE). Подключаемый модуль keyfile
поддерживает встроенную поддержку файлов конфигурации NetworkManager. При
использовании подключаемого модуля вы можете просмотреть все известные подключения системы в /etc/NetworkManager/system-connections.
По большей части вам не нужно будет изменять файл NetworkManager.conf, потому
что более конкретные параметры конфигурации находятся в других файлах.

Неуправляемые интерфейсы
Хотя вы можете захотеть, чтобы NetworkManager управлял большинством ваших
сетевых интерфейсов, будут моменты, когда потребуется, чтобы он игнорировал
интерфейсы. Например, большинству пользователей не понадобится динамическая
конфигурация интерфейса localhost (lo; см. раздел 9.16), поскольку его конфигурация никогда не меняется. К тому же лучше настроить этот интерфейс на ранней
стадии загрузки, потому что от него часто зависят базовые системные службы.
Большинство дистрибутивов держат NetworkManager подальше от localhost.
Вы можете указать NetworkManager игнорировать интерфейс с помощью подключаемых модулей. Если используете подключаемый модуль ifupdown (например,
в Ubuntu и Debian), добавьте конфигурацию интерфейса в файл /etc/network/
interfaces, а затем установите значение managed в false в секции ifupdown в файле
NetworkManager.conf:
[ifupdown]
managed=false

Для плагина ifcfg-rh, который применяют Fedora и Red Hat, найдите в каталоге
/etc/sysconfig/network-scripts , содержащем файлы конфигурации ifcfg-* ,
строку
NM_CONTROLLED=yes

Если она отсутствует или значение равно no, NetworkManager игнорирует интерфейс.
В случае локального хоста значение будет деактивировано в файле ifcfg-lo. Вы
также можете указать аппаратный адрес, который нужно игнорировать, например:
HWADDR=10:78:d2:eb:76:97

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

292   Глава 9. Сеть и ее конфигурация
устройство непосредственно в файле NetworkManager.conf с помощью его MACадреса. Вот пример, показывающий два неуправляемых устройства:
[keyfile]
unmanaged-devices=mac:10:78:d2:eb:76:97;mac:1c:65:9d:cc:ff:b9

Диспетчеризация
Последняя деталь конфигурации NetworkManager связана с определением дополнительных системных действий при включении или выключении сетевого интерфейса. В частности, некоторым сетевым демонам (например, демону безопасной
оболочки, обсуждаемому в следующей главе) для правильной работы необходимо
знать, когда начинать или прекращать прослушивание интерфейса.
Когда состояние сетевого интерфейса системы изменяется, NetworkManager запускает все в /etc/NetworkManager/dispatcher.d с аргументами up или down. Все
довольно просто, но многие дистрибутивы имеют собственные сценарии управления сетью, поэтому не помещают отдельные сценарии диспетчера в этот каталог.
В Ubuntu, например, есть только один скрипт с именем 01ifupdown, который запускает все в соответствующем подкаталоге /etc/network — /etc/network/if-up.d.
Как и в случае с остальной конфигурацией NetworkManager, детали этих сценариев
не особо важны: вам нужно лишь знать, как отследить подходящее местоположение,
если требуется что-то добавить или изменить (или использовать Netplan и позволить ему определить местоположение самостоятельно). Как всегда, не забывайте
просматривать сценарии в своей системе.

9.15. Разрешения сетевых имен
Одной из последних основных задач в любой конфигурации сети является разрешение имен хостов (hostname resolution) с помощью DNS. Мы уже видели инструмент
host, который переводит такое имя, как www.example.com, в IP-адрес 10.23.2.132.
DNS отличается от сетевых элементов, которые мы рассматривали до сих пор, потому что находится на прикладном уровне, полностью в пространстве пользователя.
Из-за этого технически он немного неуместен в этой главе, как и обсуждение сетевого и физического уровней. Однако без надлежащей настройки DNS подключение
к интернету практически бесполезно. Никто в здравом уме не показывает IP-адреса
(тем более IPv6-адреса) для веб-сайтов и адресов электронной почты, потому что
IP-адрес хоста может быть изменен, а запомнить кучу цифр непросто.
Практически все сетевые приложения в системе Linux выполняют поиск DNS.
Процесс разрешения имен обычно протекает следующим образом.
1. Приложение вызывает функцию для поиска IP-адреса, скрытого за именем
хоста. Функция находится в разделяемой библиотеке системы, поэтому при-

9.15. Разрешения сетевых имен   293
ложению не нужно знать подробности о том, как она работает или изменится
ли реализация.
2. Когда функция в разделяемой библиотеке запускается, она действует в соответствии с набором правил, найденных в /etc/nsswitch.conf (см. раздел 9.15.4), чтобы определить план действий при поиске. Например, в правилах обычно говорится, что еще до обращения к DNS необходимо проверить,
нет ли ручного переопределения в файле /etc/hosts.
3. Когда функция решает использовать DNS для поиска имени, она обращается
к дополнительному файлу конфигурации для поиска сервера имен DNS.
Сервер имен задается в качестве IP-адреса.
4. Функция отправляет DNS-запрос (по сети) на сервер имен.
5. Сервер имен отвечает IP-адресом для имени хоста, и функция возвращает
этот IP-адрес приложению.
Это упрощенная версия. В типичной современной системе больше участников
пытаются ускорить транзакцию или повысить гибкость. Давайте пока проигнорируем это и рассмотрим основные элементы. Как и в случае с другими типами
конфигурации сети, вам, вероятно, не потребуется изменять разрешение имени
хоста, но знать, как это работает, полезно.

9.15.1. Файл /etc/hosts
В большинстве систем вы можете переопределить поиск имени хоста с помощью
файла /etc/hosts. Обычно это выглядит так:
127.0.0.1
10.23.2.3
10.23.2.4
::1

localhost
atlantic.aem7.net
pacific.aem7.net
localhost ip6-localhost

atlantic
pacific

Запись (или записи) для localhost почти всегда появляется именно здесь (см. раздел 9.16). Другие записи в примере иллюстрируют простой способ добавления
хостов в локальную подсеть.
ПРИМЕЧАНИЕ
В старые добрые времена существовал один центральный файл hosts, который каждый
копировал на свою машину, чтобы иметь актуальную информацию (см. RFC 606, 608,
623 и 625), но по мере роста ARPANET/интернета это быстро вышло из-под контроля.

9.15.2. Файл resolv.conf
Традиционным файлом конфигурации DNS-серверов является файл /etc/resolv.
conf. Раньше, когда все работало проще, вот как мог бы выглядеть адрес сервера
имен провайдера 10.32.45.23 и 10.3.2.3:

294   Глава 9. Сеть и ее конфигурация
search mydomain.example.com example.com
nameserver 10.32.45.23
nameserver 10.3.2.3

Строка search определяет правила для неполных имен хостов (только для первой
часть имени хоста, например myserver вместо myserver.example.com). В данном
случае библиотека разрешения имен resolver попытается найти host.mydomain.
example.com и host.example.com.
Сейчас поиск имен уже не так прост. В конфигурацию DNS было внесено множество
улучшений и изменений.

9.15.3. Кэширование и DNS с нулевой конфигурацией
Существуют две основные проблемы с традиционной конфигурацией DNS. Вопервых, локальная машина не кэширует ответы сервера имен, поэтому повторный
доступ к сети может неоправданно замедлиться из-за запросов к серверу имен.
Чтобы решить эту проблему, многие компьютеры (и маршрутизаторы, если они
действуют как серверы имен) запускают промежуточный демон для перехвата запросов сервера имен и кэширования ответа, а затем по возможности используют эти
ответы. Наиболее распространенным из этих демонов является systemd-resolved,
также в системах встречаются dnsmasq или nscd. Вы можете настроить BIND (стандартный демон сервера имен Unix) в качестве кэша. Можно сказать, что в системе
применяется демон кэширования сервера имен, если встречаются адреса 127.0.0.53
или 127.0.0.1 либо в файле /etc/resolv.conf, либо в списке серверов при запуске
nslookup -debug host (где host — имя хоста). Однако давайте посмотрим внимательнее. Если вы используете systemd-resolved, то можете заметить, что resolv.
conf даже не является файлом в /etc — это ссылка на автоматически созданный
файл в /run.
Утилита systemd-resolved таит в себе гораздо больше, чем кажется на первый
взгляд, поскольку она может объединять несколько служб поиска имен и предоставлять их по-разному для каждого интерфейса. А это решает вторую проблему
с традиционной настройкой сервера имен: она может быть особенно негибкой, если
вы хотите просматривать имена в своей локальной сети, не путаясь с большим
количеством настроек. Например, если вы настроили сетевое устройство в своей сети, то захотите немедленно вызвать его по имени. Это часть идеи, лежащей
в основе систем службы имен с нулевой конфигурацией, таких как многоадресная
DNS (Multicast DNS, mDNS) и протокола широковещательного разрешения имен
(Link-Local Multicast Name Resolution, LLMNR). Если процесс хочет найти хост
по имени в локальной сети, он просто передает широковещательный запрос по
сети, и целевой хост отвечает своим адресом. Эти протоколы выходят за рамки
разрешения имен хостов, предоставляя также информацию о доступных службах.
Можно проверить текущие настройки DNS с помощью команды resolvectl
status (обратите внимание на то, что в старых системах она может называться

9.15. Разрешения сетевых имен   295
systemd-resolve). Вы получите список глобальных настроек (обычно редко ис-

пользуемых), а затем увидите настройки для каждого отдельного интерфейса. Это
будет выглядеть так:
Link 2 (enp0s31f6)
Current Scopes:
LLMNR setting:
MulticastDNS setting:
DNSSEC setting:
DNSSEC supported:
DNS Servers:
DNS Domain:

DNS
yes
no
no
no
8.8.8.8
~.

В примере отображены различные поддерживаемые протоколы имен, а также
сервер имен, у которого демон systemd-resolved запрашивает неизвестное ему имя.
Мы не будем углубляться в DNS или systemd-resolved, потому что это очень обширная тема. Если вы хотите изменить свои настройки, загляните на страницу
руководства resolved.conf(5) и перейдите к внесению изменений в /etc/systemd/
resolved.conf. Однако вам, вероятно, потребуется ознакомиться с документацией
systemd-resolved, а также с DNS в целом, например в книге Крикета Ли и Пола
Альбица «DNS и BIND», 5-е издание (Символ-Плюс, 2008).

9.15.4. Файл /etc/nsswitch.conf
Прежде чем закончить с темой поиска имен, рассмотрим одну последнюю настройку, о которой необходимо знать. Файл /etc/nsswitch.conf — это традиционный
интерфейс для управления несколькими настройками приоритета, связанными
с именами в системе (например, информацией о пользователе и пароле), и у него
есть настройка поиска хоста. Файл в вашей системе должен содержать такую строку:
hosts:

files dns

Добавление files перед dns гарантирует, что при поиске хостов система проверяет файл /etc/hosts на поиск хоста, прежде чем запрашивать любой DNS-сервер,
в том числе с systemd-resolved. Чаще всего это хорошая затея (особенно для поиска
localhost, как обсуждается далее), но файл /etc/hosts должен быть как можно
меньше. Не добавляйте в него ничего, чтобы повысить производительность. Вы
можете разместить хосты в небольшой частной локальной сети в /etc/hosts, но
общее правило заключается в том, что, если у конкретного хоста есть запись в DNS,
ей нет места в /etc/hosts. (Файл /etc/hosts полезен также для разрешения имен
хостов на ранних стадиях загрузки, когда сеть может быть недоступна.)
Все это работает при помощи стандартных вызовов, имеющихся в системной библиотеке. Может оказаться сложно запомнить все места, в которых может происходить поиск имен, но если вам когда-нибудь понадобится отследить их, начните
с /etc/nsswitch.conf.

296   Глава 9. Сеть и ее конфигурация

9.16. Localhost
При запуске команды ip address show вы встретите интерфейс lo:
1: lo: mtu 65536
default qlen 1000
link/loopback 00:00:00:00:00:00 brd
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft
inet6 ::1/128 scope host
valid_lft forever preferred_lft

qdisc noqueue state UNKNOWN group
00:00:00:00:00:00
forever
forever

Интерфейс lo — это виртуальный сетевой интерфейс, называемый интерфейсом
возвратной петли (loopback), потому что он закольцован сам на себя. В таком случае подключение к 127.0.0.1 (или ::1 в IPv6) означает подключение к компьютеру,
который вы используете в данный момент. Когда данные, уходящие на локальный
хост, достигают сетевого интерфейса ядра для lo, ядро просто переупаковывает их
как входящие данные и отправляет обратно через lo для применения любой прослушивающей этот интерфейс серверной программой (по умолчанию большинство
программ делают это).
Интерфейс lo часто является единственным местом, где можно увидеть статическую конфигурацию сети в сценариях во время загрузки. Например, команда ifup
в системе Ubuntu читает файл /etc/network/interfaces. Однако это может быть
излишним, поскольку systemd настраивает интерфейс loopback при запуске.
Интерфейс возвратной петли (loopback) имеет одну особенность, которую вы, возможно, заметили. Маска сети равна /8, и все, что начинается со 127, назначается для
обратной связи. Это позволяет запускать разные серверы на разных IPv4-адресах
в пространстве loopback без настройки дополнительных интерфейсов. Одним из
серверов, который использует это преимущество, является systemd-resolved, применяющий 127.0.0.53. Таким образом, это не мешает процессу другого сервера имен,
работающего на 127.0.0.1. Пока что IPv6 определяет только один адрес обратной
связи, но в будущем, возможно, это изменится.

9.17. Транспортный уровень: TCP, UDP и службы
До сих пор мы видели только, как пакеты перемещаются от хоста к хосту в сети,
другими словами, это ответ на вопрос «куда?», заданный в начале главы. Теперь
начнем отвечать на вопрос о том, что именно передается. Важно знать, как ваш
компьютер представляет пакетные данные, которые получает от других хостов,
запущенным у себя процессам. Программам пользовательского пространства было
бы сложно и неудобно работать с кучей необработанных пакетов так, как это делает
ядро. Гибкость особенно важна: несколько приложений должны иметь возможность
подключаться к сети одновременно (например, у вас может работать электронная
почта и запущено несколько веб-клиентов).

9.17. Транспортный уровень: TCP, UDP и службы   297
Протоколы транспортного уровня устраняют разрыв между необработанными
пакетами сетевого уровня и уточненными потребностями приложений. Двумя наиболее популярными транспортными протоколами являются протокол управления
передачей (Transmission Control Protocol, TCP) и протокол пользовательских датаграмм (User Datagram Protocol, UDP). Мы сосредоточимся на TCP, потому что
это, безусловно, самый распространенный протокол, но коротко рассмотрим и UDP.

9.17.1. TCP-порты и соединения
Протокол TCP обеспечивает работу нескольких сетевых приложений на одном
компьютере с помощью сетевых портов (network ports), которые являются просто
номерами, применяемыми в сочетании с IP-адресом. Если IP-адрес хоста похож на
почтовый адрес многоквартирного дома, то номер порта похож на номер почтового
ящика — это еще одно уточнение.
При использовании TCP приложение открывает соединение (не путать с подключениями NetworkManager) между одним портом на своем компьютере и портом
на удаленном хосте. Например, браузер может открыть соединение между портом 36406 на собственном компьютере и портом 80 на удаленном хосте. С точки
зрения приложения порт 36406 является локальным, а порт 80 — удаленным.
Вы можете идентифицировать соединение, используя пару IP-адресов и номеров
портов. Для просмотра соединений, открытых в данный момент на вашем компьютере, задействуйте команду netstat. Приведем пример, который показывает TCPсоединения, параметр -n отключает разрешение имен хостов (DNS), а параметр -t
ограничивает вывод только выводом TCP:
$ netstat -nt
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address
tcp
0
0 10.23.2.4:47626
tcp
0
0 10.23.2.4:41475
tcp
0
0 10.23.2.4:57132

Foreign Address
10.194.79.125:5222
172.19.52.144:6667
192.168.231.135:22

State
ESTABLISHED
ESTABLISHED
ESTABLISHED

Поля Local Address (Локальный адрес) и Foreign Address (Внешний адрес) относятся к подключениям с точки зрения вашего компьютера, поэтому на данном
компьютере интерфейс настроен на 10.23.2.4, и все порты 47626, 41475 и 57132 на
локальной стороне подключены. Первое соединение в примере — от порта 47626
к порту 5222 на 10.194.79.125.
Чтобы отображать только подключения IPv6, добавьте параметр -6 в команду
netstat.

Установка TCP-соединений
Чтобы установить соединение транспортного уровня, процесс на одном хосте инициирует соединение одного из своих локальных портов с портом на втором хосте

298   Глава 9. Сеть и ее конфигурация
с помощью специальной серии пакетов. Чтобы распознать входящее соединение
и ответить, у второго хоста должен быть процесс, прослушивающий правильный
порт. Обычно подключающийся процесс называется клиентом, а прослушивающий — сервером (подробнее об этом в главе 10).
Важно знать, что клиент выбирает на своей стороне порт, который в настоящее
время не используется, и почти всегда подключается к какому-либо известному
порту на стороне сервера. Давайте снова рассмотрим вывод команды netstat из
предыдущего раздела:
Proto Recv-Q Send-Q Local Address
tcp
0
0 10.23.2.4:47626

Foreign Address
10.194.79.125:5222

State
ESTABLISHED

Зная о соглашениях о нумерации портов, вы можете увидеть, что это соединение,
вероятно, было инициировано локальным клиентом r удаленному серверу, потому
что порт на локальной стороне (47626) выглядит как динамически назначаемый
номер, в то время как удаленный порт (5222) является хорошо известной службой,
указанной в /etc/services (служба обмена сообщениями Jabber, или XMPP, если
быть точным). Существует множество подключений к порту 443 (по умолчанию
для HTTPS) на большинстве ПК.
ПРИМЕЧАНИЕ
Динамически назначаемый порт называется эфемерным портом.
Однако если локальный порт в выводе хорошо известен, удаленный хост, вероятно,
инициировал соединение. В этом примере удаленный хост 172.24.54.234 подключился к порту 443 на локальном хосте:
Proto Recv-Q Send-Q Local Address
tcp
0
0 10.23.2.4:443

Foreign Address
172.24.54.234:43035

State
ESTABLISHED

Удаленный хост, подключающийся к вашей машине через хорошо известный порт,
подразумевает, что сервер на вашей локальной машине прослушивает этот порт.
Чтобы подтвердить это, перечислите все TCP-порты, прослушиваемые вашей
машиной, с помощью netstat, на этот раз с параметром -l, который показывает
прослушиваемые процессами порты:





$ netstat -ntl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address
Foreign Address
tcp
0
0 0.0.0.0:80
0.0.0.0:*
tcp
0
0 0.0.0.0:443
0.0.0.0:*
tcp
0
0 127.0.0.53:53
0.0.0.0:*
--пропуск--

State
LISTEN
LISTEN
LISTEN

Строка ❶ с локальным адресом 0.0.0.0:80 показывает, что локальная машина прослушивает порт 80 для подключений с любой удаленной машины, то же самое
верно для порта 443 (строка ❷). Сервер может ограничить доступ к определенным

9.17. Транспортный уровень: TCP, UDP и службы   299
интерфейсам, как показано в строке ❸ , где что-то прослушивает соединения
только в интерфейсе localhost. В этом случае используется systemd-resolved — мы
говорили о том, почему он прослушивает с адреса 127.0.0.53 вместо 127.0.0.1, еще
в разделе 9.16. Чтобы узнать больше, примените lsof для определения конкретного
процесса, выполняющего прослушивание (как описано в подразделе 10.5.1).

Номера портов и файл /etc/services
Как узнать, хорошо ли известен порт? Одного способа определить это не существует, но для начала стоит заглянуть в /etc/services, который переводит известные
номера портов в имена. Это обычный текстовый файл. В нем вы встретите записи,
подобные этой:
ssh
smtp
domain

22/tcp
25/tcp
53/udp

# SSH Remote Login Protocol

Первый столбец — это имя, второй — номер порта и конкретный протокол транспортного уровня, который может отличаться от TCP.
ПРИМЕЧАНИЕ
Помимо /etc/services существует онлайн-реестр портов на www.iana.org, который
регулируется документом по сетевым стандартам RFC 6335.
В Linux только процессы, работающие от имени суперпользователя, могут задействовать порты с 1 по 1023, известные также как системные или привилегированные
порты. Все пользовательские процессы могут прослушивать и создавать соединения
с портов 1024 и выше.

Характеристики TCP
TCP популярен как протокол транспортного уровня, потому что требует не так уж
много действий со стороны приложения. Приложение должно знать только, как
открывать (или прослушивать), считывать, записывать и закрывать соединение.
Для приложения это входящие и исходящие потоки данных, процесс почти так же
прост, как работа с файлом.
Тем не менее много действий происходит за кулисами. Во-первых, реализация
TCP должна знать, как разбить исходящий поток данных из процесса на пакеты.
Сложнее всего понять, как преобразовать серию входящих пакетов во входной поток данных, чтобы процессы могли их считывать, особенно когда входящие пакеты
поступают в случайном порядке. Кроме того, хост, использующий TCP, должен
проверять наличие ошибок: пакеты могут теряться или искажаться при отправке
по сети, и реализация TCP должна обнаруживать и исправлять эти ситуации. На
рис. 9.4 показан упрощенный пример того, как хост может применять TCP для
отправки сообщения.

300   Глава 9. Сеть и ее конфигурация

Рис. 9.4. Отправка сообщения по протоколу TCP
К счастью, пользователю не нужно с этим взаимодействовать, стоит только знать,
что реализация TCP Linux в основном находится в ядре и что утилиты, работающие
с транспортным уровнем, как правило, манипулируют структурами данных ядра.
Одним из примеров является система фильтрации пакетов iptables, обсуждаемая
в разделе 9.25.

9.18. Возврат к простой локальной сети   301

9.17.2. Протокол UDP
UDP — это более простой, чем TCP, транспортный уровень. Он определяет передачу
лишь отдельных сообщений, поток данных отсутствует. В то же время, в отличие
от TCP, UDP не будет исправлять потерянные или неупорядоченные пакеты. На
самом деле, хотя UDP имеет порты, у него даже нет соединений! Один хост просто отправляет сообщение с одного из своих портов на порт сервера, и сервер при
желании отправляет что-то обратно. Однако в UDP есть функция обнаружения
ошибок для данных внутри пакета: хост может обнаружить, что пакет поврежден,
но не будет ничего с этим делать.
Если работа TCP похожа на телефонный разговор, то работа UDP подобна отправке письма, телеграммы или мгновенного сообщения (за исключением того,
что мгновенные сообщения более надежны). Приложения, использующие UDP,
часто стремятся отправлять сообщения как можно быстрее. Они не хотят применять усложненный TCP, потому что предполагают, что сеть между двумя хостами
в целом надежна. Они не нуждаются в исправлении ошибок, выполняемом TCP,
потому что у них либо есть собственные системы обнаружения ошибок, либо они
не обращают на них внимания.
Одним из примеров приложения, использующего UDP, является протокол сетевого
времени (Network Time Protocol, NTP). Клиент отправляет короткий простой запрос
на сервер, чтобы узнать текущее время, и получает от сервера такой же короткий
ответ. Поскольку клиент хочет получить ответ как можно быстрее, UDP подходит
для приложения: если ответ с сервера затеряется где-то в сети, клиент может просто
отправить запрос повторно. Другой пример — видеочат. В этом случае картинки
отправляются с помощью UDP, и если некоторые фрагменты теряются по пути,
клиент на принимающей стороне компенсирует это как может.
ПРИМЕЧАНИЕ
Остальная часть этой главы посвящена более сложным сетевым темам, таким как сетевая
фильтрация и маршрутизаторы, поскольку они относятся к нижним сетевым уровням,
которые мы уже встречали: физическому, сетевому и транспортному. Если хотите,
переходите к следующей главе, чтобы изучить уровень приложений, где все части объединяются в пользовательском пространстве. Вы увидите процессы, которые на самом
деле задействуют сеть, а не просто перекидывают кучу адресов и пакетов.

9.18. Возврат к простой локальной сети
Сейчас мы рассмотрим дополнительные компоненты простой сети, представленные
в разделе 9.4. Эта сеть состоит из одной LAN в качестве подсети и маршрутизатора,
который соединяет ее с остальной частью интернета. Мы рассмотрим:
как хост в подсети автоматически получает свою сетевую конфигурацию;
как настроить маршрутизацию;

302   Глава 9. Сеть и ее конфигурация
что такое маршрутизатор на самом деле;
как узнать, какие IP-адреса использовать для подсети;
как настроить брандмауэры для фильтрации нежелательного трафика из
интернета.
По большей части мы сосредоточимся на IPv4 (хотя бы по той причине, что такие
адреса легче читать), но рассмотрим и отличия IPv6.
Начнем с того, как хост в подсети автоматически получает свою сетевую конфигурацию.

9.19. Протокол DHCP
В соответствии с IPv4 при настройке сетевого хоста на автоматическое получение
конфигурации из сети пользователь указывает ему задействовать протокол динамической настройки хоста (Dynamic Host Configuration Protocol, DHCP) для получения IP-адреса, маски подсети, шлюза по умолчанию и DNS-серверов. Применение
DHCP не только избавляет сетевых администраторов от необходимости вводить эти
параметры вручную, но и дает им другие преимущества, такие как предотвращение
конфликтов IP-адресов и минимизация последствий при изменениях сети. Редко
встречается такая сеть, которая не использует DHCP.
Чтобы хост мог настроить свою конфигурацию с помощью DHCP, он должен
иметь возможность отправлять сообщения на DHCP-сервер в своей подключенной
сети. Поэтому каждая физическая сеть должна иметь собственный DHCP-сервер,
и в простой сети (например, как в разделе 9.1) маршрутизатор обычно действует
как DHCP-сервер.
ПРИМЕЧАНИЕ
При отправке первоначального запроса DHCP хост даже не знает адреса DHCP-сервера,
поэтому он передает широковещательный запрос всем хостам (обычно всем хостам
в своей физической сети).
Когда компьютер просит DHCP-сервер назначить ему IP-адрес, на самом деле он
запрашивает аренду адреса на определенное время. Когда оно истекает, клиент
может попросить продлить договор аренды.

9.19.1. DHCP-клиенты Linux
Хотя существует множество различных типов систем управления сетями, только
два DHCP-клиента могут фактически арендовать адреса. Традиционным типовым клиентом является программа dhclient, работающая по стандартам Internet
Software Consortium (ISC, Консорциум по разработке ПО для сети интернет).
Однако у systemd-networkd теперь также есть встроенный DHCP-клиент.

9.20. Автоматическая настройка сети IPv6   303
При запуске dhclient сохраняет свой идентификатор процесса в /var/run/dhclient.
pid и информацию об аренде в /var/lib/dhcp/dhclient.leases.
Вы можете протестировать dhclient вручную в командной строке, но перед этим
должны удалить любой маршрут шлюза (см. подраздел 9.11.2). Чтобы запустить
тестирование, просто укажите имя сетевого интерфейса (в примере это enp0s31f6):
# dhclient enp0s31f6

В отличие от dhclient, DHCP-клиент systemd-networkd не может быть запущен
вручную в командной строке. Конфигурация, описанная на странице руководства
systemd.network(5), находится в каталоге /etc/systemd/network, но, как и другие виды
конфигурации сети, может быть автоматически сгенерирована с помощью Netplan.

9.19.2. DHCP-серверы Linux
Поставьте системе Linux задачу запустить DHCP-сервер, который обеспечивает хороший контроль над выдаваемыми им адресами. Но если вы не управляете большой
сетью со множеством подсетей, вам, вероятно, стоит использовать специализированное оборудование маршрутизатора, которое включает встроенные DHCP-серверы.
Вероятно, важнее всего знать о DHCP-серверах следующее: чтобы избежать проблем с конфликтующими IP-адресами или неправильными конфигурациями, необходимо, чтобы в одной подсети работал только один сервер.

9.20. Автоматическая настройка сети IPv6
DHCP работает хорошо, но он основывается на определенных предположениях, в том
числе на том, что DHCP-сервер будет доступен, он правильно реализован и стабилен
и может отслеживать и поддерживать аренду адресов. Хотя есть версия DHCP для
IPv6 под названием DHCPv6, существует и более распространенная альтернатива.
IETF воспользовалась большим адресным пространством IPv6 для разработки
нового способа настройки сети, который не требует центрального сервера. Он
называется конфигурацией без состояния, поскольку клиентам не нужно хранить
какие-либо данные, к примеру назначения аренды.
Конфигурация сети IPv6 без сохранения состояния начинается с сети локального
канала. Напомним, что эта сеть включает адреса с префиксом fe80::/64. Поскольку
в сети локального канала связи имеется так много доступных адресов, хост может
сгенерировать адрес, который вряд ли будет дублироваться где-либо в сети. Кроме
того, сетевой префикс уже зафиксирован, поэтому хост может транслировать в сеть,
спрашивая, использует ли какой-либо другой хост в сети этот адрес.
Как только у хоста появится локальный адрес канала, он может определить глобальный адрес. Это делается прослушиванием сообщения об объявлении маршрутизатора (router advertisement, RA), которое маршрутизаторы иногда отправляют

304   Глава 9. Сеть и ее конфигурация
по локальной сети канала. RA содержит префикс глобальной сети, IP-адрес маршрутизатора и, возможно, информацию DNS. С помощью этих данных хост может
попытаться заполнить часть идентификатора интерфейса глобального адреса
аналогично тому, что он сделал с локальным адресом канала.
Конфигурация без сохранения состояния зависит от префикса глобальной сети
длиной не более 64 бит (другими словами, ее маска сети равна /64 или ниже).
ПРИМЕЧАНИЕ
Маршрутизаторы отправляют RA также в ответ на запросы маршрутизаторов от хостов.
Эти и некоторые другие сообщения являются частью протокола ICMP для IPv6 (ICMPv6).

9.21. Настройка Linux в качестве маршрутизатора
Маршрутизаторы — это просто компьютеры с несколькими физическими сетевыми
интерфейсами. Вы можете легко настроить компьютер Linux в качестве маршрутизатора.
Рассмотрим пример. Допустим, у вас есть две подсети локальной сети, 10.23.2.0/24
и 192.168.45.0/24. Для их соединения имеется маршрутизатор Linux с тремя сетевыми интерфейсами: два для подсетей локальной сети и один для uplink-связи
с интернетом (рис. 9.5).

В

Б

Г

Д

Е

Рис. 9.5. Две подсети, соединенные маршрутизатором

9.21. Настройка Linux в качестве маршрутизатора   305
Видно, что этот пример не сильно отличается от простого примера сети, рассматриваемого в предыдущей части главы. IP-адреса маршрутизатора для подсетей
локальной сети — 10.23.2.1 и 192.168.45.1. Когда они настроены, таблица маршрутизации выглядит примерно так (на практике имена интерфейсов могут быть
иными; пока игнорируйте uplink-канал к интернету):
# ip route show
10.23.2.0/24 dev enp0s31f6 proto kernel scope link src 10.23.2.1 metric 100
192.168.45.0/24 dev enp0s1 proto kernel scope link src 192.168.45.1 metric 100

Теперь предположим, что хосты в каждой подсети имеют маршрутизатор в качестве
шлюза по умолчанию: 10.23.2.1 для 10.23.2.0/24 и 192.168.45.1 для 192.168.45.0/24.
Если 10.23.2.4 хочет отправить пакет чему-либо за пределами 10.23.2.0/24, он
передает пакет в 10.23.2.1. Например, чтобы пакет попал с 10.23.2.4 (хост A) на
192.168.45.61 (хост Д), он отправляется в 10.23.2.1 (маршрутизатор) через интерфейс enp0s31f6, а затем уходит с маршрутизатора через интерфейс enp0s1.
Однако в некоторых базовых конфигурациях ядро Linux не перемещает пакеты
автоматически из одной подсети в другую. Чтобы включить эту базовую функцию
маршрутизации, вам необходимо задать перенаправление IP-адресов в ядре маршрутизатора с помощью команды
# sysctl -w net.ipv4.ip_forward=1

Как только вы введете эту команду, машина должна начать маршрутизацию пакетов
между подсетями, предполагая, что хосты в них знают, как отправлять свои пакеты
на только что созданный вами маршрутизатор.
ПРИМЕЧАНИЕ
Вы можете проверить состояние переадресации IP-адресов с помощью команды sysctl
net.ipv4.ip_forward.
Чтобы сделать это изменение постоянным после перезагрузки, добавьте его в свой
файл /etc/sysctl.conf. В зависимости от дистрибутива может существовать возможность поместить его в файл /etc/sysctl.d, чтобы обновления дистрибутива
не перезаписывали изменения.
Когда у маршрутизатора есть третий сетевой интерфейс с каналом uplink, эта настройка обеспечивает доступ в интернет для всех хостов в обеих подсетях, поскольку
они настроены на использование маршрутизатора в качестве шлюза по умолчанию.
Вот тут все становится еще сложнее. Проблема в том, что некоторые IPv4-адреса,
например 10.23.2.4, не видны всему интернету — они находятся в так называемых
частных сетях. Чтобы обеспечить подключение к интернету, необходимо настроить
функцию преобразования сетевых адресов (Network Address Translation, NAT) на
маршрутизаторе. Программное обеспечение почти на всех специализированных
маршрутизаторах делает это, здесь нет ничего необычного, но давайте рассмотрим
проблему частных сетей немного подробнее.

306   Глава 9. Сеть и ее конфигурация

9.22. Частные сети (IPv4)
Допустим, вы решили создать собственную сеть. У вас есть компьютеры, маршрутизатор и сетевое оборудование. Учитывая то, что вы уже знаете о простой сети,
ваш следующий вопрос: «Какую IP-подсеть следует использовать?»
Если вам нужен блок интернет-адресов, которые может видеть каждый хост в интернете, вы можете купить его у своего интернет-провайдера. Но поскольку диапазон IPv4-адресов очень ограничен, эта затея становится дорогой и бесполезной
для чего-то большего, чем работа с одним сервером, который может видеть весь
интернет. Большинству пользователей на самом деле не нужны такого рода услуги,
потому что они выходят в интернет как клиенты.
Обычной недорогой альтернативой является выбор частной подсети из адресов,
указанных в документах по стандартам интернета RFC 1918/6761 (табл. 9.2).
Таблица 9.2. Частные сети, определенные RFC 1918 и 6761
Сеть

Маска подсети

Форма CIDR

10.0.0.0

255.0.0.0

10.0.0.0/8

192.168.0.0

255.255.0.0

192.168.0.0/16

172.16.0.0

255.240.0.0

172.16.0.0/12

Вы можете создавать частные подсети по своему усмотрению. Если не планируете
более 254 хостов в одной сети, выберите небольшую подсеть, например 10.23.2.0/24,
которую мы использовали на протяжении всей этой главы. (Сети с этой маской
сети иногда называют подсетями класса C. Хотя этот термин технически устарел,
он все еще полезен.)
В чем же подвох? Хосты в реальном интернете ничего не знают о частных подсетях
и не будут отправлять им пакеты, поэтому без некоторой помощи хосты, составляющие частную подсеть, не могут общаться с внешним миром. Маршрутизатор,
подключенный к интернету (с глобальным, не частным адресом), должен иметь
какой-то способ заполнить пробел между этим соединением и хостами в частной
сети.

9.23. Преобразование сетевых адресов NAT
(маскарадинг IP-адресов)
NAT — это наиболее часто применяемый способ совместного использования одного IP-адреса частной сетью, он почти универсален для домашних и небольших
офисных сетей. В Linux вариант NAT, с которым работают большинство людей,
известен как маскарадинг IP (IP masquerading).

9.23. Преобразование сетевых адресов NAT (маскарадинг IP-адресов)   307
Основная суть NAT заключается в том, что маршрутизатор не просто перемещает
пакеты из одной подсети в другую — он преобразует их по мере перемещения. Хосты
в интернете знают, как подключиться к маршрутизатору, но им ничего не известно
о частной сети, стоящей за ним. Хосты в частной сети не нуждаются в специальной
настройке, маршрутизатор является их шлюзом по умолчанию.
Система работает примерно так:
1. Хост во внутренней частной сети хочет установить соединение с внешним
миром, поэтому он отправляет свои пакеты запросов на соединение через
маршрутизатор.
2. Маршрутизатор перехватывает пакет запроса на соединение, а не передает
его в интернет, где он будет потерян, потому что в общедоступном интернете
нет частных сетей.
3. Маршрутизатор определяет пункт назначения пакета-запроса и открывает
собственное соединение с пунктом назначения.
4. Установив соединение, маршрутизатор подделывает сообщение «соединение
установлено» и отправляет его на исходный внутренний хост.
5. Маршрутизатор теперь является посредником между внутренним хостом
и пунктом назначения. Адресат ничего не знает о внутреннем хосте — соединение на удаленном хосте выглядит так, как будто оно пришло от маршрутизатора.
Это не так просто, как кажется. Обычная IP-маршрутизация знает только IP-адреса
источника и назначения на сетевом интернет-уровне. Но если бы маршрутизатор работал только с интернет-уровнем, каждый хост во внутренней сети мог бы одновременно
устанавливать только одно соединение с одним пунктом назначения (среди прочих
ограничений), поскольку в части пакета интернет-уровня нет информации, позволяющей различать несколько запросов от одного и того же хоста к одному и тому же
пункту назначения. Следовательно, NAT должен выходить за рамки интернет-уровня
и анализировать пакеты, чтобы извлекать больше идентифицирующей информации,
в частности номера портов UDP и TCP транспортных уровней. UDP довольно прост,
потому что есть порты, но нет соединений, а транспортный уровень TCP сложнее.
Чтобы настроить компьютер Linux для работы в качестве маршрутизатора NAT,
вы должны активировать следующие функции в конфигурации ядра: фильтрацию
сетевых пакетов (поддержку брандмауэра), отслеживание соединений, поддержку
iptables, полную поддержку NAT и целевую поддержку MASQUERADE. Большинство
ядер дистрибутива поставляются с ними.
Затем нужно реализовать несколько сложно выглядящих команд iptables, чтобы
маршрутизатор выполнял NAT для своей частной подсети. Вот пример, который
применим к внутренней сети Ethernet на enp0s2, совместно использующей внешнее
соединение на enp0s31f6 (вы узнаете больше о синтаксисе iptables в разделе 9.25):

308   Глава 9. Сеть и ее конфигурация
# sysctl -w net.ipv4.ip_forward=1
# iptables -P FORWARD DROP
# iptables -t nat -A POSTROUTING -o enp0s31f6 -j MASQUERADE
# iptables -A FORWARD -i enp0s31f6 -o enp0s2 -m state --state
ESTABLISHED,RELATED -j ACCEPT
# iptables -A FORWARD -i enp0s2 -o enp0s31f6 -j ACCEPT

Скорее всего, вам никогда не понадобится вручную вводить эти команды, если вы
не разрабатываете собственное программное обеспечение, особенно при наличии
такого большого количества специального оборудования маршрутизаторов. Однако
различные программы виртуализации могут настраивать NAT для использования
в сети для виртуальных машин и контейнеров.
Хотя NAT хорошо работает на практике, помните, что, по сути, это обходной маневр,
который продлевает срок службы адресного пространства IPv4.
IPv6 не нуждается в NAT благодаря большему и более сложному адресному пространству, описанному в разделе 9.7.

9.24. Linux и маршрутизаторы
Сразу после появления широкополосной связи пользователи с не слишком большими потребностями просто подключали свои устройства непосредственно к интернету. Но через некоторое время многие захотели применять для собственных
сетей одно широкополосное соединение, и пользователи Linux, в частности, нередко
устанавливали дополнительную машину для применения в качестве маршрутизатора с NAT.
Производители отреагировали на новый рынок, предложив специализированное оборудование маршрутизатора, состоящее из эффективного процессора,
флеш-памяти и нескольких сетевых портов, чьей мощности было достаточно
для управления типичной простой сетью, запуска важного программного обеспечения, такого как DHCP-сервер, и использования NAT. Когда дело дошло до
программного обеспечения, многие производители обратились к Linux, чтобы
с его помощью приводить в действие свои маршрутизаторы. Они добавили необходимые функции ядра, сократили программное обеспечение пользовательского
пространства и создали интерфейсы администрирования на основе графического
интерфейса.
Почти сразу же после появления первого из этих маршрутизаторов многие пользователи заинтересовались более глубоким изучением аппаратного обеспечения. Один производитель, Linksys, должен был выпустить исходный код своего
программного обеспечения в соответствии с условиями лицензии одного из его
компонентов, и вскоре для маршрутизаторов появились специализированные
дистрибутивы Linux, такие как OpenWRT. (WRT в названии произошло от номера
модели Linksys.)

9.25. Брандмауэры   309
Стоит ли устанавливать эти дистрибутивы на маршрутизаторы? Да, ведь они более
стабильны, чем прошивка производителя, особенно на более старом оборудовании
маршрутизатора, и обычно предлагают дополнительные функции. Например, многие производители требуют, чтобы для создания моста между сетью и беспроводным
подключением вы покупали соответствующее оборудование, но при установленном OpenWRT производитель и возраст оборудования уже не имеют значения.
Это связано с тем, что на маршрутизаторе используется действительно открытая
операционная система, которой все равно, какое оборудование применяется, лишь
бы оно поддерживалось.
Вы можете использовать большую часть сведений, изложенных в этой книге, для
изучения внутренних компонентов встроенного программного обеспечения Linux,
хотя, конечно, столкнетесь с различиями, особенно после входа в систему. Как
и во многих встроенных системах, открытая прошивка, как правило, задействует
BusyBox для предоставления многих функций оболочки. BusyBox — это одна исполняемая программа, которая предлагает ограниченную функциональность для
многих команд Unix, таких как shell, ls, grep, cat и др., что экономит значительный объем памяти. Кроме того, инициализация во встроенных системах во время
загрузки обычно очень проста. Однако, скорее всего, вы не обратите на эти ограничения внимания, потому что пользовательская прошивка Linux часто включает
интерфейс веб-администрирования, аналогичный имеющемуся у производителя.

9.25. Брандмауэры
Маршрутизаторы должны иметь какой-то брандмауэр, чтобы не пропускать нежелательный трафик из вашей сети. Брандмауэр (firewall) — это конфигурация программного и/или аппаратного обеспечения, которая обычно находится на маршрутизаторе между интернетом и сетью меньшего размера, пытаясь гарантировать, что
ничто плохое из интернета не повредит сети меньшего размера. Вы можете настроить
функции брандмауэра на любом хосте для отображения всех входящих и исходящих
данных на уровне пакетов, в отличие от уровня приложений, где серверные программы обычно пытаются самостоятельно контролировать доступ. Брандмауэр на
отдельных компьютерах иногда называют IP-фильтрацией (IPfiltering).
Система может фильтровать, получать, отправлять пакет или пересылать (маршрутизировать) пакеты другому хосту или шлюзу.
Без брандмауэра система просто обрабатывает пакеты и отправляет их дальше.
Брандмауэры устанавливают контрольные точки для пакетов в только что перечисленных пунктах передачи данных. Контрольные точки отклоняют или принимают
пакеты, как правило, на основе таких критериев:
IP-адрес источника или места назначения либо подсеть;
порт источника или места назначения (в информации транспортного уровня);
сетевой интерфейс брандмауэра.

310   Глава 9. Сеть и ее конфигурация
Брандмауэры позволяют работать с подсистемой ядра Linux, которая обрабатывает
IP-пакеты. Давайте это рассмотрим.

9.25.1. Основы брандмауэров Linux
В Linux вы создаете правила брандмауэра в последовательности под названием
цепочка (chain). Набор цепочек составляет таблицу (table). Когда пакет проходит
через различные части сетевой подсистемы Linux, ядро применяет к пакетам правила, входящие в определенные цепочки. Например, новый пакет, поступающий
с физического уровня, классифицируется ядром как ввод (input), поэтому он активирует правила, включенные в цепочки, соответствующие вводу.
Все эти структуры данных поддерживаются ядром. Вся система называется
iptables. Для работы с ней предназначена команда пользовательского пространства
iptables — она позволяет создавать правила и управлять ими.
ПРИМЕЧАНИЕ
Существует система под названием nftables, предназначенная для замены iptables, но на
момент написания книги iptables по-прежнему широко используется. Команда для администрирования nftables — это nft, существует также переводчик iptables в nftables, называемый
iptables-translate, для команд iptables, которые здесь встречаются. Еще больше все усложнила недавно внедренная система под названием bpfilter с другим подходом. Постарайтесь
не увязнуть в специфике команд, ведь главное — это действия, которые они совершают.
Поскольку таблиц может быть много — и каждая со своими наборами цепочек,
которые, в свою очередь, могут содержать множество правил, — движение пакетов
может стать довольно сложным. Однако в основном вы будете работать с одной
таблицей с именем filter, которая управляет основным потоком пакетов. В таблице фильтров есть три основные цепочки: INPUT для входящих пакетов, OUTPUT для
исходящих пакетов и FORWARD для маршрутизируемых пакетов.
На рис. 9.6 и 9.7 показаны упрощенные блок-схемы, в которых правила применяются к пакетам в таблице фильтров. Приведены две иллюстрации, потому что
пакеты могут либо поступать в систему из сетевого интерфейса (рис. 9.6), либо
генерироваться локальным процессом (рис. 9.7).
Как видно из рисунков, входящий пакет из сети может быть задействован пользовательским процессом и не дойти цепочек FORWARD и OUTPUT. Пакеты, сгенерированные
пользовательскими процессами, не достигнут цепочек INPUT или FORWARD.
Процесс усложняется, потому что на этом пути появляется много дополнительных
шагов, помимо этих трех цепочек. Изучите всю диаграмму происходящего, найдя
в интернете статьи на тему «Поток пакетов Linux в сетевом фильтре netfilter», но
помните, что в эти диаграммы попытались включить все возможные сценарии
поступления и передачи пакетов. Часто помочь разобраться может разбиение диаграммы по источникам пакетов, как показано на рис. 9.6 и 9.7.

9.25. Брандмауэры   311

Рис. 9.6. Последовательность обработки входящих пакетов из сети

Рис. 9.7. Последовательность обработки цепочек для входящих пакетов
от локального процесса

9.25.2. Настройка правил брандмауэра
Посмотрим, как система iptables работает на практике. Начнем с изучения текущей
конфигурации с помощью команды
# iptables -L

Вывод обычно представляет собой пустой набор цепочек, как показано далее:
Chain INPUT (policy ACCEPT)
target
prot opt source

destination

Chain FORWARD (policy ACCEPT)
target
prot opt source

destination

Chain OUTPUT (policy ACCEPT)
target
prot opt source

destination

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

312   Глава 9. Сеть и ее конфигурация
DROP указывает ядру отбросить пакет. Чтобы установить политику в цепочке, используйте команду iptables -P следующим образом:
# iptables -P FORWARD DROP

ВНИМАНИЕ
Не изменяйте политику на своем компьютере, пока не прочтете этот раздел целиком.
Допустим, что кто-то, находящийся по адресу 192.168.34.63, докучает вам. Чтобы
запретить ему общаться с вашей машиной, выполните команду
# iptables -A INPUT -s 192.168.34.63 -j DROP

Параметр -A INPUT добавляет правило к цепочке INPUT. Часть -s 192.168.34.63 указывает IP-адрес источника в правиле, и часть -j DROP приказывает ядру отбросить
любой пакет, соответствующий правилу. Таким образом, ваш компьютер отбросит
любой пакет, поступающий от 192.168.34.63.
Чтобы увидеть, что правило действует, снова запустите команду iptables -L:
Chain INPUT (policy ACCEPT)
target
prot opt source
DROP
all -- 192.168.34.63

destination
anywhere

К сожалению, ваш «друг» в 192.168.34.63 велел всем в своей подсети подключаться
к вашему SMTP-порту (TCP-порт 25). Чтобы избавиться и от этого трафика, запустите команду
# iptables -A INPUT -s 192.168.34.0/24 -p tcp --destination-port 25 -j DROP

В этом примере к исходному адресу добавляется квалификатор маски сети, а также параметр -p tcp для выбора только пакетов TCP. Еще одно ограничение,
--destination-port 25, гласит, что правило должно применяться только к трафику
на порт 25. Список таблиц IP для INPUT теперь будет выглядеть так:
Chain INPUT (policy ACCEPT)
target
prot opt source
DROP
all -- 192.168.34.63
DROP
tcp -- 192.168.34.0/24

destination
anywhere
anywhere

tcp dpt:smtp

Выясняется, что кто-то, кого вы знаете по адресу 192.168.34.37, не может отправить
вам электронное письмо, потому что вы заблокировали этот компьютер. Думая, что
это быстрое решение, вы запускаете команду
# iptables -A INPUT -s 192.168.34.37 -j ACCEPT

Однако это не сработало. Чтобы понять почему, посмотрим на новую цепочку:
Chain INPUT (policy ACCEPT)
target
prot opt source

destination

9.25. Брандмауэры   313
DROP
DROP
ACCEPT

all -- 192.168.34.63
tcp -- 192.168.34.0/24
all -- 192.168.34.37

anywhere
anywhere
anywhere

tcp dpt:smtp

Ядро считывает цепочку сверху вниз, используя первое подходящее правило.
Первое правило не соответствует 192.168.34.37, но соответствует второе, потому что
оно применяется ко всем хостам с 192.168.34.1 по 192.168.34.254, и это правило предписывает отбрасывать пакеты. Когда правило подходит, ядро выполняет действие
и не смотрит дальше вниз по цепочке. (Обратите внимание, что 192.168.34.37 может
отправлять пакеты на любой порт вашего компьютера, кроме порта 25, потому что
второе правило применяется только к порту 25.)
Решение состоит в том, чтобы переместить третье правило наверх. Вначале удалите
третье правило с помощью команды
# iptables -D INPUT 3

Затем вставьте его в начало цепочки с помощью команды iptables -I:
# iptables -I INPUT -s 192.168.34.37 -j ACCEPT

Чтобы вставить правило в другое место цепочки, поместите его номер после имени
цепочки (например, iptables -I INPUT 4 ...).

9.25.3. Стратегии брандмауэра
Хотя ранее мы рассмотрели, как вставлять правила и как ядро обрабатывает цепочки IP-адресов, но еще не изучили стратегий брандмауэра, которые действительно
работают. Поговорим об этом.
Существуют два основных типа сценариев брандмауэра: один для защиты отдельных
машин, где вы устанавливаете правила в цепочке INPUT каждой машины, другой для
защиты сети машин, где устанавливаете правила в цепочке FORWARD маршрутизатора.
В обоих случаях речь о серьезной безопасности не идет, если вы используете политику
по умолчанию ACCEPT и постоянно вставляете правила для отбрасывания пакетов от
источников, которые начинают отправлять ненужные или вредоносные данные. Необходимо разрешать только те пакеты, которым доверяете, и запрещать все остальное.
Предположим, что на вашем компьютере установлен SSH-сервер на TCP-порте 22.
У любого случайного хоста нет причин инициировать подключение к любому
другому порту на вашем компьютере, и вы не должны давать ни одному подобному хосту такой возможности. Чтобы это настроить, сначала установите политику
цепочки INPUT в DROP:
# iptables -P INPUT DROP

Чтобы включить ICMP-трафик (для ping и других утилит), используйте строку
# iptables -A INPUT -p icmp -j ACCEPT

314   Глава 9. Сеть и ее конфигурация
Убедитесь, что можете получать пакеты, которые отправляете как на собственный
сетевой IP-адрес, так и на 127.0.0.1 (localhost). Предположим, что IP-адрес вашего
хоста — my_addr. Выполните следующую команду:
# iptables -A INPUT -s 127.0.0.1 -j ACCEPT
# iptables -A INPUT -s my_addr -j ACCEPT

ВНИМАНИЕ
Не выполняйте эти команды одну за другой на машине, к которой у вас есть только
удаленный доступ. Самая первая команда DROP мгновенно заблокирует доступ, и вы
не сможете восстановить его, пока не вмешаетесь в работу всей системы (например,
перезагрузив компьютер).
Если вы контролируете всю свою подсеть (и доверяете всему в ней), то можете
заменить my_addr своим адресом подсети и маской подсети, например 10.23.2.0/24.
Теперь, даже если мы хотим запретить входящие TCP-соединения, все равно нужно убедиться, что хост может устанавливать TCP-соединения с внешним миром.
Поскольку все TCP-соединения начинаются с пакета SYN (запроса соединения),
то если вы пропускаете все TCP-пакеты, которые не являются SYN-пакетами, все
будет в порядке:
# iptables -A INPUT -p tcp '!' --syn -j ACCEPT

Символ ! указывает на отрицание, так что ! --syn соответствует любому не-SYN
пакету.
Далее, если вы используете удаленный DNS на основе UDP, необходимо принимать
трафик с сервера имен, чтобы ваша машина могла искать имена с помощью DNS.
Выполните это для всех DNS-серверов в /etc/resolv.conf. Примените следующую
команду, где адрес сервера имен — это ns_addr:
# iptables -A INPUT -p udp --source-port 53 -s ns_addr -j ACCEPT

И наконец, разрешите SSH-соединения с кем угодно:
# iptables -A INPUT -p tcp --destination-port 22 -j ACCEPT

Приведенные настройки iptables работают во многих ситуациях, включая
любое прямое подключение, особенно широкополосное, когда злоумышленник
с гораздо большей вероятностью просканирует порты вашего компьютера. Вы
также можете адаптировать эти настройки для маршрутизатора брандмауэра,
используя цепочку FORWARD вместо INPUT и подсети источника и назначения, где
это уместно. Для более сложных конфигураций вам может пригодиться инструмент настройки Shorewall.
В этом обсуждении мы лишь коснулись политики безопасности. Помните, что ее
суть заключается в том, чтобы разрешать только то, что вы считаете приемлемым,
а не пытаться найти и исключить все плохое. Кроме того, IP-брандмауэр — это

9.26. Ethernet, IP, ARP и NDP   315
лишь часть общей картины безопасности (в следующей главе мы рассмотрим
и другие).

9.26. Ethernet, IP, ARP и NDP
Рассмотрим еще одну важную деталь реализации IP через Ethernet. Напомним, что
хост должен поместить IP-пакет внутрь фрейма Ethernet, чтобы передать его через
физический уровень другому хосту. Помните также, что сами фреймы не содержат
информации об IP-адресе — они используют MAC-адреса (аппаратные). Вопрос
заключается в следующем: при построении фрейма Ethernet для IP-пакета как хост
узнает, какой MAC-адрес соответствует IP-адресу пункта назначения?
Обычно мы не задумываемся над этим вопросом, потому что сетевое программное
обеспечение включает автоматическую систему поиска MAC-адресов. В IPv4 это называется протоколом определения адресов (Address Resolution Protocol, ARP). Хост,
использующий Ethernet в качестве физического уровня и IP в качестве сетевого уровня, поддерживает небольшую таблицу, называемую кэшем ARP, которая сопоставляет
IP-адреса с MAC-адресами. В Linux кэш ARP находится в ядре. Чтобы просмотреть
кэш ARP вашей системы, возьмите команду ip neigh (часть neigh будет иметь смысл,
когда вы увидите эквивалент IPv6. Старая команда для работы с кэшем ARP — arp):
$ ip -4 neigh
10.1.2.57 dev enp0s31f6 lladdr 1c:f2:9a:1e:88:fb REACHABLE
10.1.2.141 dev enp0s31f6 lladdr 00:11:32:0d:ca:82 STALE
10.1.2.1 dev enp0s31f6 lladdr 24:05:88:00:ca:a5 REACHABLE

Мы используем параметр -4, чтобы ограничить вывод IPv4. В выводе видны IPадреса и аппаратные адреса хостов, о которых известно ядру. Последнее поле указывает статус записи в кэше. REACHABLE означает, что общение с хостом произошло
недавно, а STALE — что прошло какое-то время и запись должна быть обновлена.
Когда машина загружается, ее кэш ARP пуст. Так как же MAC-адреса попадают
в кэш? Все начинается, когда машина хочет отправить пакет другому хосту. Если
целевой IP-адрес отсутствует в кэше ARP, выполняются следующие действия:
1. Исходный хост создает специальный фрейм Ethernet, содержащий пакет
запроса ARP для MAC-адреса, соответствующего целевому IP-адресу.
2. Исходный хост передает этот фрейм во всю физическую сеть для подсети
цели.
3. Если один из прочих хостов в подсети знает правильный MAC-адрес, он
создает ответный пакет и фрейм, содержащий адрес, и отправляет его исходному хосту. Чаще всего отвечающий хост является целевым, и он просто
сообщает свой MAC-адрес.
4. Исходный хост добавляет пару «IP-адрес — MAC-адрес» в кэш ARP и может
продолжать.

316   Глава 9. Сеть и ее конфигурация
ПРИМЕЧАНИЕ
Помните, что ARP применяется только к системам в локальных подсетях. Чтобы добраться до пунктов назначения за пределами вашей подсети, ваш хост отправляет пакет на
маршрутизатор, и дальнейшее перестает быть вашей проблемой. Конечно, вашему
хосту все равно нужно знать MAC-адрес маршрутизатора, и он может использовать
ARP для его поиска.
Единственная реальная проблема, с которой вы можете столкнуться в ходе работы
с ARP, заключается в том, что кэш вашей системы может устареть, если вы перемещаете IP-адрес с одной карты сетевого интерфейса на другую, потому что карты
имеют разные MAC-адреса (например, при тестировании машины). Системы Unix
аннулируют записи кэша ARP, если через некоторое время не происходит никаких
действий, поэтому не должно быть никаких проблем, кроме небольшой задержки
для недействительных данных, но вы можете немедленно удалить запись кэша
ARP с помощью команды
# ip neigh del host dev interface

На странице руководства ip-neighbour(8) объясняется, как вручную настроить
записи кэша ARP, но вам этого делать не нужно. Обратите внимание на название
страницы.
ПРИМЕЧАНИЕ
Не путайте ARP с обратным протоколом преобразования адресов (RARP). RARP преобразует MAC-адрес обратно в имя хоста или IP-адрес. До того как DHCP стал популярным,
некоторые бездисковые рабочие станции и другие устройства использовали RARP для
получения своей конфигурации, но сегодня он встречается довольно редко.
IPV6: NDP
Вам может быть интересно, почему команды, управляющие кэшем ARP, не
содержат слова «arp» (или, если вы смутно знакомы с этой темой, можете
задаться вопросом, почему мы не применяем arp). В IPv6 есть новый механизм — протокол обнаружения соседей (Neighbor Discovery Protocol, NDP),
используемый в локальной сети канала. Команда ip объединяет ARP с IPv4
и UDP с IPv6. NDP включает в себя два вида сообщений:
• запрос доступных соседей (Neighbor solicitation). С его помощью получают информацию о локальном канале хоста, включая его аппаратный адрес;
• перенаправление (Neighbor advertisement). Используется для ответа на
запрос доступных соседей.
Существует несколько других компонентов NDP, включая сообщения RA,
которые мы рассматривали в разделе 9.20.

9.27. Беспроводная сеть Ethernet   317

9.27. Беспроводная сеть Ethernet
В принципе, беспроводные сети Ethernet (Wi-Fi) незначительно отличаются от
проводных. Как и любое проводное оборудование, они имеют MAC-адреса и используют фреймы Ethernet для передачи и приема данных, и в результате ядро
Linux может взаимодействовать с беспроводным сетевым интерфейсом так же,
как и с проводным. Все на сетевом уровне и выше одинаково, основные различия
заключаются в дополнительных компонентах на физическом уровне, таких как
частоты, сетевые идентификаторы и функции безопасности.
В отличие от проводного сетевого оборудования, которое очень хорошо автоматически приспосабливается к нюансам физической настройки, конфигурация беспроводной сети гораздо более вариативна. Для правильной работы беспроводного
интерфейса Linux требуются дополнительные инструменты настройки.
Взглянем на дополнительные компоненты беспроводных сетей.
Детали передачи. Это физические характеристики, например радиочастота.
Сетевая идентификация. Поскольку несколько беспроводных сетей могут
совместно использовать один и тот же базовый носитель, вы должны уметь
различать их. Параметр SSID (Service Set Identifier, или сетевое имя) является идентификатором беспроводной сети.
Управление. Хотя и возможно настроить беспроводную сеть так, чтобы
хосты напрямую общались друг с другом, однако большинство беспроводных сетей управляются одной или несколькими точками доступа, через
которые проходит весь трафик. Точки доступа часто являются мостами
между беспроводной и проводной сетями, в результате чего обе они выглядят как одна сеть.
Аутентификация. Возможно, вам захочется ограничить доступ к беспроводной сети. Для этого можно настроить точки доступа так, чтобы им требовался
пароль или другой ключ аутентификации еще до того, как они начнут взаимодействовать с клиентом.
Шифрование. В дополнение к ограничению начального доступа к беспроводной сети обычно требуется зашифровать весь трафик, передаваемый по
радиоволнам.
Конфигурация Linux и утилиты, которые обрабатывают эти компоненты, распределены по нескольким областям. Некоторые из них находятся в ядре, Linux имеет
набор беспроводных расширений, которые стандартизируют доступ пользователей
к оборудованию. Что касается пользовательского пространства, то конфигурация
беспроводной сети может усложниться, поэтому большинство людей предпочитают применять инструменты с графическим интерфейсом, такие как настольный
апплет для NetworkManager. Тем не менее стоит взглянуть на то, что скрыто от
глаз пользователя.

318   Глава 9. Сеть и ее конфигурация

9.27.1. Утилита iw
Можно просматривать и изменять устройство пространства ядра и конфигурацию
сети с помощью утилиты iw. Для работы с iw обычно необходимо знать имя сетевого
интерфейса устройства, например wlp1s0 (предсказуемое имя устройства) или wlan0
(традиционное имя). Рассмотрим пример, который выводит данные сканирования
доступных беспроводных сетей (если вы находитесь в городе, вывод информации
будет большим):
# iw dev wlp1s0 scan

ПРИМЕЧАНИЕ
Для работы этой команды сетевой интерфейс должен быть включен (если это не так, запустите команду ifconfig wlp1s0 up), но поскольку он все еще находится на физическом
уровне, не нужно настраивать параметры сетевого уровня, такие как IP-адрес.
Если сетевой интерфейс подключен к беспроводной сети, можете просмотреть
сведения о сети следующим образом:
# iw dev wlp1s0 link

MAC-адрес в выводе этой команды относится к точке доступа, к которой вы в данный момент подключены.
ПРИМЕЧАНИЕ
Команда iw различает имена физических устройств (например, phy0) и сетевых интерфейсов (например, wlp1s0) и позволяет изменять различные настройки для каждого из
них. Вы даже можете создать несколько сетевых интерфейсов для одного физического
устройства. Однако почти всегда будете просто использовать имя сетевого интерфейса.
Примените команду iw для подключения сетевого интерфейса wlp1s0 к незащищенной беспроводной сети network_name следующим образом:
# iw wlp1s0 connect network_name

Подключение к защищенным сетям — это совсем другая история. Для довольно
небезопасной системы, использующей протокол защиты, эквивалентной проводной
WEP (Wired Equivalent Privacy), можно задействовать параметр keys с командой
iw connect. Однако применять WEP не следует, так как она небезопасна и немногие
сети ее поддерживают.

9.27.2. Безопасность беспроводной сети
Для большинства настроек безопасности беспроводной сети Linux использует демон
wpa_supplicant, управляющий как аутентификацией, так и шифрованием интерфейса беспроводной сети. Он может обрабатывать схемы аутентификации WPA2

9.28. Выводы   319
и WPA3 (Wi-Fi Protected Access — защищенный доступ; не применяйте старые небезопасные WPA), а также практически любые методы шифрования, используемые
в беспроводных сетях. При первом запуске демон считывает файл конфигурации
(по умолчанию /etc/wpa_supplicant.conf) и пытается идентифицировать себя
с точкой доступа и установить связь на основе заданного имени сети. Система
хорошо задокументирована, в частности страница руководства wpa_supplicant(8)
очень подробна и ее стоит изучить.
Запуск демона вручную каждый раз, когда вы хотите установить соединение, — это
та еще работа. Да и простое создание файла конфигурации оказывается утомительным из-за большого количества возможных параметров. Что еще хуже, вся
работа по запуску iw и wpa_supplicant просто позволяет вашей системе подключаться к беспроводной физической сети — даже не настраивается сетевой уровень.
Именно в таком случае автоматические менеджеры конфигурации сети, такие как
NetworkManager, значительно облегчают процесс. Хотя они ничего не делают самостоятельно, но знают правильную последовательность и необходимую конфигурацию для каждого шага, обеспечивающие работу беспроводной сети.

9.28. Выводы
Знание позиций и ролей различных сетевых уровней имеет решающее значение для
понимания того, как работает сеть Linux и как выполнять ее настройку. Хотя мы
рассмотрели только основы, более сложные темы на физическом, сетевом и транспортном уровнях аналогичны тому, что вы видели в этой главе. Сами уровни часто
также делятся из-за различных компонентов физического уровня в беспроводной
сети.
Значительная часть действий, описанных в этой главе, выполняется в ядре с помощью некоторых базовых утилит управления пользовательским пространством
для управления внутренними структурами данных ядра, такими как таблицы
маршрутизации. Это традиционный способ работы с сетью. Однако, как и во многих других темах, обсуждаемых в этой книге, некоторые задачи не подходят для
ядра из-за их сложности и необходимости обеспечивать гибкость, и именно здесь
начинают превалировать утилиты пользовательского пространства. В частности,
NetworkManager отслеживает и отправляет запросы в ядро, а затем управляет его
конфигурацией. Другим примером является поддержка протоколов динамической
маршрутизации, таких как BGP (Border Gateway Protocol — протокол пограничного
шлюза), который применяется в крупных интернет-маршрутизаторах.
Но вам, вероятно, уже наскучила настройка сети. Давайте перейдем к ее использованию — на прикладной уровень.

10

Сетевые приложения и службы

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

10.1. Основы работы служб
Понять службы TCP легче, чем многие другие, поскольку они основаны на простых,
непрерывных, двусторонних потоках данных. Возможно, лучший способ увидеть,
как они работают, — напрямую связаться с незашифрованным веб-сервером через

10.1. Основы работы служб   321
TCP-порт 80, чтобы получить представление о том, как данные проходят через это
соединение. Например, для подключения к веб-серверу примера документации
IANA выполните следующую команду:
$ telnet example.org 80

Вы получите подобный ответ, говорящий об успешном подключении к серверу:
Trying some address... some address – IP адрес сервера example.org
Connected to example.org.
Escape character is '^]'.

Теперь введите следующие две строки:
GET / HTTP/1.1
Host: example.org

ПРИМЕЧАНИЕ
HTTP 1.1, как и его предшественник HTTP 1.0, серьезно устарел — сейчас используются
более новые протоколы, такие как HTTP/2, QUICK и разрабатываемый HTTP/3.
После последней строки дважды нажмите клавишу Enter. Сервер должен отправить
большое количество HTML-текста в качестве ответа. Чтобы прервать соединение,
нажмите сочетание клавиш Ctrl+D.
Это упражнение показывает:
что удаленный хост имеет процесс веб-сервера, прослушивающий TCPпорт 80;
telnet — это клиент, который инициировал соединение.

Причина, по которой вы должны прервать соединение с помощью комбинации
клавиш Ctrl+D, заключается в том, что в противном случае соединение может оставаться открытым, поскольку для загрузки большинства веб-страниц требуется несколько запросов. Изучив веб-серверы на уровне протокола, вы не всегда сможете
обнаружить такое поведение. Например, многие серверы быстро отключаются, если
не получают запрос вскоре после открытия соединения.
ПРИМЕЧАНИЕ
telnet изначально предназначался для выполнения входа на удаленные хосты. Клиентской
программы telnet может не оказаться в вашем дистрибутиве по умолчанию, но ее легко
установить в качестве дополнительного пакета. Хотя вход на удаленный сервер с помощью
telnet не совсем безопасен (это мы узнаем позже), клиент telnet может быть полезен для
отладки удаленных служб. telnet не работает с UDP или любым другим транспортным
уровнем, кроме TCP. Если вы ищете сетевой клиент общего назначения, рассмотрите
netcat, описанный в разделе 10.5.3.

322   Глава 10. Сетевые приложения и службы

10.2. Взглянем глубже
В предыдущем примере мы вручную взаимодействовали с веб-сервером по сети
с помощью telnet, задействуя протокол прикладного уровня HTTP. Хотя пользователи обычно для такого рода соединения задействуют веб-браузер, давайте
отойдем от telnet и применим программу командной строки, которая знает,
как обращаться к прикладному уровню HTTP. Мы используем утилиту curl со
специальным параметром для записи сведений о выполненном взаимодействии
в файл trace_file:
$ curl --trace-ascii trace_file http://www.example.org/

ПРИМЕЧАНИЕ
В вашем дистрибутиве может не быть предустановленного пакета curl, но проблем с его
установкой нет.
Вы получите большой HTML-вывод. Проигнорируйте его или перенаправьте
в /dev/null и вместо этого посмотрите на только что созданный файл trace_file.
Если соединение было успешным, первая его часть должна выглядеть примерно
так, как показано далее, в точке, где curl пытается установить TCP-соединение
с сервером:
== Info:
Trying 93.184.216.34...
== Info: TCP_NODELAY set
== Info: Connected to www.example.org (93.184.216.34) port 80 (#0)

Все, что вы видели до сих пор, происходит на транспортном уровне или ниже. Однако если это соединение завершится успешно, curl затем попытается отправить
запрос (header — заголовок) — именно здесь начинается прикладной уровень:




=> Send header, 79 bytes (0x4f)
0000: GET / HTTP/1.1
0010: Host: www.example.org
0027: User-Agent: curl/7.58.0
0040: Accept: */*
004d:

Строка ❶ — это вывод отладки curl, сообщающий, что команда будет делать дальше.
Остальные строки показывают, что curl отправляет на сервер. Это текст, выделенный жирным шрифтом, шестнадцатеричные числа в начале — просто отладочные
смещения, которые curl добавляет, чтобы помочь отслеживать, сколько данных
было отправлено или получено.
В строке ❷ видно, что curl начинает с отправки команды GET серверу (как вы делали с telnet), за которой следует некая дополнительная информация для сервера
и пустая строка. Затем сервер отправляет ответ, сначала с собственным заголовком,
выделенным здесь жирным шрифтом:

10.3. Сетевые серверы   323