Книга: Сетевое программирование. От основ до приложений
Назад: Глава 12. Библиотеки Netlink
Дальше: Глава 14. Введение в сетевое программирование для ОС Windows

Глава 13. Специальные файловые системы

Основная причина, по которой raw-устройства отсутствуют [в Linux], заключается в том, что лично я считаю, что raw-устройства — это глупость.

Линус Торвальдс, linux-kernel mailing list, 1996

Введение

В Unix-подобных ОС с самого начала была реализована введенная Bell Labs концепция «Everything is a file», или «Все есть файл».

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

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

Обычно этот вариант применяется в скриптах, особенно в скриптах оболочки, например Bash. Но иногда возникает желание использовать такой способ в приложениях. Однако в программировании на языках, подобных C++, этот способ взаимодействия используется редко по ряду причин:

• Данные файловые системы различаются между ОС, то есть переносимость таких настроек плохая даже между Unix-подобными системами. Мало того, различия могут быть даже между разными дистрибутивами Linux, потому что содержимое данных файловых систем (ФС) не стандартизовано или стандартизовано недостаточно.

• В ОС семейства Windows /proc и /sys, очевидно, нет.

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

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

Впрочем, некоторые приложения активно используют данные возможности. Например, Linux Netstat из пакета net-tools работает, читая /proc. Хотя на смену Netstat приходит утилита SS, которая этого не делает.

Записи в файлы специальных ФС приложения должны избегать — в основном потому, что приложение не должно конфигурировать систему. Этим должен заниматься системный администратор, который лучше знает, как настроить ОС в каждом конкретном случае. И если он явно не запрашивает конфигурацию, обычно ее делать нельзя.

Сценарии, в которых необходимо конфигурировать что-либо самостоятельно, существуют, но их достаточно мало:

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

• Система «под ключ». Например, программно-аппаратный комплекс. Но даже в этом случае нежелательно, чтобы конфигурированием занималось основное приложение. Вынесите это в установщик или в скрипт настройки. Либо сделайте прошивку с предопределенными настройками.

Приложение, которое не может работать без выполнения специальных действий. Например, менеджер виртуальных машин, который строит между ними сеть на базе TUN- и TAP-устройств. В этом случае ему просто необходимо добавлять и удалять такие устройства.

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

В этой главе мы рассмотрим назначение специальных ФС, например DevFS или ProcFS. Все они описаны в стандарте UNIX Filesystem Hierarchy Standard. Его полезно изучить, чтобы понимать назначение различных каталогов, точек монтирования и файловых систем в Unix-подобных ОС, включая Linux. Однако стоит также учитывать, что нестандартизованные ФС тоже существуют.

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

DevFS

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

Работа со всеми ФС начинается с монтирования ФС.

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

Любая примонтированная ФС с типом devtmpfs создает устройства автоматически и не требует явного создания через вызов mknod(), хотя может понадобиться ее дополнительная настройка через udev.

Все POSIX-совместимые ОС содержат каталог /dev, в который монтируется данная файловая система:

dev on /dev type devtmpfs

(rw,nosuid,relatime,size=8086776k,nr_inodes=2021694,mode=755,inode64)

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

Основная тому причина — историческая. Многие устройства ведут себя как файлы, поэтому концепция «Все есть файл» должна была породить красивую, чистую, поддающуюся сопровождению парадигму операций.

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

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

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

Они работают не с байтами, а с пакетами и дейтаграммами.

• Хотя обмен данными, как правило, осуществляется с помощью вызовов, аналогичных чтению и записи, для установления соединения требуется больше информации, чем просто имя файла. Например, прослушивание TCP-соединений состоит из двух шагов: один должен выполняться, когда сервер начинает прослушивание, а другой — каждый раз, когда подключается клиент.

• «Обычный API», реализующий чтение и запись, может быть реализован, но, вероятно, не будет удобен. Каждый вызов записи будет отправлять пакет, а каждый вызов чтения будет его получать. Если же буфер слишком мал для пакета, этот пакет будет потерян.

• Большинство сетевых приложений не работают с конкретными сетевыми устройствами, они работают на более высоком уровне, который предоставляют транспортные протоколы. Например, веб-браузер устанавливает TCP-соединение к веб-серверу. Через какое устройство будет идти соединение, не важно. Это должен настраивать администратор для всех приложений.

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

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

Однако, как уже говорилось, существует ОС Plan 9, в которой все является файлом. В Plan 9 операции для сетевых устройств, подобные fcntl() либо ioctl(), доступны путем чтения и записи в специальные файлы, аналогичные файлам устройств.

Для Linux применимо решение Glendix, доступное на , — проект, который предлагает порт виртуальной файловой системы /net от Plan9.

Версии ядра Linux до 1.0 создавали сетевые устройства в /dev для целей управления. Скрипт MAKEDEV 1994 года поддерживает /dev/ne[0-3] NE2000 и другие сетевые адаптеры.

Развитие DevFS

Существовало несколько вариантов поддержки устройств в Unix-подобных ОС и в Linux в частности. Один из первых — скрипт MAKEDEV, который через вызов утилиты mknod(), работающей поверх одноименного системного вызова, создавал большой набор файлов устройств.

Например, чтобы создать управляющее TUN/TAP устройство, нужно вызвать:

mknod /dev/net/tun c 10 200

Будет создан файл символьного устройства с major-номером 10 и minor-номером 200.

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

В то время /dev был лишь подкаталогом корневой файловой системы и созданные файлы устройств существовали там всегда. Драйвер устройства выбирался на основе его major-номера.

В Linux такой подход использовался до версии ядра 2.4. Ненужные файлы забивали /dev, путали пользователя, создавали проблемы с безопасностью, а major-номеров, которых было всего 255, не хватало. Кроме того, Linux становилась все более «пользовательской» системой, и нужно было поддержать динамическое извлечение и добавление устройств.

Проблему решила DevFS, но не та, которая используется в современных ядрах, а ее первый вариант. Она позволила драйверу создавать устройство через вызов devfs_register. С ее приходом в /dev перестали создаваться несуществующие устройства, а драйверы смогли устанавливать права на создаваемые ими файлы устройств. Был реализован демон devfsd. Этот демон создавал символические ссылки на файлы устройств, однако сами файлы не трогал.

Такой подход, решив существующие проблемы, создал новые:

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

