TGViewer
Channel Public Channel
BashTex | Linux

BashTex | Linux

@bash_tex

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

Подойдет для разработчиков, системных администраторов и DevOps

Реклама: @dad_admin
Subscribers
2.51K
Photos
77
Videos
13
Links
493
Recent Posts 20 shown
Post #662 173
name_to_handle_at() - как получить файловый объект без обычного пути

В Linux обычно обращаются к файлу через путь:

/etc/hosts


Но ядро умеет работать с файловым объектом иначе. Системный вызов name_to_handle_at() позволяет получить специальный file handle, связанный с объектом filesystem.

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

▪️Получаем handle

Сам системный вызов выглядит примерно так:

int name_to_handle_at(
int dirfd,
const char *path,
struct file_handle *handle,
int *mount_id,
int flags
);


Например, программа может передать:

/etc/hosts


и получить структуру с handle и идентификатором mount.

Важно: это не файловый дескриптор. Handle не позволяет просто сделать read().

▪️Зачем тогда он нужен

Основная идея - получить устойчивое представление объекта внутри filesystem, которое затем можно использовать для повторного обращения к нему.

Для этого существует парный системный вызов:

open_by_handle_at()


Схема получается такой:

path
↓
name_to_handle_at()
↓
file handle + mount ID
↓
open_by_handle_at()
↓
file descriptor


То есть сначала filesystem выдаёт идентификатор объекта, а позже по нему можно снова получить FD.

▪️Почему это отличается от обычного пути

Представим:

/var/data/report


Путь зависит от directory entries. Файл могут переименовать:

mv /var/data/report /var/archive/report


Сам inode при этом остаётся тем же объектом.
File handle предназначен именно для работы с объектом filesystem, а не с конкретным именем в каталоге.

▪️Но есть важное ограничение

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

Кроме того, open_by_handle_at() требует соответствующих привилегий. Обычное приложение не получает возможность произвольно открывать объекты по filesystem handles.

▪️Где это реально встречается

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

И главное: name_to_handle_at() не «обходит filesystem по inode».

Он просит конкретную filesystem предоставить handle для объекта, а затем этот handle может быть использован через соответствующий kernel API.

BashTex 📱 #bash #utils
  • 👍 4
Post #661 288
madvise() - как процесс подсказывает ядру, как он собирается использовать память

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

Linux предоставляет madvise() - системный вызов, через который процесс может сообщить ядру предполагаемый характер использования памяти.

Это особенно интересно для mmap() и больших memory-mapped файлов.

▪️Последовательный доступ

Если программа собирается читать память последовательно:

madvise(addr, length, MADV_SEQUENTIAL);


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

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

▪️Случайный доступ

Для случайного доступа есть:

madvise(addr, length, MADV_RANDOM);


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

▪️Можно сообщить, что страницы больше не нужны

Особенно интересен:

madvise(addr, length, MADV_DONTNEED);


Процесс говорит ядру, что содержимое этой области ему сейчас не требуется.
Для анонимной памяти это может позволить освободить физические страницы. Для файлового отображения поведение связано с отображёнными страницами и page cache.
Важно: это не то же самое, что free().

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

▪️Есть и подсказка WILLNEED

madvise(addr, length, MADV_WILLNEED);


Она сообщает ядру, что страницы, вероятно, скоро понадобятся.

Это может помочь подготовить данные заранее, например перед обработкой большого memory-mapped файла.
Но madvise() именно подсказывает, а не заставляет ядро выполнить конкретную стратегию.

▪️Практический сценарий
Представим программу, которая через mmap() обрабатывает несколько гигабайт файла строго последовательно.

Вместо случайного поведения с page cache она может сделать:

void *p = mmap(NULL, size,
PROT_READ,
MAP_PRIVATE,
fd, 0);

madvise(p, size, MADV_SEQUENTIAL);


Теперь приложение явно сообщает ядру характер будущего доступа.

А когда большой участок больше не нужен:

madvise(p, size, MADV_DONTNEED);


Это может уменьшить давление на память, не требуя немедленно уничтожать само отображение.

▪️Важный нюанс
madvise() не является универсальной кнопкой «ускорить память».
Эффект зависит от конкретного флага, типа mapping, версии ядра и сценария доступа.

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

То есть madvise() - это интерфейс, через который userspace сообщает kernel memory manager: «я примерно знаю, что собираюсь делать с этой памятью».

BashTex 📱 #bash #utils
  • 👍 3
  • 🔥 1
