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 &
Другие вопросы по тегам