В UNIX каталоги специальные (я чувствую, что передаю Церковную Госпожу из SNL). Каталоги содержат другие файлы, поэтому для их удаления требуется другая операция. Даже когда каталог пуст, в нем все еще есть два файла (.
и ..
), поэтому удаление каталога невозможно, пока он не станет действительно пустым, а ссылка будет считаться соответствующие файлы были обновлены.
На заре UNIX (мой первый опыт работы с 6-м изданием Bell Labs) были две разные команды ( rm
и rmdir
) для обычных файлов и каталогов, которые отражает тот факт, что это два разных системных вызова. rm
был прост: он просто удалял запись, имя которой вы дали ему, из каталога и уменьшал счетчик ссылок на файл, на который он указывал (конечно, файл не удаляется на самом деле, если счетчик ссылок не идет до 0). rmdir
требовал гораздо большего (на самом деле не в приложении, а в системном вызове), он должен был перейти в каталог и найти .
и ..
, перейдите к этим индексным дескрипторам и уменьшите счетчик ссылок, а затем удалите запись в родительском элементе и уменьшите счетчик ссылок (тот самый, который он только что уменьшил для .
в самом каталоге, который тогда должен быть 0).Все это занимало несколько разных секторов диска, и поэтому необходимо было тщательно настроить, чтобы возможность прерывания (то есть сбоя системы) в любой момент была устранена с помощью fsck.
Конечно, в более современных системах UNIX аппаратные ограничения (например, максимальный размер программы 64 Кбайт, равен «K») были ослаблены, и теперь вы можете выполнить rm -r
и многое из того, что лежит в основе особой природы каталогов, менее очевидны, но они все еще существуют. Я помню, что мне нужно было удалить большое дерево на машине 6-го выпуска, что включало в себя вход в каждый каталог, удаление всех файлов, возвращение к родительскому и выполнение rmdir
и, по сути, выполнение всей рекурсии по всем деревьям каталогов вручную. Мы действительно думали о сценарии, который помог бы с этим, но в те дни он появлялся так редко и был достаточно опасен, что мы решили, что если требовать от кого-то приложить все усилия, это поможет предотвратить катастрофические ошибки.
В первый раз вы получите вопрос, который гласит: «Я набрал ' sudo rm -rf /
' вместо ./
как мне восстановить?» вы можете понять, почему мы были осторожны.
Предполагая, что это просто для случайной отладки, если вы просто хотите захватить код выхода, то, если вы знаете идентификатор последнего демонизированного процесса, вы можете присоединиться к нему с помощью strace -p
и таким образом проследить до его выхода.
Если, например, у вас нет времени получить pid до его выхода, вы можете запустить команду под strace -f
и таким образом следовать дочернему процессу.У меня нет простых демонстраций демонов, но вы можете увидеть, как это может работать, на следующем примере:
$ strace -f -e exit bash -c 'nohup bash -c "sleep 10; exit 2" &'
strace: Process 9688 attached
[pid 9687] +++ exited with 0 +++
nohup: appending output to 'nohup.out'
strace: Process 9689 attached
[pid 9689] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=9689, si_uid=127, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 2 +++
Здесь мы видим, что нам удалось собрать код выхода 2 из внутренней "демонизированной" команды bash.
В качестве альтернативы, если у вас есть systemd , вы можете запустить команду try в отдельной контрольной группе:
$ systemd-run --user --service-type=forking bash -c '(sleep 99;exit 2)& exit 0'
Running as unit run-8772.service.
Я не знаю, как собрать результирующий код выхода, кроме как путем опроса. Когда это сделано слишком рано, демон все еще активен:
$ systemctl --user status run-8772
...
Active: active (running) since Wed 2017-03-29 18:56:43 CEST; 14s ago
CGroup: /user.slice/user-1000.slice/user@1000.service/run-8772.service
|-8774 /usr/bin/bash -c (sleep 99;exit 2)& exit 0
`-8775 sleep 99
После подходящей задержки процесс не работает, и мы получаем доступ к желаемому коду выхода:
$ systemctl --user status run-8772
Active: failed (Result: exit-code) since Wed 2017-03-29 18:58:22 CEST; 3s ago
Main PID: 8774 (code=exited, status=2)
Когда процесс демонизируется, он разветвляется (как минимум один раз ), родительский процесс возвращается немедленно, а дочерний процесс выполняет эту работу.
Когда этот ребенок умирает, у него больше нет родителя. Вернее, он был принятinit
(процессом id 1 ). init
сможет получить статус выхода.
В Linux и начиная с ядра 3.4 вы можете указать, какой процесс будет принимать осиротевшие процессы (для своих потомков и потомков ), используяPR_SET_CHILD_SUBREAPER
prctl()
.
Итак, как и в этом очень похожем вопросе и ответе , если в Linux 3.4+ вы можете запустить свой демон под оболочкой, которая объявляет себя дочерним поджнецом , и он сообщает о статусе выхода. его потомков-сирот:
Здесь используется perl
и жестко заданное значениеPR_SET_CHILD_SUBREAPER
prctl()
:
perl -MPOSIX -le '
require "syscall.ph";
syscall(&SYS_prctl,36,1) >= 0 or die "cannot set subreaper: $!";
if (!fork) {
exec @ARGV;
exit(127);
}
# now reporting on all children and grand-children:
while (($pid = wait) > 0) {
print "$pid: ". WEXITSTATUS($?)
}' your-daemon here