• В результате устройства не имели предсказуемых имен: имена зависели от последовательности их подключения и еще некоторых факторов.

• Все существующие имена приходилось хранить в невыгружаемой памяти ядра.

• Пользовательские скрипты при загрузке должны были ожидать заполнения иерархии и как-то синхронизироваться.

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

Эти проблемы начали решать с версии ядра 2.6. Первым шагом была реализация SysFS. В ней стали отображаться поддерживаемые драйверами устройства. Общий код создавал структуру каталогов с настройками устройств на основе структур данных в драйвере.

Следующим шагом была реализация демона udevd, который, используя SysFS и набор правил конфигурации, формировал актуальную иерархию устройств в /dev.

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

В /dev монтировался обычный TmpFS.

Постепенно udevd разросся, был интегрирован в SystemD и стал требовать для своей работы большое число пользовательских утилит и библиотек.

Это было неприемлемо для встраиваемых систем и аварийных режимов системы, в которых доступен только Initramfs, поэтому в современной Linux реализован гибридный подход:

• В /dev монтируется DevTmpFS, которая сразу отображает все перечисленные драйверами устройства.

• Если демон udevd меняет какой-либо из файлов, демон становится ответственным за него и управляет им сам. Так же, как он бы это делал с обычным TmpFS.

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

Также существует достаточно старый интерфейс Transport Layer Interface, или TLI, который мы упоминали в главе 1. В нем для того, чтобы получить сокет соответствующего семейства протоколов, необходимо было открыть специальный файл: /dev/ip, /dev/tcp или /dev/udp.

Другой класс устройств, которые обычно не имеют записей в /dev в Linux, — видео­адаптеры. Хотя в некоторых других вариантах Unix они есть. В принципе, простые видеоадаптеры могут быть представлены как устройства кадрового буфера, то есть блочные устройства, в которых блок представляет цвет каждого пикселя.

Ускоренные видеоадаптеры можно представить как символьные устройства, на которые приложения посылают команды.

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

Примерно та же проблема возникает при работе с высокоскоростными сетевыми адаптерами.

В Linux тоже есть устройства /dev/tcp и /dev/udp, в большинстве ядер они отключены. Рассмотрим пример использования такого устройства.

Сначала устройство необходимо создать:

sudo mknod /dev/tcp c 30 36

После этого его можно использовать в скриптах:

#!/bin/bash

 

TCP_HOST=google.com

TCP_PORT=80

 

# Открыть файл на ввод и вывод, связав его с дескриптором 3,

# и перезапустить скрипт.

exec 3<>"/dev/tcp/${TCP_HOST}/${TCP_PORT}"

 

# Отправить запрос в дескриптор.

echo -e "HEAD / HTTP/1.0\r\nHost: ${TCP_HOST}\r\nConnection: close\r\n\r\n" \

    >&3

 

# Прочитать ответ из дескриптора.

cat <&3

В результате получим ответ:

src/book01/ch13/shell/dev-tcp.sh

HTTP/1.0 301 Moved Permanently

Location: http://www.google.com/

Content-Type: text/html; charset=UTF-8

Content-Security-Policy-Report-Only: object-src 'none'; ... https://csp.withgoogle.com/csp/gws/other-hp

Date: Tue, 25 Jun 2024 04:46:38 GMT

Expires: Thu, 25 Jul 2024 04:46:38 GMT

Cache-Control: public, max-age=2592000

Server: gws

Content-Length: 219

X-XSS-Protection: 0

X-Frame-Options: SAMEORIGIN

Получается, что для сети файловый интерфейс вполне применим. Но в том же Bash легко обойтись и без него: в нем хватает сетевых утилит. Например, в некоторых современных дистрибутивах запрос GET выполнить очень просто. Для этого достаточно набрать в консоли:

GET google.com

<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ru"><head><meta content

...

Видим, что сервер вернул HTML-страницу. Аналогично выполняются POST, HEAD и некоторые другие HTTP-запросы. В данном случае GET и прочие — это утилиты из пакета libwww-perl или perl-libwww — в разных дистрибутивах он называется по-разному.

В скриптах чаще всего используется URL. Большинству разработчиков и администраторов этот вариант понятнее, чем использование сетевого файлового интерфейса, и он гораздо лучше переносим.

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

Также очевидно, что теперь, когда интерфейс устоялся, никакого смысла добавлять сетевые устройства в /dev или переписывать работающий код нет. Однако это не означает, что сетевые устройства не могут находиться в /dev/: любой конкретный драйвер может создать узел устройства для того, чтобы пользователь мог с ним взаимодействовать через ioctl(). Например, существует каталог /dev/net, в котором находятся устройства для управления TAP/TUN-интерфейсами, PPP и сетевые блочные устройства:

ls -l /dev/net/

crw-rw-rw- 1 root root tun

Устройство tun, показанное выше, может быть использовано для создания новых интерфейсов.

Делается это примерно следующим образом:

...

 

// Тип параметра для ioctl.

ifreq ifr = {};

 

// Устройство открывается для того, чтобы выполнить ioctl над дескриптором.

int dev_fd = open("/dev/net/tun", O_RDWR);

 

if (-1 == dev_fd)

{

    perror("Opening /dev/net/tun");

}

 

// Тип, имя, флаги создаваемого виртуального интерфейса.

ifr.ifr_flags = IFF_TUN;

std::copy_if(dev_name.cbegin(), dev_name.cend(), ifr.ifr_name,

             [](char c) { return c != '\0'; });

 

// Выполнить ioctl над дескриптором устройства:

if (-1 == ioctl(dev_fd, TUNSETIFF, &ifr))

{

    perror("TUNSETIFF ioctl");

    close(dev_fd);

}

...

О TUN/TAP мы более подробно будем говорить при описании перехвата данных в главе 23.

FDescFS

FDescFS — это специальная файловая система FreeBSD, которая монтируется в /dev/fd и предоставляет доступ к файловым дескрипторам каждого процесса. Ее альтернатива в Linux — каталог /proc/self/fd, который может быть полезен для работы с дескрипторами процесса.

Содержимое FDescFS отображается как список нумерованных файлов, соответствующих открытым файлам процесса, читающего смонтированный каталог. Файлы имеют шаблон имени типа /dev/fd/n, где n — число, которое ссылается на файловые дескрипторы. Например, /dev/fd/0.

ProcFS

