Minor и major page fault: почему page fault не всегда означает проблему с диском
Когда процесс обращается к виртуальной памяти, нужная страница не всегда уже находится в памяти именно в том состоянии, которое ожидает процесс.
Тогда возникает page fault - обращение к ядру для обработки этого случая.
Но сам факт page fault ещё не означает, что система полезла на диск.
▪️ Смотрим статистику процесса
Или:
Там можно увидеть количество minor и major faults.
▪️
При minor page fault данные уже находятся в RAM, но процессу нужно, например, создать или настроить соответствующее отображение страницы.
Дисковый I/O для загрузки этой страницы не требуется.
Поэтому большое количество minor faults само по себе не означает медленный диск.
▪️
При major fault для продолжения работы процесса требуется получить данные из более медленного источника, например с диска.
Условно:
Именно major faults интересны при поиске проблем с памятью и I/O.
▪️ Смотрим процесс в реальном времени
Можно увидеть примерно:
Здесь процесс создаёт много minor faults, но major faults нет.
Это совершенно другая ситуация, чем:
Во втором случае часть обращений действительно приводит к ожиданию загрузки данных.
▪️ Практический сценарий
Приложение внезапно начинает тормозить.
Но:
показывает заметный
Тогда стоит дополнительно посмотреть I/O:
Возможно, процесс регулярно ждёт чтения данных со storage.
▪️ Важный нюанс
Не стоит использовать количество page faults как самостоятельную метрику производительности.
Высокий
Гораздо интереснее смотреть на major faults вместе с latency и I/O activity.
То есть:
Сам по себе page fault - это не ошибка. Это механизм, который Linux постоянно использует при работе с виртуальной памятью.
BashTex📱 #bash #systemd
Когда процесс обращается к виртуальной памяти, нужная страница не всегда уже находится в памяти именно в том состоянии, которое ожидает процесс.
Тогда возникает 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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
nohup vs session vs terminal - что реально происходит после закрытия SSH
Запустили процесс по SSH:
Закрыли SSH-сессию - а потом обнаружили, что процесс исчез.
Почему?
Проблема не в самом SSH. Нужно понимать связь между терминалом, session, shell и сигналами.
▪️ Что происходит при SSH-подключении
Условно:
Shell создаёт процессы в своей session и process group, а SSH предоставляет им псевдотерминал.
Когда соединение закрывается, терминал исчезает. Это может привести к отправке
Именно здесь обычный background-процесс может неожиданно завершиться.
▪️
Это не означает, что процесс переживёт закрытие SSH.
Проверить дерево:
Здесь особенно интересны:
Можно увидеть, к какой session и terminal относится процесс.
▪️ Что делает
Он в первую очередь меняет обработку
Проверить:
Процесс всё ещё может находиться в той же session.
Но получение
▪️ А что такое
Session - это уровень выше process group.
У неё есть лидер - обычно shell, запущенный после входа по SSH.
Посмотреть:
Например:
Оба процесса находятся в одной session, хотя имеют разные process groups.
▪️ Почему
Можно создать новую session:
Теперь процесс не находится в старой session SSH.
Это уже принципиально отличается от простого:
Но для полноценного фонового запуска всё равно нужно учитывать stdin/stdout/stderr и дальнейшее поведение приложения.
▪️ Практический вариант
Если нужен простой запуск долгой команды:
Если нужна полноценная интерактивная среда, которая переживает отключение SSH:
В
▪️ Важный нюанс
Поэтому вопрос «почему процесс умер после выхода из SSH?» лучше начинать не с
Сначала смотрим, к какой session, process group и terminal он вообще был привязан.
BashTex📱 #bash #systemd
Запустили процесс по SSH:
./backup.sh &
Закрыли SSH-сессию - а потом обнаружили, что процесс исчез.
Почему?
Проблема не в самом SSH. Нужно понимать связь между терминалом, session, shell и сигналами.
Условно:
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 относится процесс.
nohupnohup ./backup.sh >backup.log 2>&1 &
nohup не создаёт магическую «вечную» копию процесса.Он в первую очередь меняет обработку
SIGHUP и перенаправляет стандартные потоки, если они всё ещё связаны с терминалом.Проверить:
ps -o pid,ppid,sid,pgid,tty,cmd -p <PID>
Процесс всё ещё может находиться в той же session.
Но получение
SIGHUP уже не должно завершить его обычным способом.sessionSession - это уровень выше 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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
veth - как Linux соединяет два network namespace
Network namespace изолирует сетевой стек процесса. У каждого namespace могут быть свои интерфейсы, маршруты и таблица соседей.
Но как соединить два таких изолированных сетевых мира?
Для этого в Linux есть veth pair - виртуальный Ethernet-кабель с двумя концами.
▪️ Создаём два namespace
Теперь создаём пару:
Получили:
То, что отправлено в один конец, появляется на другом.
▪️ Разносим концы по namespace
Теперь:
В каждом namespace находится только свой конец виртуального кабеля.
▪️ Назначаем адреса
Теперь можно проверить связь:
Пакет проходит примерно так:
Никакого физического интерфейса между namespace здесь нет.
▪️ Почему это используется в контейнерах
Контейнер получает собственный network namespace.
Один конец veth помещается внутрь контейнера:
Второй конец остаётся на host и обычно подключается к Linux bridge.
Например:
Так несколько изолированных namespace получают доступ к одной L2-сети.
▪️ Проверяем, где находится интерфейс
На host:
В namespace:
Можно увидеть, что
▪️ Важный нюанс
Он просто соединяет два сетевых пространства на уровне Ethernet.
Если нужно выйти из namespace в интернет, поверх veth обычно добавляют bridge, routing, NAT или другой сетевой механизм.
То есть
А уже дальше Linux решает, куда отправлять пакеты.
BashTex📱 #bash #systemd
Network namespace изолирует сетевой стек процесса. У каждого namespace могут быть свои интерфейсы, маршруты и таблица соседей.
Но как соединить два таких изолированных сетевых мира?
Для этого в Linux есть veth pair - виртуальный Ethernet-кабель с двумя концами.
ip netns add ns1
ip netns add ns2
Теперь создаём пару:
ip link add veth1 type veth peer name veth2
Получили:
veth1 <──────────> veth2
То, что отправлено в один конец, появляется на другом.
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥1
Почему Permission denied: как найти каталог, на котором ломаются права
Иногда файл имеет правильные права, пользователь состоит в нужной группе, но
Причина может быть выше по пути: чтобы открыть
Разберем, как быстро найти место, где ломается доступ.
1️⃣
В выводе будут отдельно показаны:
Сразу видно, на каком уровне пользователь может упереться в права.
2️⃣ Почему важен
Для каталога
Например:
Файлы внутри могут быть доступны для чтения, но войти в каталог без
Проверить можно:
3️⃣ Проверяем конкретного пользователя
Если доступ должен быть у
А группы:
Это помогает понять, действительно ли процесс запускается с теми группами, которые вы ожидаете.
4️⃣ ACL тоже могут менять картину
Обычных
Там могут быть дополнительные разрешения для конкретного пользователя или группы.
5️⃣ Почему
Команда:
показывает права самого файла.
Но если проблема находится в
Именно поэтому
BashTex📱 #bash #systemd
Иногда файл имеет правильные права, пользователь состоит в нужной группе, но
Permission denied всё равно появляется.Причина может быть выше по пути: чтобы открыть
/var/www/site/config.php, процесс должен иметь право x на каждый каталог в этом пути.Разберем, как быстро найти место, где ломается доступ.
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
Сразу видно, на каком уровне пользователь может упереться в права.
x у каталогаДля каталога
x означает не «запуск», а возможность проходить через него и обращаться к объектам внутри.Например:
chmod 644 /var/www/site
Файлы внутри могут быть доступны для чтения, но войти в каталог без
x уже нельзя.Проверить можно:
sudo -u www-data cat /var/www/site/config.php
Если доступ должен быть у
www-data:sudo -u www-data namei -l /var/www/site/config.php
А группы:
id www-data
Это помогает понять, действительно ли процесс запускается с теми группами, которые вы ожидаете.
Обычных
ls -l иногда недостаточно. Проверить ACL:getfacl /var/www/site/config.php
Там могут быть дополнительные разрешения для конкретного пользователя или группы.
ls -l файл не всегда помогаетКоманда:
ls -l /var/www/site/config.php
показывает права самого файла.
Но если проблема находится в
/var/www/site, информация о файле сама по себе этого не объяснит.Именно поэтому
namei -l удобен при странных Permission denied: он показывает всю цепочку каталогов, через которую процесс должен пройти до нужного файла.BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Архитектура большого bash-скрипта
Пока скрипт на 30 строк - все терпимо. На 300 строк начинается хаос. На 1000 - уже никто не понимает, где init, где логика, где cleanup. Чтобы bash не превратился в лапшу, ему нужна архитектура.
📂 Нормальная структура проекта
Где:
▪️ Деление на модули
Не надо держать все в одном файле.
Лучше так:
Примеры модулей:
▪️ Naming: единый стиль
Худший вариант:
Лучше:
Для приватных функций можно префикс:
▪️ Поток исполнения. В начале файла:
Это делает скрипт читаемым как программу, а не как свалку команд.
BashTex📱 #bash #scripts
Пока скрипт на 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 - основная логика
Худший вариант:
doStuff()
x()
RunAll()
Лучше:
log_info()
check_dependencies()
deploy_app()
cleanup_tmp()
Для приватных функций можно префикс:
_internal_parse_config()
main() {
load_config
check_dependencies
run_tasks
}
main "$@"Это делает скрипт читаемым как программу, а не как свалку команд.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegram
BashTex | Linux
Авторский канал для тех, кто хочет глубже погрузиться в мир Linux.
Подойдет для разработчиков, системных администраторов и DevOps
Реклама: @dad_admin
Подойдет для разработчиков, системных администраторов и DevOps
Реклама: @dad_admin
👍8
tee + process substitution: один поток и несколько получателей
Иногда нужно одновременно: увидеть вывод команды в терминале, записать его в файл и передать на обработку другой программе Если делать это по очереди - потеряется поток данных. Здесь помогает связка tee + process substitution.
▪️ Что делает tee. tee дублирует поток:
вывод остается в терминале и одновременно пишется в
▪️ Несколько получателей. tee может писать сразу в несколько файлов:
Но иногда нужно не просто файл, а другую команду.
▪️ Process substitution. Bash позволяет подставить вывод команды как файл:
>(command)
Это называется process substitution.
▪️ Комбинируем
Одна копия идет в терминал, а другая в grep. Если найден ERROR, запись попадет в errors.log.
▪️ Более реальный пример. Допустим, идет сбор логов:
Теперь: полный поток остается в терминале, ошибки автоматически сохраняются
▪️ Можно делать несколько обработчиков
Один поток и сразу несколько фильтров.
BashTex📱 #bash #utils
Иногда нужно одновременно: увидеть вывод команды в терминале, записать его в файл и передать на обработку другой программе Если делать это по очереди - потеряется поток данных. Здесь помогает связка tee + process substitution.
command | tee file.log
вывод остается в терминале и одновременно пишется в
file.logcommand | tee out1.log out2.log
Но иногда нужно не просто файл, а другую команду.
>(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
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegram
BashTex | Linux
Авторский канал для тех, кто хочет глубже погрузиться в мир Linux.
Подойдет для разработчиков, системных администраторов и DevOps
Реклама: @dad_admin
Подойдет для разработчиков, системных администраторов и DevOps
Реклама: @dad_admin
🔥6👍2
DEBUG trap - как Bash выполняет код перед каждой командой
В Bash есть специальный
Это удобно не только для отладки. Через него можно посмотреть, какие команды реально выполняет скрипт, с какими аргументами и в каком контексте.
▪️ Самый простой пример
Перед каждой командой Bash вызовет trap:
▪️ Можно добавить PID и функцию
Теперь при выполнении функций можно получить что-то вроде:
Это уже превращается в простой трассировщик Bash-скрипта.
▪️ Но
Trap срабатывает перед выполнением простых команд,
Поведение также зависит от того, включено ли наследование trap внутри функций и subshell.
Например:
Чтобы
или:
▪️ У trap есть интересный побочный эффект
Сам код внутри
Для диагностики лучше держать его простым:
А после диагностики обязательно убрать:
▪️ Практический сценарий
Есть большой deployment-скрипт, который запускает десятки функций и команд. Лог показывает только:
Вместо того чтобы расставлять
и получаем последовательность реально выполнявшихся команд.
Важно:
BashTex📱 #bash #utils
В 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 собирается выполнить.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
Сам код внутри
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
flock vs fcntl - почему две блокировки одного файла могут вести себя совершенно по-разному
В Linux есть несколько механизмов блокировки файлов. Наиболее часто встречаются
На первый взгляд они делают одно и то же: не дают нескольким процессам одновременно работать с одним файлом.
Но внутри ядра это разные механизмы, поэтому блокировки могут вообще не замечать друг друга.
▪️
Простейший вариант:
Пока первый процесс держит блокировку, второй:
получит ошибку и сразу завершится из-за
Часто этот механизм используют для защиты cron-задач:
▪️
Здесь можно блокировать не только весь файл, но и конкретный диапазон байтов.
На уровне C:
Можно заблокировать первые 100 байт, а другой процесс при этом сможет работать с другим диапазоном.
▪️ Главная ловушка
Процесс A:
Процесс B использует
Они не обязаны конфликтовать.
Причина в том, что
То есть наличие:
не означает автоматически:
И наоборот.
▪️ Обе блокировки advisory
Ни
Если приложение вообще не проверяет блокировку, оно может спокойно сделать:
Поэтому механизм работает только тогда, когда все участники системы договариваются его соблюдать.
▪️ Есть ещё одна важная разница
Для
У
Из-за этого без понимания модели владения FD легко получить ситуацию, когда один
▪️ Практический вывод
Если нужно просто гарантировать, что cron или несколько экземпляров shell-скрипта не выполняются одновременно:
обычно достаточно.
Если приложение должно блокировать отдельные диапазоны файла или ему нужна POSIX-модель блокировок, используют
И главное: нельзя считать
BashTex📱 #bash #utils
В 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
И наоборот.
Ни
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
madvise() - как процесс подсказывает ядру, как он собирается использовать память
Когда процесс работает с большой областью памяти, ядро не всегда знает, как именно приложение будет обращаться к этим страницам.
Linux предоставляет
Это особенно интересно для
▪️ Последовательный доступ
Если программа собирается читать память последовательно:
ядро получает подсказку, что страницы будут использоваться примерно по порядку.
Это позволяет иначе управлять page cache и упреждающим чтением.
Например, обработчик большого файла может пройти по нему от начала до конца вместо случайных обращений.
▪️ Случайный доступ
Для случайного доступа есть:
Если приложение читает страницы в произвольном порядке, агрессивное read-ahead может оказаться бесполезным.
Подсказка позволяет ядру учитывать такой сценарий.
▪️ Можно сообщить, что страницы больше не нужны
Особенно интересен:
Процесс говорит ядру, что содержимое этой области ему сейчас не требуется.
Для анонимной памяти это может позволить освободить физические страницы. Для файлового отображения поведение связано с отображёнными страницами и page cache.
Важно: это не то же самое, что
Виртуальный адрес всё ещё может оставаться отображённым, но физические страницы могут быть освобождены и восстановлены при следующем обращении.
▪️ Есть и подсказка
Она сообщает ядру, что страницы, вероятно, скоро понадобятся.
Это может помочь подготовить данные заранее, например перед обработкой большого memory-mapped файла.
Но
▪️ Практический сценарий
Представим программу, которая через
Вместо случайного поведения с page cache она может сделать:
Теперь приложение явно сообщает ядру характер будущего доступа.
А когда большой участок больше не нужен:
Это может уменьшить давление на память, не требуя немедленно уничтожать само отображение.
▪️ Важный нюанс
Эффект зависит от конкретного флага, типа mapping, версии ядра и сценария доступа.
И особенно важно не путать
То есть
BashTex📱 #bash #utils
Когда процесс работает с большой областью памяти, ядро не всегда знает, как именно приложение будет обращаться к этим страницам.
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().Виртуальный адрес всё ещё может оставаться отображённым, но физические страницы могут быть освобождены и восстановлены при следующем обращении.
WILLNEEDmadvise(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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
name_to_handle_at() - как получить файловый объект без обычного пути
В Linux обычно обращаются к файлу через путь:
Но ядро умеет работать с файловым объектом иначе. Системный вызов
Это используется низкоуровневыми инструментами, которым нужно сохранить ссылку на объект, а не его текстовый путь.
▪️ Получаем handle
Сам системный вызов выглядит примерно так:
Например, программа может передать:
и получить структуру с handle и идентификатором mount.
Важно: это не файловый дескриптор. Handle не позволяет просто сделать
▪️ Зачем тогда он нужен
Основная идея - получить устойчивое представление объекта внутри filesystem, которое затем можно использовать для повторного обращения к нему.
Для этого существует парный системный вызов:
Схема получается такой:
То есть сначала filesystem выдаёт идентификатор объекта, а позже по нему можно снова получить FD.
▪️ Почему это отличается от обычного пути
Представим:
Путь зависит от directory entries. Файл могут переименовать:
Сам inode при этом остаётся тем же объектом.
File handle предназначен именно для работы с объектом filesystem, а не с конкретным именем в каталоге.
▪️ Но есть важное ограничение
Handle не является универсальным идентификатором любого файла на всех filesystem.
Его формат зависит от filesystem, а поддержка механизма должна быть реализована самой filesystem.
Кроме того,
▪️ Где это реально встречается
Механизм особенно интересен для низкоуровневых инструментов резервного копирования, мониторинга и работы с filesystem, где обычный путь может быть неудобен или недостаточно надёжен.
И главное:
Он просит конкретную filesystem предоставить handle для объекта, а затем этот handle может быть использован через соответствующий kernel API.
BashTex📱 #bash #utils
В Linux обычно обращаются к файлу через путь:
/etc/hosts
Но ядро умеет работать с файловым объектом иначе. Системный вызов
name_to_handle_at() позволяет получить специальный file handle, связанный с объектом filesystem.Это используется низкоуровневыми инструментами, которым нужно сохранить ссылку на объект, а не его текстовый путь.
Сам системный вызов выглядит примерно так:
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4