Post #660 283
flock vs fcntl - почему две блокировки одного файла могут вести себя совершенно по-разному

В Linux есть несколько механизмов блокировки файлов. Наиболее часто встречаются flock() и fcntl().

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

▪️flock блокирует сам файл

Простейший вариант:

flock /tmp/app.lock -c 'echo "working"; sleep 10'


Пока первый процесс держит блокировку, второй:

flock -n /tmp/app.lock -c 'echo "working"'


получит ошибку и сразу завершится из-за -n.
Часто этот механизм используют для защиты cron-задач:

flock -n /run/myjob.lock /usr/local/bin/myjob


▪️fcntl работает через диапазоны файла
Здесь можно блокировать не только весь файл, но и конкретный диапазон байтов.

На уровне C:

struct flock lock = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET,
.l_start = 0,
.l_len = 100
};

fcntl(fd, F_SETLK, &lock);


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

▪️Главная ловушка

Процесс A:

flock /tmp/test.lock


Процесс B использует fcntl() для того же файла.

Они не обязаны конфликтовать.

Причина в том, что flock и традиционные POSIX advisory locks через fcntl исторически используют разные пространства блокировок.
То есть наличие:

flock → locked


не означает автоматически:

fcntl → blocked


И наоборот.

▪️Обе блокировки advisory

Ни flock, ни fcntl сами по себе не запрещают процессу открыть и изменить файл.

Если приложение вообще не проверяет блокировку, оно может спокойно сделать:

echo "data" >> /tmp/file


Поэтому механизм работает только тогда, когда все участники системы договариваются его соблюдать.

▪️Есть ещё одна важная разница

Для flock блокировка связана с открытым file description. При fork() дочерний процесс наследует соответствующую блокировку.

У fcntl семантика другая: POSIX locks связаны с процессом и имеют собственные правила поведения при close().

Из-за этого без понимания модели владения FD легко получить ситуацию, когда один close() неожиданно освобождает блокировку.

▪️Практический вывод
Если нужно просто гарантировать, что cron или несколько экземпляров shell-скрипта не выполняются одновременно:

flock -n /run/myjob.lock /usr/local/bin/myjob


обычно достаточно.

Если приложение должно блокировать отдельные диапазоны файла или ему нужна POSIX-модель блокировок, используют fcntl().

И главное: нельзя считать flock и fcntl взаимозаменяемыми только потому, что оба называются file locking.

BashTex 📱 #bash #utils
  • 👍 6
Post #659 265
DEBUG trap - как Bash выполняет код перед каждой командой

В Bash есть специальный DEBUG trap, который позволяет выполнить собственный код непосредственно перед выполнением команды.
Это удобно не только для отладки. Через него можно посмотреть, какие команды реально выполняет скрипт, с какими аргументами и в каком контексте.

▪️Самый простой пример

trap 'echo "CMD: $BASH_COMMAND"' DEBUG

echo "hello"
mkdir /tmp/test
rm -rf /tmp/test


Перед каждой командой Bash вызовет trap:

CMD: echo "hello"
CMD: mkdir /tmp/test
CMD: rm -rf /tmp/test


$BASH_COMMAND содержит команду, которую Bash собирается выполнить.

▪️Можно добавить PID и функцию

trap 'printf "[%s] %s: %s\n" "$$" "${FUNCNAME[1]:-main}" "$BASH_COMMAND"' DEBUG


Теперь при выполнении функций можно получить что-то вроде:

[4217] main: prepare
[4217] deploy: mkdir -p /srv/app
[4217] deploy: cp app.conf /srv/app/


Это уже превращается в простой трассировщик Bash-скрипта.

▪️Но DEBUG не означает буквально «перед каждой строкой»

Trap срабатывает перед выполнением простых команд, for, case, некоторых условных конструкций и других элементов shell execution.
Поведение также зависит от того, включено ли наследование trap внутри функций и subshell.
Например:

trap 'echo "DEBUG: $BASH_COMMAND"' DEBUG

foo() {
echo "inside"
}

foo


Чтобы DEBUG распространялся внутрь функций, часто используют:

set -T


или:

shopt -s functrace


▪️У trap есть интересный побочный эффект
Сам код внутри DEBUG тоже выполняется Bash, поэтому слишком сложный обработчик может сам стать источником неожиданных эффектов.
Для диагностики лучше держать его простым:

trap 'printf "%s\n" "$BASH_COMMAND" >> /tmp/bash-debug.log' DEBUG


А после диагностики обязательно убрать:

