setid команды linux
Я пытаюсь написать оболочку, которая будет выполнять сценарий в качестве лидера сеанса. Меня смущает поведение команды linux setsid
, Рассмотрим этот скрипт, называемый test.sh
:
#!/bin/bash
SID=$(ps -p $$ --no-headers -o sid)
if [ $# -ge 1 -a $$ -ne $SID ] ; then
setsid bash test.sh
echo pid=$$ ppid=$PPID sid=$SID parent
else
sleep 2
echo pid=$$ ppid=$PPID sid=$SID child
sleep 2
fi
Выходные данные различаются в зависимости от того, выполнен он или получен:
$ bash
$ SID=$(ps -p $$ --no-headers -o sid)
$ echo pid=$$ ppid=$PPID sid=$SID
pid=9213 ppid=9104 sid= 9104
$ ./test.sh 1 ; sleep 5
pid=9326 ppid=9324 sid= 9326 child
pid=9324 ppid=9213 sid= 9104 parent
$ . ./test.sh 1 ; sleep 5
pid=9213 ppid=9104 sid= 9104 parent
pid=9336 ppid=1 sid= 9336 child
$ echo $BASH_VERSION
4.2.8(1)-release
$ exit
exit
Итак, мне кажется, что setsid
немедленно возвращается, когда скрипт получен, но он ждет своего потомка, когда скрипт выполняется. Почему присутствие контролирующего tty имеет какое-либо отношение к setsid
? Спасибо!
Изменить: Для пояснения я добавил отчеты pid / ppid / sid для всех соответствующих команд.
3 ответа
Исходный код setsid
Утилита на самом деле очень проста. Вы заметите, что это только fork()
s если он видит, что его идентификатор процесса и идентификатор группы процессов равны (т. е. если он видит, что это лидер группы процессов) - и что он никогда wait()
s для его дочернего процесса: если этоfork()
s, то родительский процесс просто сразу возвращается. Если этоне такfork()
, то это дает видимостьwait()
для ребенка, но на самом деле происходит только то, что это ребенок, а это Bash, wait()
Инг (как всегда). (Конечно, когда это действительно fork()
Баш не может wait()
для ребенка это создает, потому что процессы wait()
для своих детей, а не для своих внуков.)
Таким образом, поведение, которое вы видите, является прямым следствием другого поведения:
- когда ты бежишь
. ./test.sh
или жеsource ./test.sh
или еще что - или в этом отношении, когда вы просто бежитеsetsid
прямо из приглашения Bash - Bash запуститсяsetsid
с новым идентификатором группы процессов для целей управления заданиями, такsetsid
будет иметь тот же идентификатор процесса, что и идентификатор группы процессов (то есть, это лидер группы процессов), поэтому он будетfork()
и не будетwait()
, - когда ты бежишь
./test.sh
или жеbash test.sh
или еще много чегоsetsid
,setsid
будет входить в ту же группу процессов, что и скрипт, в котором он выполняется, поэтому его идентификатор процесса и идентификатор группы процессов будут другими, поэтому он не будетfork()
, так что это даст видимость ожидания (безwait()
луг).
Поведение, которое я наблюдаю, то, что я ожидаю, хотя и отличается от вашего. Вы можете использовать set -x
чтобы убедиться, что вы видите вещи правильно?
$./test.sh 1 ребенок родитель $ . test.sh 1 ребенок $ uname -r 3.1.10 $ echo $BASH_VERSION 4.2.20(1)-release
При беге ./test.sh 1
родительский скрипт - интерактивная оболочка - является лидером сессии, поэтому $$ != $SID
и условное верно.
При беге . test.sh 1
интерактивная оболочка выполняет сценарий в процессе и является собственным лидером сеанса, поэтому $$ == $SID
и условие ложно, поэтому никогда не выполняется внутренний дочерний скрипт.
Я не вижу проблем с вашим скриптом как есть. Я добавил дополнительные выражения в ваш код, чтобы увидеть, что происходит:
#!/bin/bash
ps -H -o pid,ppid,sid,cmd
echo '$$' is $$
SID=`ps -p $$ --no-headers -o sid`
if [ $# -ge 1 -a $$ -ne $SID ] ; then
setsid bash test.sh
echo pid=$$ ppid=$PPID sid=$SID parent
else
sleep 2
echo pid=$$ ppid=$PPID sid=$SID child
sleep 2
fi
Случай, который вас касается:
./test.sh 1
И поверьте мне, запустите этот модифицированный скрипт, и вы увидите, что именно происходит. Если оболочка, которая не является лидером сеанса, запускает сценарий, то она просто переходит к else
блок. Я что-то пропустил?
Теперь я понимаю, что вы имеете в виду: когда вы делаете ./test.sh 1
с вашим сценарием как есть, тогда родитель ожидает завершения дочернего процесса. ребенок блокирует родителя. Но если вы начнете ребенка в фоновом режиме, то вы заметите, что родитель завершает до ребенка. Так что просто внесите это изменение в ваш скрипт:
setsid bash test.sh &