Рис. 13.1. Схема каталогов файловой системы ProcFS

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

Обычно монтируется в каталог /proc:

proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)

В первых версиях Unix-программы узнавали о запущенных процессах в системе напрямую, читая структуры из памяти ядра. Например, первые версии коман­ды ps читали псевдофайл /dev/mem и разбирали необработанные данные, но выставлять системные данные непосредственно в пользовательское пространство через /dev/mem достаточно небезопасно.

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

ProcFS, которая появилась в Unix System V, содержала информацию о каждом запущенном в данный момент процессе, которую можно было получить, используя схему /proc/$PID/stuff. Интерфейсы и структуры каталогов могли оставаться прежними, даже если структуры данных в ядре изменялись.

К процессу, из которого делается запрос к /proc, можно обратиться через симлинк self:

ls -l /proc/self

lrwxrwxrwx 1 root root 0 мая 22 00:57 /proc/self -> 991517

Или через его PID, который можно получить, вызвав функцию getpid() либо использовав специальную переменную с именем $$ в оболочке.

Linux расширил содержимое /proc, добавив информацию о состоянии ядра и настройках.

Внимание! ProcFS не стандартизован! В некоторых старых BSD-системах он вообще отсутствует.

Чтение настроек сети через ProcFS

В /proc содержатся информационные файлы, доступные только для чтения, и файлы, доступные для записи, которые могут изменять состояние ядра. С точки зрения сетевого программирования нас прежде всего интересует каталог /proc/self/net или /proc/net, который является ссылкой на self:

ls -ld /proc/net

lrwxrwxrwx 1 root root 8 июл 29 21:39 /proc/net -> self/net

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

cat /proc/net/dev

Inter-| Receive                                                  | Transmit

face  | bytes   packets errs drop fifo frame compressed multicast| bytes

   lo:  9809906   98481    0    0    0     0          0         0   9809906   

eno2:        0       0    0    0    0     0          0         0         0

wlo1: 17673001  155075    0    0    0     0          0         0  43285148

IPv6-адреса можно прочитать из /proc/net/if_inet6:

cat /proc/net/if_inet6

fe800000000000006072680ebbd12345 03 40 20 80     wlo1

00000000000000000000000000000001 01 80 10 80       lo

fe80000000000000004207fffe912345 04 40 20 80  docker0

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

Для записи доступен pktgen — генератор пакетов, о котором мы поговорим далее.

Посмотрим, что вообще может содержать /proc:

net/arp — выводит список ARP-таблиц ядра. Этот файл полезен для получения ассоциации аппаратных адресов с IP-адресами.

• net/atm/ — файлы в этом каталоге содержат настройки и статистику ATM — асинхронного режима передачи. Этот каталог в основном используется, например, для ADSL-подключений.

• net/dev — показанный выше список различных сетевых устройств и статистика приема и передачи данных.

• net/dev_mcast — список многоадресных групп, которые прослушивает каждое из устройств.

• net/igmp — список многоадресных IP-адресов, к которым присоединилась эта система.

• net/ip_mr_cache — список кэшей многоадресной маршрутизации.

• net/ip_mr_vif — список многоадресных виртуальных интерфейсов.

• net/netfilter/nfnetlink_queue — информация об очередях Netfilter, если он включен.

net/netstat — подробный список сетевой статистики: тайм-ауты TCP, отправленные и полученные SYN-cookie и т.п.

net/psched — глобальные параметры планировщика пакетов.

net/rarp — содержит базу данных RARP, если RARP был включен при сборке ядра.

net/raw — необработанная статистика устройства.

net/route — таблица маршрутизации ядра.

net/rt_cache — текущий кэш маршрутизации.

net/snmp — список данных SNMP для используемых сетевых протоколов.

net/sockstat — общая статистика по сокетам.

net/ip_tables_names — список используемых типов IPTables. Содержит одно или несколько из следующих значений: filter, mangle или nat. Существует только в случае, если загружен iptables.

• net/tcp — подробная информация о TCP-сокетах.

• net/udp — подробная информация о UDP-сокетах.

• net/unix — список используемых сокетов домена UNIX.

net/wireless — статистика беспроводных интерфейсов.

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

В старых версиях ядра Linux ioctl() для получения маршрутов не было. Поэтому в старой программе route из пакета net-tools файлы /proc/net/route, /proc/net/ipv6_route, /proc/net/rt_cache использовались для просмотра маршрутов.

Другие протоколы использовали аналогичные файлы, например /proc/net/x25/route или /proc/net/atalk_route.

Настройка параметров сети через ProcFS

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

Внимание! Изменение настроек системы с помощью файлов в каталоге /proc/sys может сделать ядро нестабильным, что потребует перезагрузки системы. Убедитесь в корректности параметров, прежде чем пытаться изменять какое-либо значение в /proc/sys.

Для нас в этом каталоге интересен подкаталог /proc/sys/net, в котором содержатся настройки сети уровня системы. Например, значение по умолчанию для описанной ранее опции IPV6_V6ONLY определяется содержимым файла /proc/sys/net/ipv6/bindv6only:

# cat /proc/sys/net/ipv6/bindv6only

0

# echo 1 > /proc/sys/net/ipv6/bindv6only

# cat /proc/sys/net/ipv6/bindv6only

1

# echo 0 > /proc/sys/net/ipv6/bindv6only

# cat /proc/sys/net/ipv6/bindv6only

0

Максимально допустимый размер вспомогательного буфера для каждого сокета можно установить с помощью /proc/sys/net/core/optmem_max:

# cat /proc/sys/net/core/optmem_max

20480

# echo 20485 > /proc/sys/net/core/optmem_max

# cat /proc/sys/net/core/optmem_max

20485

Состав каталогов определяется набором модулей и опций времени компиляции ядра.

Некоторые файлы конфигурации в /proc/sys/ содержат более одного значения.

Чтобы правильно установить им новые значения, поместите между ними пробел:

echo "4 2 50" > /proc/sys/kernel/acct.

Каталог /proc/sys/net/core/ содержит файлы для настройки взаимодействия между ядром и сетевыми уровнями разных протоколов.

Наиболее важные из этих файлов:

message_burst — устанавливает лимит времени в десятых долях секунды для записи нового предупреждающего сообщения из сетевого кода в журнал ядра. Этот параметр используется для смягчения атак типа «отказ в обслуживании». Значение по умолчанию — 50, то есть сообщения будут записываться не чаще одного за 5 секунд.