trap - DEBUG


▪️Практический сценарий

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

deployment failed


Вместо того чтобы расставлять echo по всему скрипту, временно включаем:

trap 'printf "[%s] %s\n" "$SECONDS" "$BASH_COMMAND"' DEBUG


и получаем последовательность реально выполнявшихся команд.

Важно: DEBUG предназначен прежде всего для трассировки. Для полноценного production-аудита он неудобен: trap может влиять на поведение скрипта и генерировать очень много вывода.

BashTex 📱 #bash #utils
  • 👍 8
Post #658 332
tee + process substitution: один поток и несколько получателей

Иногда нужно одновременно: увидеть вывод команды в терминале, записать его в файл и передать на обработку другой программе Если делать это по очереди - потеряется поток данных. Здесь помогает связка tee + process substitution.

▪️ Что делает tee. tee дублирует поток:


command | tee file.log


вывод остается в терминале и одновременно пишется в file.log

▪️ Несколько получателей. tee может писать сразу в несколько файлов:


command | tee out1.log out2.log


Но иногда нужно не просто файл, а другую команду.

▪️ Process substitution. Bash позволяет подставить вывод команды как файл:


>(command)

Это называется process substitution.

▪️ Комбинируем


command | tee >(grep ERROR > errors.log)


command генерирует поток
tee дублирует его

Одна копия идет в терминал, а другая в grep. Если найден ERROR, запись попадет в errors.log.

▪️ Более реальный пример. Допустим, идет сбор логов:


journalctl -f | tee >(grep ERROR >> errors.log)


Теперь: полный поток остается в терминале, ошибки автоматически сохраняются

▪️ Можно делать несколько обработчиков


journalctl -f | tee \
>(grep ERROR >> errors.log) \
>(grep WARN >> warn.log)


Один поток и сразу несколько фильтров.

BashTex 📱 #bash #utils
Telegram BashTex | Linux Авторский канал для тех, кто хочет глубже погрузиться в мир Linux. Подойдет для разработчиков, системных администраторов и DevOps Реклама: @dad_admin
  • 🔥 6
  • 👍 2
Post #657 347
Архитектура большого bash-скрипта

Пока скрипт на 30 строк - все терпимо. На 300 строк начинается хаос. На 1000 - уже никто не понимает, где init, где логика, где cleanup. Чтобы bash не превратился в лапшу, ему нужна архитектура.

📂 Нормальная структура проекта


project/
├── main.sh
├── lib/
│ ├── log.sh
│ ├── config.sh
│ ├── checks.sh
│ └── deploy.sh
├── conf/
│ └── app.conf
└── tmp/


Где:

main.sh - точка входа
lib/ - функции по темам
conf/ - конфиги
tmp/ - временные файлы


▪️ Деление на модули

Не надо держать все в одном файле.
Лучше так:


source "$(dirname "$0")/lib/log.sh"
source "$(dirname "$0")/lib/checks.sh"


Примеры модулей:

log.sh - логгер
config.sh - загрузка переменных
checks.sh - проверки окружения
actions.sh - основная логика


▪️ Naming: единый стиль

Худший вариант:


doStuff()
x()
RunAll()


Лучше:


log_info()
check_dependencies()
deploy_app()
cleanup_tmp()


Для приватных функций можно префикс:


_internal_parse_config()


▪️ Поток исполнения. В начале файла:


main() {
load_config
check_dependencies
run_tasks
}

main "$@"


Это делает скрипт читаемым как программу, а не как свалку команд.

BashTex 📱 #bash #scripts
Telegram BashTex | Linux Авторский канал для тех, кто хочет глубже погрузиться в мир Linux. Подойдет для разработчиков, системных администраторов и DevOps Реклама: @dad_admin
  • 👍 8
Post #655 512
Почему Permission denied: как найти каталог, на котором ломаются права

Иногда файл имеет правильные права, пользователь состоит в нужной группе, но Permission denied всё равно появляется.

Причина может быть выше по пути: чтобы открыть /var/www/site/config.php, процесс должен иметь право x на каждый каталог в этом пути.

Разберем, как быстро найти место, где ломается доступ.

1️⃣namei показывает права на весь путь

namei -l /var/www/site/config.php


В выводе будут отдельно показаны:

drwxr-xr-x root root /
drwxr-xr-x root root var
drwxr-x--- root www-data www
drwx------ root root site
-rw-r----- root www-data config.php


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

2️⃣Почему важен x у каталога

