Вопрос или проблема
Хорошо, это решенная проблема, но остаются некоторые вопросы…
У меня есть несколько “скриптов”, которые автоматомонтируют файловые системы, а затем делятся различными частями через NFS и Samba.
Я запускал версии этих скриптов в течение многих месяцев на системе с фиксированными (заболченными) дисками, и использовал системную службу systemd, все работало хорошо.
Моя текущая система имеет диск, подключенный через USB (плюс фиксированный NVME), поэтому я использовал udev. Конечно же, я столкнулся с проблемой “нельзя использовать mount(1) внутри правила udev (отдельное пространство имен монтирования), поэтому я сделал “обычное” (запустил службу):
graeme@argon:~$ cat /etc/udev/rules.d/99-QPImount.rules
ACTION=="add|change|online|bind", ENV{ID_FS_USAGE}=="filesystem", ENV{ID_FS_LABEL_ENC}=="rest?", TAG+="systemd" ENV{SYSTEMD_WANTS}+="QPImount.service"
Это работало нормально, когда я тестировал (udevadm trigger -a add /dev/sda4) и на практике, когда система была выключена и включена.
Однако, сегодня я включил систему без подключенного USB-диска, а затем позже подключил его. Мой скрипт не запустился. Пройдя все шаги вручную, казалось, все было в порядке, пока я не попытался запустить службу вручную:
#systemctl start QPImount
Это зависает.
graeme@argon:~$ cat /etc/systemd/system/QPImount.service
[Unit]
Description=Make all the QPIhomebrew filesystems available (NFS & SAMBA)
After=remote-fs.target
Before=minidlna.service samba.service
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'QPImount' && echo 'QPImount completed'
[Install]
#WantedBy=multi-user.target minidlna.service samba.service
#WantedBy=multi-user.target
Вы можете видеть, что я хочу дождаться remote-fs.target (экспорт NFS проваливается до этого момента)
Для справки, в скрипте есть отладка, так что я вижу, что он еще не запустился.
Если я перечислю цели…
# systemctl list-units --type target
UNIT LOAD ACTIVE SUB JOB DESCRIPTION
basic.target loaded active active Basic System
bluetooth.target loaded active active Bluetooth Support
cryptsetup.target loaded active active Local Encrypted Volumes
getty.target loaded active active Login Prompts
graphical.target loaded inactive dead start Graphical Interface
integritysetup.target loaded active active Local Integrity Protected Volumes
local-fs-pre.target loaded active active Preparation for Local File Systems
local-fs.target loaded active active Local File Systems
multi-user.target loaded inactive dead start Multi-User System
network-online.target loaded active active Network is Online
network.target loaded active active Network
nfs-client.target loaded active active NFS client services
nss-lookup.target loaded active active Host and Network Name Lookups
nss-user-lookup.target loaded active active User and Group Name Lookups
paths.target loaded active active Path Units
remote-fs-pre.target loaded active active Preparation for Remote File Systems
remote-fs.target loaded active active Remote File Systems
rpc_pipefs.target loaded active active rpc_pipefs.target
rpcbind.target loaded active active RPC Port Mapper
slices.target loaded active active Slice Units
sockets.target loaded active active Socket Units
sound.target loaded active active Sound Card
swap.target loaded active active Swaps
sysinit.target loaded active active System Initialization
time-set.target loaded active active System Time Set
timers.target loaded active active Timer Units
veritysetup.target loaded active active Local Verity Protected Volumes
Я могу видеть, что remote-fs.target активен.
Итак, мой вопрос:
Я полагаю, что systemd “ждет чего-то” для запуска QPImount.service, существует ли команда, которая скажет мне, чего он ждет, прежде чем запустить QPImount.
…статус показывает статус первого использования службы QPImount (для монтирования файловых систем NVME) ..ничего не началось на этот раз (потому что она не запустилась?)
root@argon:/home/graeme# systemctl status QPImount
QPImount.service - Make all the QPIhomebrew filesystems avaiable (NFS & SAMBA)
Loaded: loaded (/etc/systemd/system/QPImount.service; static)
Active: activating (start) since Mon 2025-03-03 09:02:01 GMT; 43min ago
Main PID: 1005 (QPImount)
Tasks: 2 (limit: 9569)
CPU: 134ms
CGroup: /system.slice/QPImount.service
005 /bin/bash /usr/local/bin/QPImount
1492 systemctl restart minidlna.service
Mar 03 09:02:01 argon systemd[1]: Starting QPImount.service - Make all the QPIhomebrew filesystems avaiable (NFS & SAMBA)...
Mar 03 09:02:01 argon bash[1005]: QPImount invoked with
Mar 03 09:02:01 argon bash[1029]: rmdir: failed to remove '/QPI/mounts/rest2/Multimedia': No such file or directory
Mar 03 09:02:01 argon bash[1029]: rmdir: failed to remove '/Multimedia': No such file or directory
Mar 03 09:02:01 argon bash[1033]: rmdir: failed to remove '/QPI/mounts/rest2/USBUploads': No such file or directory
Mar 03 09:02:01 argon bash[1033]: rmdir: failed to remove '/USBUploads': No such file or directory
Mar 03 09:02:01 argon bash[1047]: rmdir: failed to remove '/QPI/mounts/rest2/Recordings': No such file or directory
Mar 03 09:02:01 argon bash[1047]: rmdir: failed to remove '/Recordings': No such file or directory
Mar 03 09:02:01 argon bash[1048]: rmdir: failed to remove '/QPI/mounts/rest2': No such file or directory
Mar 03 09:02:02 argon bash[1005]: For QPI rest3 adding Public svn git Web InternalAdmin Download home
…ниже есть “решение”, но вопрос о том, как это обнаружить, остается открытым
Похоже, людям не нравится, когда я частично отвечаю в оригинале… так что выделил сюда
————– Решение ————–
Написав это, я нашел свою проблему ( https://wiki.c2.com/?CardboardProgrammer также известная как картонный супермен)
В сервисе я указываю “Before=minidlna.service samba.service” … но в моем скрипте я делаю :
systemctl restart smb.service # Share out what we can via SABMA
systemctl restart minidlna.service # Share out via DLNA protocol
…так что “смертельное объятие”, я должен запуститься ПЕРЕД smb.service, НО до того, как я завершу, я должен перезапустить smb.service (думаю, я мог бы отправить в фон?) Я полагаю, что это тоже “нет-нет” …но видите мою проблему? Как только появляется новая файловая система, различные службы нуждаются в перезапуске, чтобы справиться с этим.
Кстати, я заметил, что runlevel(1) возвращал “неизвестный” в этот момент, но теперь возвращает “5” … подсказка/сигнатура к зависанию.
Но первоначальный вопрос остается: “Существует ли команда, которую я могу ввести в systemd, чтобы спросить ‘что вы ждете, чтобы запустить эту службу?”
…не “от чего это зависит”, а скорее “вы это не запустили? …почему?” предположительно у systemd есть какое-то внутреннее состояние, которое говорит “ожидание на X”
(Второй вопрос, как лучше всего достичь желаемого [перезапуска])
.
Ответ или решение
Ваша система сталкивается с проблемой зависания сервиса systemd, и, судя по вашему описанию, это связано с "взаимной блокировкой" (deadlock), когда один сервис ожидает завершения работы другого, но при этом сам пытается его запустить. Чтобы более подробно разобраться в этой проблеме и предложить решения, давайте основывать наш анализ на методе TEA (Theory, Example, Application).
Теория
Systemd и зависимости: Systemd — это система инициализации, основанная на зависимостях. Она управляет загрузкой системных сервисов в соответствии с заданной конфигурацией. Основные элементы конфигурации включают директивы After
, Before
, Requires
, и Wants
. Если сервис A имеет Before=B
и одновременно пытается вызвать systemctl restart B
внутри скрипта, это может привести к взаимной блокировке.
Директивы и параметры:
After
иBefore
определяют порядок запуска сервисов.Requires
иWants
устанавливают жесткие и мягкие зависимости соответственно.Type=oneshot
используется для задач, не требующих долгого времени для выполнения.
Взаимная блокировка: Этот термин используется для описания ситуации, в которой два или более процесса ждут ресурсов, захваченных друг другом, и ни один из них не может продолжить выполнение, освобождая ресурс.
Пример
Исходя из вашего описания, следующее произошло:
- Определение зависимостей: У вас задано
Before=minidlna.service samba.service
, что означает, что ваш сервис должен завершиться перед стартом указанных сервисов. - Вызов restart: Внутри скрипта вы пытаетесь запустить
systemctl restart smb.service
иsystemctl restart minidlna.service
, что конфликтует с указаниемBefore
.
Этот конфликт заставляет systemd ждать завершения вашего сервиса, который, в свою очередь, пытается перезапустить зависимости, из-за чего система входит в состояние ожидания, и ваш сервис зависает.
Применение
Диагностика проблемы:
-
Использование systemctl list-dependencies: Этот командный инструмент systemd может помочь определить все зависимости и обратные зависимости, связанные с конфигурацией вашего сервиса.
systemctl list-dependencies QPImount
-
Журнализировать события: Используйте
journalctl -xe
для просмотра журнала systemd, что может дать более полную картину происходящего. -
Логи unit-файла: Проверьте лог-файлы вашего сервиса, они дадут инсайты о том, на каком этапе выполнения скрипта возникают сложности.
Решения:
-
Реорганизация зависимостей:
- Перепишите зависимости, чтобы избежать циклических ссылок. Например, возможно, стоит разделить ваш скрипт на два этапа: первый выполняет все действия, не влияющие на запуск других сервисов, второй — перезапускает зависимости.
-
Асинхронное выполнение: Запуск зависимых сервисов в фоновом режиме может помочь. Вместо
restart
возможно использоватьsystemctl try-restart
, который не будет мешать вашему сервису завершить работу. -
Отсмотр состояния systemd: На системах Linux с поддержкой D-Bus можно использовать утилиты, такие как
busctl
, для получения информации о внутренних состояниях systemd.busctl tree org.freedesktop.systemd1
Советы по лучшим практикам:
- Плоская экосистема сервисов: Избегайте избыточного взаимного влияния сервисов друг на друга через restart внутри скриптов. Рассмотрите возможность делегирования таких задач внешним скриптам, которые запускаются после загрузки сервиса.
- Use of Sockets: Для NFS и других сетевых сервисов рассмотрите использование сокетов (socket activation) для их активации по мере необходимости.
Ваш оригинальный вопрос о том, есть ли команда, чтобы systemd сообщил, что именно он ожидает для старта определенного сервиса, не имеет прямого решения, так как systemd не предоставляет утилиту с такой функцией. Однако логика зависимостей и выводы systemctl
— мощные инструменты для анализа блокировок.
Используя эти стратегии, вы должны быть в состоянии выяснить, что мешает запуску вашего сервиса, и избежать взаимных блокировок в будущем.