• message_cost — устанавливает стоимость каждого предупреждающего сообщения. Чем выше значение параметра, тем больше вероятность того, что предупреждающее сообщение будет проигнорировано. Этот параметр тоже используется для защиты от DoS-атак. Значение по умолчанию — 5.

• netdev_max_backlog — устанавливает максимальное количество пакетов, разрешенных в очереди, когда определенный интерфейс получает пакеты быстрее, чем ядро может их обработать. Значение по умолчанию для этого параметра — 300.

• optmem_max — максимальный размер вспомогательного буфера, разрешенный для каждого сокета.

• rmem_default — размер буфера приема сокета по умолчанию в байтах.

• rmem_max — максимальный размер буфера приема сокета в байтах.

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

wmem_max — максимальный размер буфера передачи сокета в байтах.

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

Каталоги /proc/sys/net/ipv4/ и /proc/sys/net/ipv6/ содержат дополнительные сетевые настройки протоколов IPv4 и IPv6 соответственно.

Рассмотрим несколько файлов из каталога /proc/sys/net/ipv4/:

icmp_destunreach_rate, icmp_echoreply_rate, icmp_paramrob_rate и icmp_timeexeed_rate — максимальная скорость отправки пакетов ICMP на хосты при определенных условиях. Значение 0 выключает задержку.

• icmp_echo_ignore_all и icmp_echo_ignore_broadcasts — ядро будет игнорировать пакеты ICMP ECHO, то есть ping-запросы от каждого хоста или только те, которые исходят с широковещательных и многоадресных адресов соответственно, если значение 1 параметра запрещает ядру отвечать на ping-запросы.

• ip_default_ttl — TTL, время жизни по умолчанию, ограничивающее количество промежуточных узлов, которые может пройти данный пакет, прежде чем достигнет пункта назначения или будет отброшен. Увеличение этого значения может снизить производительность системы.

• ip_forward — разрешить интерфейсам в системе пересылать пакеты друг другу. По умолчанию для этого файла установлено значение 0. Запись в этот файл значения 1 включает пересылку.

• ip_local_port_range — диапазон портов, которые будут использоваться TCP или UDP для эфемерных портов. Первое число указывает младший порт, а второе — старший порт. Любые системы, которым требуется больше портов, чем по умолчанию от 1024 до 4999, должны использовать диапазон от 32 768 до 61 000.

• tcp_fin_timeout — значение по умолчанию для опции TCP_LINGER2.

• tcp_retries1 — количество разрешенных повторных передач для ответа на входящее соединение. По умолчанию 3.

• tcp_retries2 — количество разрешенных повторных передач TCP-пакетов. По умолчанию 15.

tcp_syn_retries — ограничение на количество повторных передач SYN пакета при установке соединения.

В каталогах /proc/sys/net/ipv4/ и /proc/sys/net/ipv6/ существует ряд других каталогов, каждый из которых охватывает отдельный аспект сетевого стека:

conf/ позволяет настраивать каждый системный интерфейс отдельно:

• conf/default/ — настройки по умолчанию для несконфигурированных устройств.

• conf/all/ — настройки, переопределяющие все специальные конфигурации.

• neigh/ — настройки для связи с хостами, напрямую подключенными к системе, то есть соседями по сети. Также содержит различные настройки для систем, удаленных не более чем на один переход.

route/ — настройки, отвечающие за маршрутизацию. В отличие от conf/ и neigh/ данный каталог применим к маршрутизации через любые интерфейсы в системе. Многие из этих параметров, например max_size, max_delay и min_delay, относятся к управлению размером кэша маршрутизации.

Обратите внимание, что списки параметров конфигурации интерфейсов для IPv4 и IPv6 различаются:

/proc/sys/net/ipv4/conf/eno2

/proc/sys/net/ipv6/conf/eno2

accept_local

accept_redirects

accept_source_route

arp_accept

arp_announce

arp_filter

arp_ignore

arp_notify

bc_forwarding

bootp_relay

disable_policy

disable_xfrm

drop_gratuitous_arp

drop_unicast_in_l2_multicast

force_igmp_version

forwarding

igmpv2_unsolicited_report_interval

igmpv3_unsolicited_report_interval

ignore_routes_with_linkdown

log_martians

mc_forwarding

medium_id

accept_dad

accept_ra

accept_ra_defrtr

accept_ra_from_local

accept_ra_min_hop_limit

accept_ra_mtu

accept_ra_pinfo

accept_ra_rt_info_max_plen

accept_ra_rt_info_min_plen

accept_ra_rtr_pref

accept_redirects

accept_source_route

addr_gen_mode

autoconf

dad_transmits

disable_ipv6

disable_policy

drop_unicast_in_l2_multicast

drop_unsolicited_na

enhanced_dad

force_mld_version

force_tllao

promote_secondaries

proxy_arp

proxy_arp_pvlan

route_localnet

rp_filter

secure_redirects

send_redirects

shared_media

src_valid_mark

tag

forwarding

hop_limit

ignore_routes_with_linkdown

ioam6_enabled

ioam6_id

ioam6_id_wide

keep_addr_on_down

max_addresses

max_desync_factor

mc_forwarding

mldv1_unsolicited_report_interval

mldv2_unsolicited_report_interval

mtu

ndisc_notify

ndisc_tclass

optimistic_dad

proxy_ndp

ra_defrtr_metric

regen_max_retry

router_probe_interval

router_solicitation_delay

router_solicitation_interval

router_solicitation_max_interval

router_solicitations

rpl_seg_enabled

seg6_enabled

seg6_require_hmac

stable_secret

suppress_frag_ndisc

temp_prefered_lft

temp_valid_lft

use_oif_addrs_only

use_optimistic

use_tempaddr

Например, разрешение на пересылку дейтаграмм через параметр forwarding на конкретном адаптере можно настроить отдельно для IPv4 и IPv6.

Для IPv6 также существует каталог /proc/sys/net/ipv6/icmp, в котором хранятся настройки ICMP6-протокола. Для IPv4 они разбросаны по файлам в каталоге /proc/sys/net/ipv4.

Для сокетов типа AF_UNIX настройки хранятся в нескольких каталогах:

/proc/sys/net/unix/ — общие настройки сокета, например максимальный размер дейтаграммы, max_dgram_len.

