Однажды старый и молодой программисты встретились на митапе, чтобы послушать и поговорить о файловых системах.
"FAT32 - простая и универсальная файловая система, использовавшаяся в старых версиях Windows, не поддерживает файлы более 4 Гб; NTFS - родная файловая система современных Windows'ов, поддерживается в Linux с помощью стороннего драйвера ntfs-3g; ext4 - журналируемая файловая система Linux, позволяющая произвести восстановление в случае сбоя; JFS - журналируемая файловая система IBM созданная для серверов, имеет размер блока от 512 байт до 4 Кб и поддерживает максимальный размер файла до 4 Пб; ReiserFS - самая экономная файловая система, поскольку позволяет хранить в одном блоке несколько файлов; XFS - высокопроизводительная файловая система, разработанная Silicon Graphics для накопителей большой ёмкости, поддерживает блоки размером от 512 байт до 64 Кб; Btrfs - файловая система, разработанная компанией Oracle Corporation на основе структуры B-деревьев специально для Linux, работает по принципу copy-on-write; ZFS - файловая система Sun Microsystems, отличающаяся расширенным контролем за носителями информации..." - зачитывал заранее заготовленный текст докладчик.
Молодой программист предлагает старому:
-Слушай, давай в нашем проекте реализуем JFS, прирост производительности на сервере будет гарантирован.
-Нет.
-Ну, тогда давай внедрим поддержку ZFS, она особенно хороша при управлении различными дисковыми устройствами.
-Нет.
-Может тогда интегрируем ReiserFS, если у клиентов будет много мелких файлов, она позволит использовать пространство диска более рационально?
-Нет.
- Ну а что тогда, оставим наших пользователей совсем без поддержки файловых систем? - Погрустневшим голосом спрашивает молодой.
-Вот сейчас досидим митап, а затем вернёмся в офис и напишем слой абстракции VFS, через который приложения пользователей смогут обращаться с файлами и каталогами всех этих файловых систем!
(Притча о пользе абстракций).
Виртуальная файловая система (VFS) - одна из пяти основных подсистем ядра Linux. Она абстрагирует сведения об аппаратных устройствах, предоставляя единообразный интерфейс ввода-вывода приложениям при работе с различными типами файловых систем. В связи с тем, что VFS умеет выполнять переключение между файловыми системами в процессе работы, её также называют виртуальным коммутатором файловых систем.
В общих чертах файловая система - это упорядоченная структура данных (и методы работы с ней), организующая доступ к файлам на блочных устройствах. Ключевыми элементами файловой системы, поддерживаемой VFS, являются суперблоки, индексные описатели (дескрипторы), каталоги, записи каталогов и файлы. Методы представлены набором системных вызовов ядра и функций GLIBC (библиотеки GNU C). Соответствующее соединение структур и методов позволяет говорить о файловой системе и её элементах как об объектах в том смысле, который они имеют в программировании. VFS воплащает две важные особенности объектно-ориентированного подхода. Каждая файловая система имеет свою специфику и если бы не было VFS, то пришлось бы разрабатывать разные версии системных вызовов для каждого типа поддерживаемой файловой системы. Благодаря VFS стало возможным применять одни и те же системные вызовы к различным типам файловых систем. В этом проявляется такое свойство объектов, как полиморфизм. Другая особенность объектов - наследование, заключается в возможности расширения и переопределения функций VFS в различных реализация файловых систем.
Структура данных файловой системы Linux упорядочена иерархически с помощью каталогов и может быть представлена в виде дерева, основанием которого является корневой каталогог "/", являющийся родительским для всех остальных - его ветвей. Содержание частей такой структуры отражается в записях каталогов (directory entry, dentry). Подсистема VSF создаёт эти объекты в оперативной памяти на основании строкового представления имён файлов. Dentry используются для ускорения доступа к файлам.
Индексные дескрипторы (index node, inode) - это объекты, которые содержат метаданные файлов (информацию о владельце, режиме доступа, типе файла и блоках данных, занимаемых файлом). Их идентификаторами являются неотрицательные целые числа, обращаясь к которым ядро может прочитать метаданные файлов.
Сами файлы в файловой системе является логически связанными блоками данных. Каталоги - специальные файлы, которые содержат строки записей соответствия индексных дескрипторов именам файлов.
Суперблок - объект, представляющий метаданные файловой системы (тип, размер, статус, набор функций и другие). Без него Linux не может подключить файловую систему, поэтому он помещается в корень каждой файловой системы сразу же после загрузочной записи и резервируется ещё в нескольких местах устройства хранения, а также может быть создан в режиме реального времени.
Файловая система Linux предоставляет единое глобальное пространство имён для файлов и каталоги всех устройств, даже если на них имеются свои файловые системы. Процесс включения таких устройств в общее пространство называется монтированием и для его осуществления применяется команда mount. Она связывает каталог существующего дерева файловой системы с корневым каталогом файловой системы монтируемого устройства. Место стыковки файловых систем становится точкой монтирования. Ею может быть любой каталог, но есть несколько мест используемых чаще всего: /media - точка автоматического монтирования съёмных накопителей, таких как USB Flash и /mnt - точка монтирования любых устройств в ручном режиме. Синтаксис команды: mount -параметры устройство точка_монтирования.
Чтобы посмотреть как это происходит в автоматическом режиме, сначала откроем системный журнал для просмотра 10 последних строк: sudo tail -f /var/log/syslog и подключим usb flash накопитель.
На экране появится информация о всех этапах взаимодействия системы с подключенным устройством:
- ядро получило сигнал о новом устройстве на шине USB и прочитало информацию о нём;
- зонд MTP-probe проверил устройство на поддержку медиа-протокола Microoft;
- ядро привязало USB накопитель как интерфейсу обобщённых устройств SCSI с именем sg5;
- ядро привязало USB накопитель к интерфейсу дисковых устройств SCSI с именем sdf;
- sdf состоит из 7682048 512-байтных логических блоков (3.93GB);
- на устройстве sdf сформирован 1 раздел: sdf1;
- ядро определяет файловую систему на sdf1: EXT4-fs;
- система инициализации systemd подготавливает точку монтирования: media/v/946e0dd5-efb7-4803-8f12-4557e02e4ea0;
- демон udisksd монтирует устройство /dev/sdf1 к media/v/946e0dd5-efb7-4803-8f12-4557e02e4ea0.
В дистрибутивах Linux, использующих систему инициализации systemd для автомонтирования дисков вместо mount используется инструмент udisks, который состоит из демона udisksd и программы udisksctl. Udisksd предоставляет интерфейс D-BUS подсистемы межпроцессного взаимодействия (IPC) для управления устройствами хранения. Udisksctl запускается пользователями из командной строки для взаимодействия с udisksd.
Разделы - это часть смежных блоков накопителя, логически выделенная для работы с ними как с отдельным устройством. Информация о таком делении содержится в таблице разделов, записанной в первых блоках накопителя. Таблицы разделов могут быть выполнены в формате MBR или GPT. Посмотреть таблицу разделов можно с помощью команды parted: sudo parted -l
Существуют различные форматы таблицы разделов: msdos - формат MBR, поддерживаемый BIOS старых материнских плат; gpt- новый формат, являющийся частью стандарта EFI (Extensible Firmware Interface), предложенного Intel на смену BIOS.
Отключение устройств от файловой системы называется размонтированием. Его можно выполнить с помощью команды umount либо udisksctl. Например, размонтируем USB Flash накопитель sdf1:
sudo udisksctl unmount -b /dev/sdf1
Смонтируем его же вручную. Для этого сначала создадим каталог для монтирования: sudo mkdir /mnt/flashdisk
Затем сделаем его точкой монтирования файловой системы ext4, расположенной на первом разделе устройства sdf:
sudo mount -t ext4 /dev/sdf1 /mnt/flashdisk
Посмотрим что там имеется:
ls -li /mnt/flashdisk
Параметр -i добавляет в вывод команды ls номера индексных дескрипторов файла, а параметр -l добавляет вывод информации, содержащейся в дескрипторе.
Цифры в первом столбце - 11 и 12 - номера дескрипторов каталогов "lost+found" и "testdir".
Первый символ последовательности "drwx------" отражает тип файла, а остальные - права доступа к нему.
Файлы могут быть следующих типов:
- обычный файл, обозначается символом "-";
- каталог, "d";
- файл символьного устройства, "с";
- файл блочного устройства, "b";
- локальный сокет, "s";
- именованный канал, "p";
- символическая ссылка, "l".
Права доступа состоят из триад разрешения r (чтение), w (запись), x (выполнение) для владельца файла, группы владельца и всех остальных. Наличие символа r,w,или x означает что данная операция разрешена для владельца, группы и всех остальных, а символ "-" в соответствующих позициях означает, что операция запрещена. Символьное отражение дано для удобства пользователя. Компьютер оперирует данными в двоичной системе, разрешённая операция для него соответствует 1, а запрещённая 0. Триады таких двоичных записей в свою очередь можно перевести в восьмеричную систему счисления и записывать вместо рёх цифр по одной для обозначения прав владельца группы и файла, например 755 - пользователю разрешено "rwx", группе пользователя "r-x", остальным - тоже "r-x". Для каталога "x" означает разрешение просмотра списка файлов.
Далее в строках вывода команды ls -li указывается число жестких ссылок. Файл размещается в определённых блоках устройства хранения, а жесткие ссылки - это именованные связи между файловым дескриптором и файлом. У одного файла может быть несколько таких связей, в данном случае их 2. Прояснить почему их 2 а не 1 поможет дополнительный параметр -a к команде ls:
ls -lia /mnt/flashdisk
Из вывода этих команд видно, что в каждом каталоге есть записи ".", которые имеют тот же индекс файлового дескриптора, что и сами эти каталоги. Таким образом получается, что первая жёсткая ссылка - это имя каталога, а вторая - '.', которая также ведёт к нему. Жесткие ссылки задаются с помощью команды ln.
Например: sudo ln /mnt/flashdisk/testdir/file1 /mnt/flashdisk/f1
Создадим ещё несколько символических ссылок:
sudo ln /mnt/flsashdisk/testdir/file1 /mnt/flashdisk/lost+found/f1
sudo ln /mnt/flsashdisk/testdir/file1 /mnt/flashdisk/ff1
Теперь у файла с индексным дескриптором 13 имеется 4 жёстких ссылки. Найти их можно с помощью команды find:
sudo find /mnt/flashdisk/ -inum 13
Записи root root в выводе коамнды ls-li - это имя владельца файла и имя группы владельца . Далее идут размер файла в байтах, дата и время его последнего изменения и последней записью идёт имя файла.
Файл "." в каталоге /mnt/flashdisk имеет индексный дескриптор дескриптор с номером 2. Такой номер в файловой системе ext4 присваивается корневому каталогу. Можно посмотреть информацию о нём воспользовавшись программой debugfs:
sudo debugfs /dev/sdf1
В приглашении к вводу команд нужно ввести stat <2>
Расшифровка информации, выведенной на экран:
- тип: каталог
- права доступа: 755
- владелец: root (потому что идентификатор 0)
- группа: root (потому что идентификатор 0)
- ссылок: 4
- блоков: 8
- время создания: Thu Apr 13 22:58:53 2023
- логический диапазон блоков (экстенты): 8502
В экстентах находится содержимое данного дескриптора - список файлов и каталогов для которых данный каталог является родительским.Можно посмотреть содержимое этого блока внутренней командой debugfs: block_dump. Для этого нужно сначала нажать клавишу "q", чтобы появилось приглашение к вводу, а затем ввести block_dump 8502
Слева будет отображена информация в шестнадцатеричных кодах (HEX), а справа то же самое в формате ASCII-символов - имена файлов и каталогов.
Вывести номера индексных дескрипторов можно передав debugfs команду ls -l <2>. Они будут отображены в первом слева столбце.
Выведем информацию файлового дескриптора с номером 13, который соответствует файлу file1 (и жесткой ссылке на него f1): stat <13> .
Поле EXTENTS пустое. Это значит файл не содержит никаких данных.
Для завершения работы с debugfs нужно ввести команду quit.
Узнать общее количество индексных дескрипторов файловой системы, их размер и другие параметры можно прочитав суперблок с помощью команды tune2fs: sudo tune2fs -l /dev/sdf1
Смонтированное с помощью команды mount устройство при перезагрузке компьютера автоматически ремонтируются. Если нужно их снова таким способом смонтировать, стоит проверить не изменилось ли имя устройства. Сделать это можно разными способами, например с помощью команды lsblk:
sudo lsblk -o NAME,MOUNTPOINT,LABEL
В этот раз операционная система присвоила USB Flash накопителю имя sde и автоматически смонтировала файловую систему его единственного раздела sde1 в каталог /media/v/946e0dd5-efb7-4803-8f12-4557e02e4ea0
В выводе lsblk обращают на себя внимание так же странные блочные устройства в большом количестве, именуемые loop (петля). Эти устройства на самом деле обычные файлы, которые были смонтированы как файловые системы и интерпретируются Linux как блочные устройства. Система управления пакетами приложений Snap использует эти петли для того, чтобы предоставить пользователю приложение вместе со всеми необходимым для его работы зависимостями из одного файла. Такие файлы постоянно смонтированы в каталоге /snap.
Можно создать собственное подобное устройство обычными средствами Linux. Для этого сначала нужно заиметь файл, который будет использован в качестве образа для размещения файловой системы. Подходящая команда - dd, которой сначала задаётся источник данных (input file, if), а затем приёмник данных (output file, of), в который она запишет поблочно данные из источника.
Можно создать собственное подобное устройство обычными средствами Linux. Для этого сначала нужно заиметь файл, который будет использован в качестве образа для размещения файловой системы. Подходящая команда - dd, которой сначала задаётся источник данных (input file, if), а затем приёмник данных (output file, of), в который она запишет поблочно данные из источника:
sudo dd if=/dev/zero of=file.img bs=1k count=10000
Представить полученный файл блочным устройством поможет команда losetup: sudo losetup /dev/loop53 file.img
Теперь системе доступно новое блочное устройство loop53. Можно создать на нём файловую систему с помощью команды mkfs:
sudo mkfs -t ext4 /dev/loop53
После того, как блочное устройство loop53 обзавелось собственной файловой системой, его можно монтировать обычным образом:
sudo mkdir /mnt/testloop
sudo mount -t ext4 /dev/loop53 /mnt/testloop
После монтирования с этим виртуальным устройством можно работать на файловом уровне как с настоящим накопителем. В том числе создать новый файл образа, связать его с новой петлёй и создать ещё одну файловую систему и т.д. Этот может быть полезно при создании зашифрованных файловых систем.
Linux также позволяет смонтировать в файловую систему часть оперативной памяти. Для этого в нём реализована поддержка временного файлового хранилища tmpfs и RAM-дисков. Для tmpfs достаточно просто создать новую точку монтирования и выполнить само монтирование.
sudo mkdir /mnt/tmpfs_test
sudo mount -t tmpfs -o size=1024M tmpfs /mnt/tmpfs_test
Можно проверить какая будет скорость записи в этом каталоге с помощью той же команды dd. Укажем ей создать файл tempfile размером 512 Кб частями по 1 Кб:
cd /mnt/tmpfs_test
sync; dd if=/dev/zero of=tmpfile bs=1K count=512; sync
Скорость записи в tempfs: 319 Мб/с
Для сравнения перейдём в каталог testloop и повторим замер:
Скорость записи в loop 131 Мб/c
Запись в файловую систему, размещённую в оперативной памяти, получилась в 2,4 раза быстрее чем на USB Flash. Поэтому, когда нужно работать с большим количеством мелких файлов имеет смысл подумать о применении tmpfs. Однако нужно при этом иметь в виду, что после перезагрузки или выключения компьютера все данные, размещённые там, будут потеряны.
Чтобы создать RAM диски необходимо задействовать специальные модули ядра - brd, для представления этого в виде блочных устройств /dev/ram, либо zRAM, который позволит создавать блочные устройства /dev/zram. Последние отличются тем, что данные в оперативной памяти будут храниться в сжатом виде, что увеличивает доступный для хранения объём за счёт увеличения нагрузки на микропроцессор. Эта технология используется некоторыми дистрибутивами Linux вместо традиционного файла подкачки на жёстком диске.
Те модули, которые не загружаются в ядро автоматически при обработке сигналов от оборудования демоном udev, могут быть задействованы с помощью команд insmod и modprobe. Так, к примеру, поддержка шифрования в петлевых устройствах осуществляется включением модулей cryptoloop и blowfish. Создание сжатого диска в оперативной памяти также начинается с включения соотвествующего модуля ядра: sudo modprobe zram
В интернетах можно найти множество статей, в которых рекомендуют создавать столько zram устройств, сколько потоков поддерживает микропроцессор, чтобы обеспечить максимальную производительность, потому что каждое устройство является однопоточным. Однако, прежде чем это делать, имеет смысл проверить так ли это в текущей системе, ибо прогресс не стоит на месте. Узнать это можно прочитав запись параметра max_comp_streams устройства zram0 в каталоге /sys:
cat /sys/block/zram0/max_comp_streams
У меня 8-поточный процессор и установленное количество потоков для обработки zram0 тоже 8. Значит достаточно одного zram0.
Алгоритм сжатия записывается в параметр comp_algorithm. Там же перечислены и доступные альтернативные варианты:
cat /sys/block/zram0/comp_algorithm
По умолчанию выбран алгоритм lzo-rle. Можно вместо него выбрать zstd, скорость которого ниже, зато степень сжатия выше:
sudo echo zstd > /sys/block/zram0/comp_algorithm
Далее нужно задать размер диска, записав соответствуещее значение в параметр disksize: echo 2G > /sys/block/zram0/disksize
Затем можно сформировать на нём файловую систему и смонтировать его как обычный накопитель, либо подключить в качестве устройства подкачки (swap), воспользовавшись командами mkswap и swapon: mkswap /dev/zram0 swapon /dev/zram0 -p 512
Параметр -p задаёт приоритет использования подкачки на /dev/zram0. Источников подкачки может быть несколько и раньше будет использоваться то, чей приоритет окажется выше.
Посмотреть доступное количество количество памяти подкачки можно с помощью команды: free -h
Для сравнения выполнить эту команду и при отключенноой подкачки на zram0: swapoff /dev/zram0
У zRAM есть также параметры, позволяющие добавлять и удалять устроства по требованию.
cat /sys/class/zram-control/hot_add - выведет на экран индентификаторы доступных zRAM устройств, а запись туда новых значений укажет на добавление новых устройств. Запись индентификатора zRAM устройства в параметр hot_remove - удалит существующее устройсвто:
echo 1 > /sys/class/zram-control/hot_remove
Удаляют модули из ядра той же командой modprobe c параметром -r. А проверяют какие модули загружены, обращаясь к файлу modules из каталога /proc. Информацию о модуле можно получить с помощью команды modinfo, например: modinfo fuse
Кстати, FUSE - файловая система, размещаемая в пользовательском пространстве, куда из ядра маршрутизируются все связанные с ней системные вызовы, что значительно упрощяет создание собственных файловых систем для любых целей, в связи с чем для неё появились "обёртки" в таких языках программирования как Python, Ruby и Java. С её использованием разработали SSHFS, NTFS-3G, GlusterFS и ZFS. В частности это позволяет монтировать файловую систему удалённого сервера и работать с ней как с обычным накопителем, а также монтировать архивы как файловые системы.
Например, смонитруем архив.
Для этого установим программу archivemount из репозитория Ubuntu:
sudo apt install archivemount
Проверяем есть ли в системе группа пользователей fuse:
id -Gn
Проверяем, есть ли текущий пользователь системы в группе fuse:
cat /etc/group | grep fuse
Если пользователя там нет, то добавляем:
sudo useradd -G fuse v
Переходим в каталог /mnt/flashdisk и создаём там тестовый архивный файл:
cd /mnt/flashdisk
sudo tar czf archive.tar.gz *
ls -li
И монтируем этот архив в какой-нибудь свободный каталог. У меня после перезагрузки освободился каталог /mnt/testloop из предыдущей серии опытов. Его и используем:
sudo archivemount archive.tar.gz /mnt/testloop
Посмотрим теперь, что там:
sudo ls -lhF /mnt/testloop
Поддерживаемый форматы файлов, монтируемых таким способом: tar , ustar, pax interchange, cpio, ISO9960, Zip и различные GNU-разновидности на основе tar архивов.
Идём дальше. Если вам, как и мне надоело каждый раз после перезагрузки компьютера повторять все операции монтирования и настройки устройств заново загляните в файл /etc/fstab - туда можно записать всё что надо один раз и Linux в следующие разы будет делать это вместо вас. У команды mount также есть специальный параметр -a для монтирования всех файловых систем, указанных в этом файле.
sudo nano /etc/fstab
Формат записей там следующий:
устройство - точка монтирования - тип файловой системы - параметры - флаг резервного копирования - флаг проверки.
Флаг установлен, если в соответствующей позиции 1 и снят, если там 0. Установка флага резервного копирования включит архивирование файловой системы при создании резервной копии системы командой dump. Установка флага проверки разрешает автоматическое применение команды fsck для поиска ошибок в файловой системе.
Параметр "defaults" указывает на то, что будет использована конфигурация заданная по умолчанию.
Указанием "ro" задаётся монтирование устройства в режиме "только чтение", если нужна ещё и запись, его нужно заменить на "rw"
Параметр "umask" задаёт маску для присвоения прав доступа при создании файлов.
Есит также и другие параметры, которые можно указать. Например user - разрешает монтировать и размонтировать такую систему обычному пользователю, nouser- наоборот запрещает монтирование и размонтирование обычным пользователям.
Вместо длинных идентификаторов можно указывать сокращённые имена устройств, если конфигрурация компьютера не меняется.
Для этого нужно свериться с содержимым каталога /dev/disk/by-uuid/:
ls -l /dev/disk/by-uuid/
Если в дистрибутиве используется systemd можно настроить монтирование и его средствами. Для этого нужно разобраться с правилами написания модулей (units). Пользовательские файлы модулей находятся в /etc/systemd/system. Туда же и следует поместить свой модуль для монтирования какого-либо устройства в файловую систему.
Модули ответственные за монтирование заканчиваются на .mount.
Посмотрим что они из себя представляют:
ls /etc/systemd/system/*.mount
cat /etc/systemd/system/snap-atom-282.mount
Как видно этот файл состоит из трёх секций: [Unit], [Mount] и [Install]. В каждой секции с новой строки перечисляются служебные слова-диррективы, которым после знака "=" присваюваются какие-либо значения.
В секцию [Unit] помещается дирректива "Description", содержащая текстовую строку, в свободной форме описывающую назначение модуля. Сюда же попала дирректива Before. Её переданы названия модулей, которые не будут запущенны до тех пор, пока не запустится данный модуль.
Секция [Mount] содержит диррективы, описывающие что и как должно быть смонтировано. Диррективе "What" присваивается абсолютный путь к устройству, которое будет смонтировано, Wheare - абсолютный путь до точки монтирования, Type - тип монтируемой файловой системы, Options - параметры монтирования, LazyUnmount - размонтирует файловую систему устройства как только она больше не занята.
Последняя секция, [Install] используется для определения поведения модуля при разрешении (enable) или запрещении (disable) его автоматического включения. С помощью диррективы WantedBy указывается при каком целевом состоянии systemd будет автоматически запускать модуль. Значение multi-user.target означает возможность запуска модуля на этапе приглашения пользователя ко входу в систему (многопользовательский режим без графики).
Создадим свой модуль по образцу исходного:
sudo cp /etc/systemd/system/snap-atom-282.mount /etc/systemd/system/test_loop.mount
sudo nano /etc/systemd/system/test_loop.mount
Проверяем модуль:
systemctl status test_loop.mount
С первого раза не получилось. Выдаёт ошибку: "test_loop.mount: Where= settings doesn't match unit name. Refusing".
Эта ошибка возникает из-за того, что название модуля не отвечает требованию systemd: имя модуля должно отражать абсолютный путь к точке монтирования, причём "/" должен быть заменён на "-". Т.е., если точка монтирования /mnt/testloop1, то файл модуля должен называться mnt-testloop1.mount.
Переименовываем и пробуем ещё раз:
sudo mv /etc/systemd/system/test_loop.mount /etc/systemd/system/mnt-testloop1.mount
sudo systemctl status mnt-testloop1.mount
Теперь ошибок нет. Можно запускать:
sudo systemctl start mnt-testloop1.mount
Проверяем что получилось:
sudo systemctl status mnt-testloop1.mount
ls -l /mnt/testloop1
Работает. Теперь чтобы это происходило автоматически каждый раз при зазрузке системы осталось только включить этот модуль:
sudo systemctl enable mnt-testloop1.mount
Выключить автозагрузку модуля можно применив параметр disable. Остановить текущее выполнение с помощью параметра stop.
Для лучшего понимания процессов, происходящих в Linux при работе с файловой системой, имеет смысл спуститься на уровень системных вызовов, когда запускается та или иная команда. В этом может помочь программа strace, предназначенная для трассировки системных вызовов и сигналов какого-либо процесса.
Например, посмотрим, что происходит, при создании файла командой touch:
sudo strace -o syscl.txt -T -tt -e trace=all touch test.txt
Данная команда запишет в файл syscl.txt все системные вызовы, задействованные командой touch при создании файла, а также время начала каждого системного вызова и длительность его выполнения.
Открываем файл:
mcedit syscl.txt
Каждая строка является системным вызовом и первым идёт execve(). В скобках ему передаётся местоположение команды, которую необходимо запустить, её аргументы, извлечённые из командной строки и переменные окружения. После символа "=" записано значение, возвращаемое системным вызовом. Если бы в процессе его выполнения произошла ошибка, возвращаемое значение было бы равно -1. Подробности можно почерпнуть на соответствующей странице документации: man 2 execve.
Далее следует системный вызов brk(). Получая аргумент NULL, он возвращает верхний маркер окончания доступной памяти. Это нужно чтобы определить, сколько памяти доступно для процесса.
Системный вызов access() проверяет права пользователя на доступ к файлу, указанному в качестве аргумента, в данном случае /etc/ld.so.preload.
В этот файл, если он создан, помещаются пути к библиотекам, которые будут загружены раньше остальных. Это позволяет переназначать системные вызовы используемые в программе. А проверка доступа к файлу необходима для обеспечения безопасности. Если всё прошло успешно, будет возвращено значение 0, а если произошла ошибка, на выходе будет -1.
Следующая строка - вызов openat(). Он открывает файл, путь к которому указан в качестве аргумента, а возвращает дескриптор файла, необходимый для последующих операций с ним. И открываемый здесь файл - ld.so.cache. Он содержит информацию об актуальный для системы версиях разделяемых библиотек и символических ссылок на них, что необходимо для работы динамического линковщика ld.so.
После получения файлового дескриптора, с файлом можно выполнять какие-либо операции.
Системный вызов fstat() получает в качестве аргумента номер файлового дескриптора и возврщащает информацию об этом файле. В переменную st_mode помещается режим доступа, st_size хранит размер файла в байтах.
Полученная в результате этих действий информация передаётся далее системному вызову mmap(). Он создаёт отображение файла в виртуальном адресном пространстве. Первым параметром задаётся адрес участка памяти, с которого начнётся отображение. При задании значения NULL, ядро само выберет адрес. Следующие параметры - размер файла, режим защиты памяти (PROT_READ - только для чтения), тип отображаемого объекта (MAP_PRIVATE - закрытое отображение с механизмом копирования при записи, сам файл не изменяется), файловый дескриптор и смещение (точка отсчёта в содержимом файла с которой начнётся отображение в память).
При успешном выполнении этот системный вызов возвращает указатель на область памяти с отражёнными данными. А если произошла ошибка, возвращаемое значение будет равно -1.
Файл, с которым больше не нужно работать закрывается с помощью системного вызова Close().
Далее открывается файл libc.so.6, который на самом деле является символической ссылкой на установленную в системе библиотеку libc-2.31.so реализующую интерфейс системных вызовов для программ на языке Си к ядру (цифра 6 - номер версии интерфейса).
Все последующие вызовы предназначены для обеспечения выполнения запущенной программы и предоставления ей функций из разделяемых библиотек вплоть до строчки 17:30:28.506110, где наконецто начинается непосредственно работа с файлом, переданным команде touch - "test.txt". Вызов openat() с аргументом O_CREAT создаёт этот файл.
Вызов dup2() копирует файловый дескриптор с номером 3 в файловый дескриптор с номером 0. А с помощью вызова utimensat() задаётся время создания файла.
Трассировка показала, как много интересного скрывает Linux даже казалось бы за самыми простыми и незатейливыми командами для работы с файловой системой. И ключём к этому удивительному миру ядра системы является интерфейс системных вызовов и стандартная библиотека языка Си при нём.
Планы на будущее. Мне осталось проштудировать ещё одну крупную тему - сети, чтобы затем приступить к более практически полезным делам - администрированию серверов, освоению прикладных программ и изучению программирования. Вроде бы по объёму это не так-то и много, но с нуля погрузиться в тему, полную новых понятий, да ещё и порождённых в основном сознанием людей-носителей другого языка, не так-то и просто. Это похоже на путешествие по какой-то сказочной стране, где обстановка меняется с такой скоростью, что едва успеваешь уловить происходящее, а иногда кажется, что мимо пролетело что-то важное, но суть схватить не успел. Вот для того, чтобы хоть частично зафиксировать промежуточные результаты появляются и будт появляться подобные заметки. Это как узелки на пямять - через какое-то время просмотрев эти записи, возможно, легче будет восстановить всю кратину.