Для каталога x означает не «запуск», а возможность проходить через него и обращаться к объектам внутри.
Например:

chmod 644 /var/www/site


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

sudo -u www-data cat /var/www/site/config.php


3️⃣Проверяем конкретного пользователя
Если доступ должен быть у www-data:

sudo -u www-data namei -l /var/www/site/config.php


А группы:

id www-data


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

4️⃣ACL тоже могут менять картину

Обычных ls -l иногда недостаточно. Проверить ACL:

getfacl /var/www/site/config.php


Там могут быть дополнительные разрешения для конкретного пользователя или группы.

5️⃣Почему ls -l файл не всегда помогает
Команда:

ls -l /var/www/site/config.php


показывает права самого файла.

Но если проблема находится в /var/www/site, информация о файле сама по себе этого не объяснит.

Именно поэтому namei -l удобен при странных Permission denied: он показывает всю цепочку каталогов, через которую процесс должен пройти до нужного файла.

BashTex 📱 #bash #systemd
  • 👍 9
Post #654 429
veth - как Linux соединяет два network namespace

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

Но как соединить два таких изолированных сетевых мира?

Для этого в Linux есть veth pair - виртуальный Ethernet-кабель с двумя концами.

▪️Создаём два namespace

ip netns add ns1
ip netns add ns2


Теперь создаём пару:

ip link add veth1 type veth peer name veth2


Получили:

veth1 <──────────> veth2


То, что отправлено в один конец, появляется на другом.

▪️Разносим концы по namespace

ip link set veth1 netns ns1
ip link set veth2 netns ns2


Теперь:

namespace ns1        namespace ns2

veth1 <──────────> veth2


В каждом namespace находится только свой конец виртуального кабеля.

▪️Назначаем адреса

ip -n ns1 addr add 10.10.0.1/24 dev veth1
ip -n ns2 addr add 10.10.0.2/24 dev veth2

ip -n ns1 link set veth1 up
ip -n ns2 link set veth2 up


Теперь можно проверить связь:

ip netns exec ns1 ping 10.10.0.2


Пакет проходит примерно так:

process
↓
network stack ns1
↓
veth1
↓
veth2
↓
network stack ns2
↓
process


Никакого физического интерфейса между namespace здесь нет.

▪️Почему это используется в контейнерах

Контейнер получает собственный network namespace.
Один конец veth помещается внутрь контейнера:

container
eth0
│
│ veth
│
host


Второй конец остаётся на host и обычно подключается к Linux bridge.
Например:

container eth0
│
veth
│
bridge
├── veth
├── veth
└── host


Так несколько изолированных namespace получают доступ к одной L2-сети.

▪️Проверяем, где находится интерфейс
На host:

ip link


В namespace:

ip -n ns1 link


Можно увидеть, что veth1 и veth2 физически не существуют как один интерфейс — это два связанных виртуальных endpoint’а.

▪️Важный нюанс
veth сам по себе не маршрутизирует трафик.
Он просто соединяет два сетевых пространства на уровне Ethernet.
Если нужно выйти из namespace в интернет, поверх veth обычно добавляют bridge, routing, NAT или другой сетевой механизм.

То есть veth - это буквально виртуальный кабель:

namespace A
│
veth A
│
└──────── veth B
│
namespace B


А уже дальше Linux решает, куда отправлять пакеты.

BashTex 📱 #bash #systemd
  • 👍 6
  • 🔥 1
Post #653 357
nohup vs session vs terminal - что реально происходит после закрытия SSH

Запустили процесс по SSH:

./backup.sh &


Закрыли SSH-сессию - а потом обнаружили, что процесс исчез.
Почему?

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

▪️Что происходит при SSH-подключении
Условно:

sshd
↓
shell
↓
terminal / PTY
↓
команды


Shell создаёт процессы в своей session и process group, а SSH предоставляет им псевдотерминал.
Когда соединение закрывается, терминал исчезает. Это может привести к отправке SIGHUP процессам, связанным с терминалом.
Именно здесь обычный background-процесс может неожиданно завершиться.

▪️& не делает процесс независимым

./backup.sh &


& лишь запускает команду в фоне для текущего shell.
Это не означает, что процесс переживёт закрытие SSH.
Проверить дерево:

ps -o pid,ppid,sid,pgid,tty,stat,cmd


Здесь особенно интересны:

PID   PPID   SID   PGID   TTY


Можно увидеть, к какой session и terminal относится процесс.

▪️Что делает nohup

nohup ./backup.sh >backup.log 2>&1 &