/proc/sys/fs — в этом каталоге настройки каналов начинаются с префикса pipe-. Например /proc/sys/fs/pipe-max-size — предел, до которого непривилегированный процесс может подстроить емкость буфера канала.

Дополнительную информацию о каталогах и значениях конфигурационных файлов в ProcFS возможно найти в man 7 ip, man 5 proc, man 5 procfs и в документации по ядру: filesystems/proc.txt, networking/ip-sysctl.txt.

SysFS

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

Также позволяет дистрибутивам Linux управлять событиями подключения устройств через соответствующие демоны, такие как udev.

Структура драйверов в ядре автоматически создает каталоги в SysFS, показанные на рис. 13.2, при регистрации драйверов, на основе типа драйвера и значений в их структурах данных.

Рис. 13.2. Схема каталогов и файловой системы SysFS и связанных структур

Все драйверы определенного типа будут иметь одни и те же элементы, доступные через SysFS.

Информация об инициализированных устройствах сгруппирована по разным критериям: типам устройств, шинам, протоколам и т.п.

В разных каталогах существуют символические ссылки на различные устройства.

SysFS монтируется в каталог /sys:

sys on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)

Данная ФС была реализована с целью выделения параметров устройств из ProcFS, которая исторически предназначалась для получения доступа к дереву процессов и должна выполнять именно эту задачу. Но когда в Linux начали добавлять в ProcFS параметры ядра, никакого стандарта еще не существовало. Поэтому /proc вскоре превратился в свалку атрибутов устройств, распределенным по разным подкаталогам.

Начиная с ядра Linux версии 2.6, параметры ядра, связанные с драйверами устройств и файловыми системами, были вынесены в каталог /sys.

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

Старый способ — через /proc.

Новый способ — через /sys, который предпочтительно использовать в новом коде.

Подытожим отличия между двумя ФС:

SysFS содержит более подробную информацию об устройствах. Положение узлов представляет иерархию устройств по подсистемам. Для каждого объекта в модели драйвера создается каталог.

• SysFS более строго организована, чем /proc, так как начиналась с более строгого дизайна, и ей не требуется поддерживать устаревшую функциональность, как это делается в ProcFS. Каждый файл в SysFS содержит значение одного атрибута.

ProcFS допускает произвольные файловые операции, SysFS более ограниченна. Записи ProcFS получают структуру file_operations, которая содержит указатели на функции, запускаемые файловыми системными вызовами. Например: open(), read(), mmap() и т.д., и с ними можно выполнять произвольные действия. В SysFS для работы с атрибутами используются методы show() и store().

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

/sys/bus

Содержит список подкаталогов, каждый из которых представляет тип физической шины, поддерживаемой ядром: USB, PCI, SCSI и т.д.

Каждый тип шины содержит подкаталоги devices и drivers:

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

• drivers — содержит каталоги, описывающие драйверы устройства, зарегистрированные в диспетчере шины. Его файлы — атрибуты, которые показывают текущую конфигурацию драйвера, параметры, которые можно изменить, и символические ссылки, указывающие на каталог физического устройства, к которому привязан драйвер.

/sys/devices

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

Основной каталог, на который ссылаются подкаталоги в каталогах классификаторов:

/sys/block — драйверы блочных устройств. Символические ссылки в этом каталоге — имена устройств, например dm-0, loop1, nvme0n1 и т.д.

• /sys/class — классификация по типу устройства, зарегистрированному в ядре. Класс устройства описывает функциональный тип устройства. Каждый каталог класса содержит подкаталоги символических ссылок на зарегистрированные в этом классе устройства.

/sys/dev — классификация по типу и значениям major, minor устройства. Внутри содержит каталоги block и char, в которых устройства представлены как символические ссылки вида major:minor.

Посмотрим на каталог /sys/class/net/, в котором представлена конфигурация и статистика различных сетевых устройств:

ls -l /sys/class/net

docker0 -> ../../devices/virtual/net/docker0

eno2 -> ../../devices/pci0000:00/0000:00:1f.6/net/eno2

lo -> ../../devices/virtual/net/lo

tun0 -> ../../devices/virtual/net/tun0

wlo1 -> ../../devices/pci0000:00/0000:00:14.3/net/wlo1

Подкаталоги — устройства, а файлы подкаталогов — их параметры:

ls -l /sys/class/net/eno2/

-r--r--r-- 1 root root addr_assign_type

-r--r--r-- 1 root root address

-r--r--r-- 1 root root addr_len

-r--r--r-- 1 root root broadcast

-rw-r--r-- 1 root root carrier

-r--r--r-- 1 root root carrier_changes

-r--r--r-- 1 root root carrier_down_count

-r--r--r-- 1 root root carrier_up_count

lrwxrwxrwx 1 root root device -> ../../../0000:00:1f.6

-r--r--r-- 1 root root dev_id

-r--r--r-- 1 root root dev_port

-r--r--r-- 1 root root dormant

-r--r--r-- 1 root root duplex

-rw-r--r-- 1 root root flags

-rw-r--r-- 1 root root gro_flush_timeout

-rw-r--r-- 1 root root ifalias

-r--r--r-- 1 root root ifindex

-r--r--r-- 1 root root iflink

-r--r--r-- 1 root root link_mode

-rw-r--r-- 1 root root mtu

-r--r--r-- 1 root root name_assign_type

-rw-r--r-- 1 root root napi_defer_hard_irqs

-rw-r--r-- 1 root root netdev_group

-r--r--r-- 1 root root operstate

-r--r--r-- 1 root root phys_port_id

-r--r--r-- 1 root root phys_port_name

-r--r--r-- 1 root root phys_switch_id

drwxr-xr-x 2 root root power

-rw-r--r-- 1 root root proto_down

drwxr-xr-x 4 root root queues

-r--r--r-- 1 root root speed

drwxr-xr-x 2 root root statistics

lrwxrwxrwx 1 root root subsystem -> ../../../../../class/net

-r--r--r-- 1 root root testing

-rw-r--r-- 1 root root threaded

-rw-r--r-- 1 root root tx_queue_len

-r--r--r-- 1 root root type

-rw-r--r-- 1 root root uevent

 

cat /sys/class/net/eno2/mtu

1500

Видно, например, что, используя SysFS, можно получить и установить MTU сетевого адаптера.