nohup не создаёт магическую «вечную» копию процесса.

Он в первую очередь меняет обработку SIGHUP и перенаправляет стандартные потоки, если они всё ещё связаны с терминалом.
Проверить:

ps -o pid,ppid,sid,pgid,tty,cmd -p <PID>


Процесс всё ещё может находиться в той же session.

Но получение SIGHUP уже не должно завершить его обычным способом.

▪️А что такое session
Session - это уровень выше process group.

У неё есть лидер - обычно shell, запущенный после входа по SSH.

Посмотреть:

ps -o pid,ppid,sid,pgid,tty,cmd


Например:

PID   PPID   SID   PGID   TTY
1000 900 1000 1000 pts/0
1050 1000 1000 1050 pts/0


Оба процесса находятся в одной session, хотя имеют разные process groups.

▪️Почему setsid - это уже другое
Можно создать новую session:

setsid ./backup.sh


Теперь процесс не находится в старой session SSH.

Это уже принципиально отличается от простого:

./backup.sh &


Но для полноценного фонового запуска всё равно нужно учитывать stdin/stdout/stderr и дальнейшее поведение приложения.

▪️Практический вариант
Если нужен простой запуск долгой команды:

nohup ./backup.sh </dev/null >backup.log 2>&1 &


Если нужна полноценная интерактивная среда, которая переживает отключение SSH:

tmux


В tmux процесс продолжает работать внутри отдельной terminal/session среды, а вы можете подключиться к ней позже.

▪️Важный нюанс

nohup, setsid и tmux решают разные задачи.

&       → background job
nohup → защита от SIGHUP + перенаправление потоков
setsid → новая session
tmux → отдельная управляемая terminal session


Поэтому вопрос «почему процесс умер после выхода из SSH?» лучше начинать не с nohup, а с:

ps -o pid,ppid,sid,pgid,tty,stat,cmd


Сначала смотрим, к какой session, process group и terminal он вообще был привязан.

BashTex 📱 #bash #systemd
  • 👍 7
Post #652 367
Minor и major page fault: почему page fault не всегда означает проблему с диском

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

Тогда возникает page fault - обращение к ядру для обработки этого случая.
Но сам факт page fault ещё не означает, что система полезла на диск.

▪️Смотрим статистику процесса

ps -o pid,comm,min_flt,maj_flt -p 1234


Или:

pidstat -r -p 1234 1


Там можно увидеть количество minor и major faults.

▪️minor fault

При minor page fault данные уже находятся в RAM, но процессу нужно, например, создать или настроить соответствующее отображение страницы.
Дисковый I/O для загрузки этой страницы не требуется.

Поэтому большое количество minor faults само по себе не означает медленный диск.

▪️major fault

При major fault для продолжения работы процесса требуется получить данные из более медленного источника, например с диска.
Условно:

minor
процесс → kernel → RAM → продолжение

major
процесс → kernel → storage → RAM → продолжение


Именно major faults интересны при поиске проблем с памятью и I/O.

▪️Смотрим процесс в реальном времени

pidstat -r -p 1234 1


Можно увидеть примерно:

PID   minflt/s   majflt/s
1234 15230.00 0.00


Здесь процесс создаёт много minor faults, но major faults нет.

Это совершенно другая ситуация, чем:

PID   minflt/s   majflt/s
1234 1200.00 85.00


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

▪️Практический сценарий
Приложение внезапно начинает тормозить.
top показывает:

CPU: 20%
RAM: 60%


Но:

pidstat -r -p 1234 1


показывает заметный majflt/s.
Тогда стоит дополнительно посмотреть I/O:

iostat -xz 1


Возможно, процесс регулярно ждёт чтения данных со storage.

▪️Важный нюанс
Не стоит использовать количество page faults как самостоятельную метрику производительности.

Высокий minflt/s может быть совершенно нормальным для приложения с определённым характером работы с памятью.
Гораздо интереснее смотреть на major faults вместе с latency и I/O activity.

То есть:

page fault
↓
minor или major?
↓
если major → есть ли реальный I/O?
↓
где процесс проводит время?


Сам по себе page fault - это не ошибка. Это механизм, который Linux постоянно использует при работе с виртуальной памятью.

BashTex 📱 #bash #systemd
  • 👍 6
Post #650 549
inotify vs polling: почему постоянный find может быть плохим мониторингом

Иногда нужно отследить появление нового файла в каталоге.

Самый простой вариант:

while true; do
find /var/incoming -type f
sleep 1
done

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