Эту же настройку возможно посмотреть и через ProcFS, используя каталог /proc/sys/net/ipv6/conf/eno2/mtu. Но установить ее через ProcFS нельзя, только через SysFS.

/sys/firmware

Каталог прошивки содержит интерфейсы для просмотра и управления прошивкой для конкретной платформы. В /sys/firmware/efi, например, содержатся конфигурационные данные UEFI.

/sys/fs

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

Каталоги содержат настройки для каждой из файловых систем, а также общие настройки. Там же содержатся каталоги для FUSE, контрольных групп BPF и т.д.

/sys/module

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

/sys/kernel

Файлы и подкаталоги с информацией о работающем ядре: подсистема управления памятью, контрольные группы и т.п.

Содержит точки монтирования файловых систем TraceFS и DebugFS.

/sys/power

Управление питанием всей системы и устройств.

DebugFS

DebugFS — специальная файловая система, разработанная для целей отладки. Она дает возможность сделать информацию ядра доступной в пользовательском пространстве.

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

Обычно монтируется в /sys/kernel/debug:

debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime)

Из данных, содержащихся в этой ФС, прикладной разработчик может получить массу информации о драйверах, которую не показывают другие средства. ­Например, данные от драйвера Wi-Fi-адаптера:

# ls -1 /sys/kernel/debug/ieee80211/phy0/

aql_enable

aql_threshold

aql_txq_limit

aqm

force_tx_status

fragmentation_threshold

ht40allow_map

hw_conf

hwflags

iwlwifi -> ../../iwlwifi/0000:00:14.3

keys

long_retry_limit

misc

netdev:p2p-dev-wlo1

netdev:wlo1

power

queues

rate_ctrl_alg

reset

rts_threshold

short_retry_limit

statistics

total_ps_buffered

user_power

wep_iv

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

# cat /sys/kernel/debug/ieee80211/phy0/netdev\:wlo1/txpower

22

 

# cat /sys/kernel/debug/ieee80211/phy0/hwflags

SIGNAL_DBM

SPECTRUM_MGMT

AMPDU_AGGREGATION

SUPPORTS_PS

SUPPORTS_DYNAMIC_PS

MFP_CAPABLE

WANT_MONITOR_VIF

SUPPORT_FAST_XMIT

REPORTS_TX_ACK_STATUS

CONNECTION_MONITOR

AP_LINK_PS

TIMING_BEACON_ONLY

CHANCTX_STA_CSA

SUPPORTS_CLONED_SKBS

SINGLE_SCAN_ON_ALL_BANDS

TDLS_WIDER_BW

SUPPORTS_AMSDU_IN_AMPDU

NEEDS_UNIQUE_STA_ADDR

SUPPORTS_REORDERING_BUFFER

USES_RSS

TX_AMSDU

TX_FRAG_LIST

DEAUTH_NEED_MGD_TX_PREP

BUFF_MMPDU_TXQ

SUPPORTS_VHT_EXT_NSS_BW

STA_MMPDU_TXQ

Таким образом, ядро может выводить через DebugFS «закрытые» сведения или даже управлять некоторыми внутренними настройками. Поэтому доступ к DebugFS может получить только администратор.

Вот пример принудительного опустошения буфера передачи устройства:

# echo 0 > /sys/kernel/debug/iwlwifi/0000\:00\:14.3/iwlmvm/tx_flush

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

TraceFS

TraceFS предназначена для упрощения доступа из пространства пользователя к данным трассировки ядра Linux.

В большинстве дистрибутивов ФС монтируется в каталог /sys/kernel/tracing, но может быть смонтирована в /sys/kernel/debug/tracing, причем одновременно:

tracefs on /sys/kernel/tracing type tracefs (rw,nosuid,nodev,noexec,relatime)

tracefs on /sys/kernel/debug/tracing type tracefs (rw,...,relatime)

Это одна и та же ФС, просто смонтирована два раза в разные точки.

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

TraceFS сохраняет обратную совместимость со старыми приложениями, эмулируя поведение DebugFS.

BPFFS

BPFFS — псевдофайловая система, которая позволяет создавать файлы, ссылающиеся на объекты Berkley Packet Filter.

Существует два варианта BPF:

cBPF, или «классический BPF» — пакетный фильтр, использующий язык фильтрации пакетов, как в tcpdump.

eBPF — виртуальная машина, которая может исполнять скрипты в пространстве ядра ОС, то есть это не пакетный фильтр.

В ядре Linux реализован именно eBPF. Он совместим с cBPF и транслирует его байт-код в представление eBPF прямо в ядре. Программы eBPF либо интерпретируются, либо JIT-компилируются.

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

Встроенный верификатор выполняет статический анализ кода и отклоняет сбойные и плохо влияющие на работу ядра программы. Например, будут отклонены программы с циклами for/while без условий выхода или программы, разыменовывающие указатели без проверки.

Код BPF запускается, как правило, в случае наступления определенного события и выполняется до своего завершения, что похоже на обработчик прерывания.

eBPF используется в обработке сетевого трафика, при трассировке и для выполнения функций безопасности.

Код eBPF программ может использовать некоторые функции ядра для выполнения некоторых системных вызовов и получения доступа к структурам ядра.

Файловая система BPF обычно монтируется в /sys/fs/bpf:

bpf on /sys/fs/bpf type bpf (rw,nosuid,nodev,noexec,relatime,mode=700)

Объекты BPF создаются пользователем при помощи команд системного вызова bpf:

BPF_PROG_LOAD — проверить и загрузить BPF-программу.

BPF_MAP_CREATE — создать BPF-карту. Карта — это структурированная область памяти, которую программы BPF могут использовать совместно.

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

Рис. 13.3. Файловая система BPF

Каждый файловый объект, доступный в пространстве пользователя, увеличивает счетчик ссылок объекта BPF в ядре.

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

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

Рассматривать BPF в данной книге мы не будем по соображениям объема, хотя eBPF — очень успешная разработка в Linux и в разном виде она была перенесена в другие операционные системы, например ОС Windows, где похожий инструмент называется WFP.

ConfigFS

ConfigFS была добавлена в ядро Linux в 2005 году, чтобы обеспечить гибкую настройку модулей пользователем без введения новых ioctl() и системных вызовов. Монтируется в /sys/kernel/config. Предназначается для создания и уничтожения объектов ядра из пространства пользователя. По сути, эта ФС в чем-то обратна SysFS, которая используется для просмотра объектов и управления ими. SysFS нежелательно использовать для создания объектов, ибо ее архитектура этого не предполагала.

Объект ядра создается через вызов mkdir и будет создан, если какой-либо модуль ядра поддерживает создание такого объекта. Удаляется объект через rmdir. Созданные «каталоги» содержат файлы-атрибуты, в которые отображается состояние модуля.

Обычно данная ФС пустая. Ее использует небольшое число модулей, например модуль netconsole.

SockFS

Если посмотреть на список всех смонтированных файловых систем, он будет несколько больше показанного командой mount:

cat /proc/filesystems

nodev   sysfs

nodev   tmpfs

nodev   bdev

nodev   proc

nodev   cgroup

nodev   cgroup2

nodev   cpuset

nodev   devtmpfs

nodev   binfmt_misc

nodev   configfs

nodev   debugfs

nodev   tracefs

nodev   securityfs

nodev   sockfs

nodev   bpf

nodev   pipefs

nodev   ramfs

nodev   hugetlbfs

nodev   devpts

nodev   autofs

nodev   efivarfs

nodev   mqueue

nodev   binder

nodev   pstore

       ext3

       ext2

       ext4

       fuseblk

nodev   fuse

nodev   fusectl

       vfat

       squashfs

nodev   overlay

В списке файловых систем можно заметить такие, как bdev, cpuset, securityfs, sockfs, pipefs, mqueue, binder, pstore и подобные. Это служебные файловые системы.

Например, cpuset нужна для того, чтобы связывать наборы процессоров с задачами, mqueue — для поддержки очередей POSIX, и т.д. Мы не будем описывать их все и коснемся только SockFS.

Для записи данных в сокеты Unix-подобных ОС можно использовать системный вызов write()и системный вызов read() — для их чтения. В Linux для этого используется подсистема VFS — виртуальная файловая система. Это модульная инфраструктура, позволяющая регистрировать модули обработки различных файловых систем.

В коде ядра, в файле net/socket.c, содержится функция sock_init(), которая вызывается при инициализации ОС. В функции регистрируется инфраструктура SysFS для управления сокетами, а затем регистрируется и монтируется специальная файловая система SockFS, описанная структурой sock_fs_type:

static struct file_system_type sock_fs_type =

{

    .name = "sockfs",

    .init_fs_context = sockfs_init_fs_context,

    .kill_sb = kill_anon_super,

};

Причем монтируется она без явной точки монтирования.

Системный вызов __sys_socket, рассмотренный в главе 7, выполняет еще несколько операций, которые мы не описывали, что в итоге приводит к сопоставлению с новым сокетом открытого файла, хранящегося в памяти на SockFS.

Вызов делает следующее:

1. Приводит к вызову функции __sock_create(), в которой вызывается функция sock_alloc(), создающая inode сокета в SockFS через вызов alloc_inode().

2. Вызывает функцию sock_map_fd(), которая выполняет следующие действия:

a) вызывает alloc_file_pseudo() для создания нового файла и привязки его к inode. По сути, это вызов alloc_file(). В качестве обработчиков файловых операций передается структура socket_file_ops, которая перенаправляет файловые вызовы на вызовы сокета;

б) вызывает для этого файла stream_open(), что позволяет работать с ним как с открытым файловым потоком;

в) вызывает fd_install(), чтобы установить в таблице дескрипторов указатель на файл.

В итоге, например, запись через write() в дескриптор сокета приводит к записи в псевдофайл и вызову __sock_sendmsg(), который является обработчиком ­записи.

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

SockFS можно даже монтировать:

mkdir sfs

sudo mount -t sockfs sockfs sfs

ls sfs

ls: невозможно открыть 'sfs': это не каталог

sudo umount sfs

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

Использование служебной файловой системы для поддержки файловых вызовов на дескрипторе, который файлом не является, не отличительная особенность Linux. Даже в ОС Windows есть похожее средство — файловая система IFS, которая позволяет использовать подмножество файлового API для чтения из сокетов и записи в них.

Каталоги /run и /tmp

Каталог /run содержит настройки работающих приложений и некоторую информацию о работающей системе с момента последней загрузки, например файл utmp с зарегистрированными в системе пользователями. Также в этом каталоге хранит свои настройки демон udev. Настройки ядра ОС данная ФС не содержит.

Обычно в этот каталог монтируется TmpFS, хранящая в оперативной памяти информацию, требуемую на раннем этапе загрузки:

run on /run type tmpfs (rw,nosuid,nodev,relatime,mode=755,inode64)

Каталог /run доступен на запись в процессе загрузки, и его используют системные приложения.

В /run не всегда монтируется tmpfs. Если данный каталог создан в ФС, расположенной в постоянном хранилище, например на диске, файлы в этом каталоге должны быть либо удалены, либо усечены системой в начале процесса загрузки.

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

Каталог /var/run является символической ссылкой на /run и существует исторически.

На данный момент /var/run считается неправильным расположением и сохраняется для поддержки устаревшего программного обеспечения.

Правильный каталог — /run.

Каталог /tmp доступен на запись всем пользователям. В этом каталоге может сохранять файлы любое приложение.

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

В большинстве современных ОС TmpFS монтируется в /tmp, как и в /run:

tmpfs on /tmp type tmpfs

(rw,nosuid,nodev,size=8100948k,nr_inodes=1048576,inode64)

Внимание! Поскольку /run и /tmp обычно хранятся в памяти, в них желательно не записывать большие файлы, так как память может переполниться. И если для /tmp обычно устанавливается ограничение на максимальный размер, то для /run — нет, то есть он будет расти до исчерпания всей свободной памяти. После этого часть ФС будет сброшена в раздел подкачки, если он подключен, что снизит производительность работы системы в целом.

Если вы пишете системное приложение, храните временные данные в /run/<каталог_приложения>, в ином случае пользуйтесь для хранения /tmp. Всегда при выходе из процесса удаляйте из него все созданные им файлы.

Функции sysctl() и sysconf()

Функцию sysctl() и системный вызов можно встретить в старых версиях Linux. В ядре Linux 5.5 системный вызов sysctl был удален. Поддержка GLibC была удалена в версии 2.32.

sysctl() отвечает за чтение и запись параметров ядра:

#include <unistd.h>

#include <linux/sysctl.h>

 

int _sysctl(__sysctl_args *args);

Сейчас вместо него рекомендуется использовать /proc/sys. Если вы раньше применяли sysctl(), это тот случай, когда для новых версий ОС придется использовать специальные ФС.

Функция sysconf() нужна для получения параметров системы в процессе выполнения:

#include <unistd.h>

 

long sysconf(int name);

Параметр name — имя получаемой опции.

Функция позволяет вернуть такие параметры, как максимальное количество файлов, которое может открыть процесс, размер страницы памяти, максимальное количество символов в имени системного пользователя и т.п. Нас же она интересует прежде всего потому, что дает возможность получить максимальное количество буферов, которое может быть использовано такими функциями, как sendmsg(), то есть количество элементов массива msg_ioc в структуре msghdr. Для этого используется опция _SC_IOV_MAX, и в общем случае получение максимального количества буферов выглядит так:

extern "C"

{

// Определить макрос, чтобы включить объявление данной функции.

#if !defined(_XOPEN_SOURCE)

#define _XOPEN_SOURCE

#endif

#include <limits.h>

#include <unistd.h>

}

 

int main()

{

    // Получить значение параметра.

    auto buf_count = sysconf(_SC_IOV_MAX);

 

    if (-1 == buf_count)

    {

        // Не удалось получить через sysconf().

        perror("sysconf");

#if defined(IOV_MAX)

        // Установим константу.

        buf_count = IOV_MAX;

        std::cout

            << "Buffers count got via IOV_MAX\n";

#endif

    }

    else

    {

        std::cout

            << "Buffers count got via sysconf()\n";

    }

 

#if defined(IOV_MAX)

    std::cout

         << "IOV_MAX = " << IOV_MAX << "\n";

#endif

    std::cout

        << "Buffers count = "

        << buf_count << std::endl;

 

   return EXIT_SUCCESS;

}

По умолчанию в Linux выделяется 1024 буфера:

build/bin/b01-ch12-sysconf

Buffers count got via sysconf()

IOV_MAX = 1024

Buffers count = 1024

Функция sysconf() предоставляет еще одну полезную возможность — получение размера страницы, что используется, например, в libmnl для установки размера буфера:

#define MNL_SOCKET_BUFFER_SIZE \

    (sysconf(_SC_PAGESIZE) < 8192L ? sysconf(_SC_PAGESIZE) : 8192L)

Резюме

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

Обычно систему через них настраивают системные администраторы. Не рекомендуется вносить такие изменения в «обычных» приложениях, не предназначенных для настройки ОС.

Существует несколько типов виртуальных файловых систем:

DevFS — содержит псевдофайлы устройств, что дает процессам в пользовательском пространстве доступ к устройствам и некоторым параметрам работающего ядра ОС. Все POSIX-совместимые ОС содержат каталог /dev, в который монтируется данная файловая система. Почти все устройства, которые подключены к системе и опознаны соответствующим драйвером, представлены в DevFS.

• ProcFS — используется для представления информации о работающих процессах и параметрах структур данных ядра через файлы. Обычно монтируется в каталог /proc.

• SysFS и ConfigFS — обеспечивают получение системной информации и единый унифицированный способ доступа к параметрам оборудования, файловых систем, модулей, атрибутам драйверов и настройкам ядра из пользовательского пространства. SysFS монтируется в каталог /sys. Данные ФС были реализованы с целью выделения параметров устройств из ProcFS. Есть два способа внести изменения в работающее ядро: старый способ — через /proc — и новый способ — через /sys или через /sys/kernel/config. Новый способ предпочтительнее.

• DebugFS и TraceFS — используются для целей отладки. Они позволяют разработчикам ядра сделать его информацию доступной в пользовательском пространстве. Обычно монтируется в /sys/kernel/debug.

BPFFS — позволяет взаимодействовать с Berkley Packet Filter. Файловая система BPF обычно монтируется в /sys/fs/bpf.

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

Помимо них, приложению доступны каталоги, в которых оно может сохранять данные, необходимые для работы: в /tmp хранятся временные данные, удаляемые между перезагрузками, а в /var более постоянные данные, такие как, например, кэши.

Чтобы не обращаться из приложения к SysFS напрямую, в Linux использовалась функция sysctl(), но в более новых версиях ОС эта функция удалена. На эту функцию чем-то похожа функция sysconf(), возвращающая некоторые параметры системы. В частности, вызвав ее, можно получить максимальное количество буферов, которое способна использовать функция sendmsg(). Эта функция обращается к кэшированным значениям, а не к операционной системе напрямую через системный вызов. Это делает ее довольно быстрой и удобной.

Вопросы и задания

1. Что подразумевается под фразой «Все есть файл» в контексте Unix?

2. Почему в сетевых приложениях на C++ редко используется способ взаимодействия через специальные файлы?

3. Может ли веб-сервер заниматься настройкой системы и почему?

4. Что хранится в DevFS?

5. Почему сетевые устройства обычно не представлены в DevFS?

6. Какие файлы находятся в /proc/sys/?

7. Что может произойти в случае неправильной записи в файлы /proc? А в файлы /dev?

8. Где в /proc находятся сетевые настройки?

9. В каких случаях приложение может заниматься настройкой сетевого стека операционной системы?

10. Различаются ли параметры конфигурации одного и того же интерфейса для IPv4 и IPv6? Почему?

11. Для чего используется SysFS в Linux?

12. В чем разница между SysFS и ProcFS?

13. Где в /sys находятся настройки сетевых адаптеров?

14. Для чего нужны DebugFS и TraceFS? В чем их отличие?

15. Что такое BPFFS? Чем BPFFS может быть полезна разработчику сетевых приложений?

16. Для чего используется ConfigFS?

17. Для чего нужна SockFS?

18. Какие другие специальные файловые системы поддерживаются в Linux кроме перечисленных выше?

19. Какую информацию приложение может хранить в каталоге /tmp?

20. Какую информацию приложение может хранить в каталоге /var? Где именно в его иерархии?

21. Зачем нужна функция sysctl()?

22. Чем может быть полезна функция sysconf()?

23. Напишите программу, которой в качестве ключа сообщается название интерфейса и она только с помощью обращения к /proc получает и выводит MTU этого интерфейса.

Назад: Глава 12. Библиотеки Netlink
Дальше: Глава 14. Введение в сетевое программирование для ОС Windows