▪️Следим за изменениями без постоянного обхода

Например, с inotifywait:

inotifywait -m /var/incoming

При создании файла можно получить:

/var/incoming/ CREATE report.csv


А с -e можно ограничить интересующие события:

inotifywait -m \
-e create -e moved_to \
/var/incoming


Теперь процесс ждёт события вместо постоянного запуска find.

▪️Можно сразу обрабатывать новые файлы

inotifywait -m -e create -e moved_to --format '%w%f' \
/var/incoming |
while read -r file; do
process "$file"
done


Когда файл появляется или перемещается в каталог, вызывается обработчик.

▪️Почему polling хуже
При polling:

find → sleep → find → sleep → find


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

При inotify:
wait → event → обработка → wait
процесс большую часть времени просто ожидает событие.

▪️Но есть важный нюанс
inotify не является очередью событий для бесконечного количества изменений.

У ядра есть ограничение на очередь событий:

cat /proc/sys/fs/inotify/max_queued_events


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

Тогда появляется:

IN_Q_OVERFLOW


И приложение уже не может считать, что знает обо всех произошедших изменениях.

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

▪️Практический сценарий
Есть каталог, куда другой сервис складывает файлы:

/var/incoming/


Вместо постоянного:

find /var/incoming ...


можно реагировать непосредственно на CREATE или MOVED_TO.

Но если файл сначала создаётся и затем постепенно записывается, одного события создания недостаточно.

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

Именно поэтому в production важно следить не только за самим событием, но и за способом передачи файла.

BashTex 📱 #bash #systemd
  • 👍 7
Post #649 547
Sparse files: почему файл на 100 ГБ может занимать несколько мегабайт

Размер файла и реально занятое место на диске не всегда совпадают.

Можно создать файл, который логически имеет размер 100 ГБ, но физически занимает всего несколько мегабайт.
Для этого используются sparse files.

▪️Создаём sparse-файл

truncate -s 100G test.img


Теперь:

ls -lh test.img


покажет:

-rw-r--r-- 1 user user 100G test.img


Но:

du -h test.img


может показать всего несколько килобайт.

ls смотрит на логический размер, а du - на реально выделенные filesystem blocks.

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

Условно:

логический файл:

[данные][ огромная область нулей ][данные]
↓ ↓ ↓
блоки hole блоки

Эта незаполненная область называется hole.

▪️Можно посмотреть оба размера

stat test.img

Обратите внимание на:

Size:
Blocks:

Size — логический размер файла.
Blocks — сколько filesystem blocks реально выделено.

▪️Почему это важно
Sparse files часто используются для образов виртуальных машин и контейнеров:

VM disk image → 100 GB
actual allocated space → 12 GB

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

Но если эти области постепенно заполняются, реальное потребление диска начинает расти.

▪️Как проверить sparse-файл

du -h test.img
du --apparent-size -h test.img


Второй вариант показывает размер так, будто sparse-областей нет.

Например:

8.0K    test.img
100G test.img


▪️Практический сценарий

Есть сервер виртуализации, и администратор видит:

ls -lh /var/lib/images/*


Образы занимают:

500G

Но:

du -sh /var/lib/images

показывает:

180G

Это не обязательно ошибка подсчёта.
Часть образов может быть sparse.

▪️Важный нюанс
Копирование sparse-файла не всегда сохраняет holes.
Например, некоторые способы копирования могут превратить разреженный файл в обычный и реально записать нулевые блоки.

Проверить результат можно снова через:

du -h file
du --apparent-size -h file


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

BashTex 📱 #bash #systemd
  • 👍 7
Post #648 474
du vs df: почему Linux показывает разный размер диска

Иногда ситуация выглядит странно:

df -h /


показывает, что занято 80 GB.

А:

du -sh /


находит только 55 GB.

Кажется, что Linux «потерял» 25 GB. На самом деле du и df измеряют разные вещи.

▪️Что считает df
df смотрит на файловую систему и её блоки:

df -h /


Он показывает, сколько места файловая система считает занятым и свободным.

▪️Что считает du
du обходит дерево каталогов и суммирует пространство, связанное с найденными файлами:

du -xhd1 / 2>/dev/null


-x здесь важен: он не позволяет уйти на другие файловые системы.

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

Например:

rm /var/log/app.log


Имя файла исчезло из каталога, но процесс всё ещё держит его открытым.
Для du такого файла уже не существует.
А файловая система продолжает считать его блоки занятыми.
Проверить:

lsof +L1


Можно увидеть что-то вроде:

app   1234  ...  /var/log/app.log (deleted)


Пока процесс не закроет этот дескриптор, место может не освободиться.

▪️Есть и другие причины
Например, du без -x может пересекать mount points и давать неожиданный результат.
Или часть пространства файловой системы зарезервирована и не доступна обычным пользователям.

Посмотреть файловую систему подробнее:

findmnt /
df -h /
df -i /


▪️Практический сценарий
На сервере внезапно заканчивается место:

df -h /


Показывает:

/dev/sda2   100G   95G   5G   95% /


Но:

du -xsh /


показывает только 70G.

Первое, что стоит проверить:

lsof +L1


Если там огромный (deleted) лог, причина найдена.

▪️Важный нюанс
df отвечает примерно на вопрос:

«Сколько блоков файловой системы сейчас занято?»

А du:

«Сколько места занимают доступные мне файлы в этом дереве?»

Поэтому при расхождении df и du не стоит сразу запускать rm -rf по большим каталогам.

Сначала нужно понять, куда именно делось пространство.

BashTex 📱 #bash #systemd
  • 👍 6
Post #647 452
file vs расширение - как Linux определяет тип файла на самом деле

В Linux расширение файла само по себе ничего не определяет.

Для системы:

backup.tar.gz
backup.jpg
backup.txt


это просто имена файлов. Можно спокойно сделать:
mv image.jpg image.txt
и содержимое файла от этого никак не изменится.

▪️Откуда тогда file знает тип

Утилита file смотрит не на расширение, а на содержимое файла.

file image.txt
file archive.bin
file unknown


Например:

unknown: PNG image data, 1920 x 1080, 8-bit/color RGB


Даже если файл называется:

photo.txt

file всё равно может определить его как PNG.

▪️Главный источник - magic bytes

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

Например, PNG начинается с:

89 50 4e 47 0d 0a 1a 0a

Проверить первые байты можно:

xxd -l 16 photo.png

А file сопоставляет такие сигнатуры с базой magic:

file --version

и использует правила из базы magic.

▪️Можно обмануть расширение

cp image.png document.txt
file document.txt


Результат всё равно будет примерно таким:

document.txt: PNG image data


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

▪️Но не всё определяется только первыми байтами
file может анализировать не только сигнатуру.

Для ELF:

file /bin/ls


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

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

▪️Практический сценарий

Скачали файл с подозрительным расширением:

download.exe


Не стоит сразу доверять имени.

Сначала:

file download.exe


А затем, если это бинарник:

readelf -h download.exe


или:

xxd -l 32 download.exe


Так можно понять, что реально лежит внутри файла.

▪️Важный нюанс
file не является универсальным детектором содержимого.

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

И ещё важнее: file определяет тип по содержимому, но не говорит, что файл безопасен.

Файл может называться document.pdf, определяться как PDF и при этом содержать вредоносную нагрузку.

В Linux расширение - часть имени.

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

BashTex 📱 #bash #systemd
  • 👍 6
Post #646 697
BashTex 📱 #мем
  • 😁 8
  • 🔥 2
  • 🗿 1
Post #645 585
systemd-delta: как найти, чем на самом деле переопределён unit

Иногда вы открываете unit-файл и видите одну конфигурацию, а systemd ведёт себя иначе.

Причина часто в drop-in override, созданном через systemctl edit.

Например, основной unit:

/usr/lib/systemd/system/nginx.service


а дополнительная настройка лежит здесь:

/etc/systemd/system/nginx.service.d/override.conf


▪️Посмотреть переопределения

systemd-delta


Команда показывает отличия между vendor-конфигурацией и локальными изменениями.
Можно проверить конкретный unit:

systemd-delta nginx.service


Если сервис был переопределён, вы увидите соответствующий drop-in.

▪️Почему это важно

Допустим, в основном unit указано:

[Service]
Restart=no

А где-то в /etc/systemd/system/nginx.service.d/override.conf:
[Service]
Restart=always


Открыв только /usr/lib/systemd/system/nginx.service, вы будете смотреть на неполную картину.

systemd при запуске учитывает оба слоя.

▪️Посмотреть итоговую конфигурацию

Для проверки того, что systemd реально использует:

systemctl show nginx.service


Например:

systemctl show nginx.service -p Restart -p RestartUSec


А список связанных drop-in можно увидеть так:

systemctl cat nginx.service


Здесь systemd покажет основной unit и применённые drop-in-файлы.

▪️Практический сценарий

Сервис неожиданно перезапускается:

systemctl status nginx


В основном unit Restart= не настроен.

Вместо того чтобы сразу менять конфигурацию, проверяем:

systemd-delta nginx.service


И обнаруживаем старый override:

/etc/systemd/system/nginx.service.d/override.conf


Именно он мог изменить поведение сервиса.

▪️Важный нюанс

systemd-delta особенно полезен после обновлений дистрибутива.

Пакет может заменить vendor unit в /usr/lib/systemd/system/, но локальный override в /etc/systemd/system/ продолжит действовать.

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

BashTex 📱 #bash #systemd
  • 👍 7
Post #644 597
PSI: /proc/pressure/* - как понять, чего реально не хватает системе

load average показывает, что в системе есть очередь на выполнение или I/O. Но он плохо отвечает на другой вопрос: насколько пользователи и процессы реально страдают от этого дефицита?

Для этого в Linux есть PSI - Pressure Stall Information:


cat /proc/pressure/cpu
cat /proc/pressure/memory
cat /proc/pressure/io


Например:


full avg10=0.00 avg60=0.01 avg300=0.00 total=...


▪️some - хотя бы часть задач была вынуждена ждать ресурс.
▪️full - все задачи соответствующей группы одновременно стояли из-за этого ресурса.

avg10, avg60, avg300 - процент времени за последние 10, 60 и 300 секунд, когда происходило такое ожидание.

Например:


some avg10=18.5
full avg10=7.2


Это уже гораздо интереснее, чем просто увидеть высокий RAM usage.

Если memory some растёт - процессы регулярно упираются в нехватку памяти.

Если начинает расти memory full - система уже попадает в периоды, когда все учитываемые задачи одновременно не могут нормально продвигаться из-за memory pressure.

То же самое для I/O:


cat /proc/pressure/io


Если I/O PSI высокий, а CPU почти не испытывает pressure, проблема может быть не в загрузке процессора, а в storage latency.

А CPU PSI позволяет увидеть ситуацию, когда CPU формально загружен не на 100%, но runnable-задачи всё равно регулярно ждут свою очередь.

Проверить всё сразу:


for f in /proc/pressure/*; do
echo "=== $f ==="
cat "$f"
done


PSI полезен именно как метрика задержки из-за конкуренции за ресурсы, а не просто их потребления.

Поэтому вместо вопроса «CPU/RAM/Disk загружены?» иногда правильнее спросить:
«Какой ресурс заставляет процессы ждать?»

BashTex 📱 #perfomance #linux
  • 👍 9
Post #641 523
failglob - почему Bash должен падать при нераскрывшемся glob

Обычно Bash спокойно оставляет glob как есть, если совпадений нет:

rm /var/log/app/*.old


Если .old-файлов нет, команда фактически получит:

rm: cannot remove '/var/log/app/*.old': No such file or directory


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

▪️failglob меняет поведение

shopt -s failglob

files=(/var/log/app/*.old)


Если совпадений нет, Bash сам выдаст ошибку:

bash: no match: /var/log/app/*.old


То есть проблема обнаруживается на этапе раскрытия glob, ещё до запуска команды.

▪️Особенно полезно в скриптах:

#!/usr/bin/env bash

set -e
shopt -s failglob

for file in /backup/*.tar.gz; do
process "$file"
done


Без failglob цикл может получить буквально строку /backup/*.tar.gz.

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

▪️Но есть нюанс

failglob не всегда нужен. Если отсутствие файлов - нормальная ситуация, лучше использовать nullglob:

shopt -s nullglob

files=(/backup/*.tar.gz)

for file in "${files[@]}"; do
process "$file"
done


Тогда при отсутствии совпадений массив просто будет пустым.

Итого получается:

failglob → отсутствие совпадения считается ошибкой. nullglob → отсутствие совпадения превращается в пустой список.

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

BashTex 📱 #bash #linux
  • 👍 3
  • 🔥 1
Older posts →

About this channel

How can I read @bash_tex without a Telegram account?
TGViewer shows the public web preview Telegram publishes for BashTex | Linux: recent posts, photos, videos and the subscriber count, with no app, login or account.
How many subscribers does BashTex | Linux have?
BashTex | Linux (@bash_tex) has 2.51K subscribers on Telegram, refreshed roughly every 30 minutes.
Does BashTex | Linux know I viewed it here?
No. Public channel previews carry no viewer identity, and TGViewer has no accounts or tracking of what you look up.
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →