linux命令setsid

时间:2012-03-13 01:46:56

标签: bash sessionid

我正在尝试编写一个包装器,它将执行脚本作为会话负责人。 我对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 个答案:

答案 0 :(得分:18)

The source code of the setsid utility实际上非常简单。你会注意到它只有fork()才会看到它的进程ID和进程组ID相等(即,如果它看到它是一个进程组组长) - 并且它永远不会wait() s为其子进程:如果它fork() s,则父进程立即返回。如果它 fork(),那么它会为孩子提供wait()的外观,但实际上发生的事情只是它 孩子,这是Bash wait()(就像它一直一样)。 (当然,当它确实fork()时,Bash不能为它创建的孩子wait(),因为他们的孩子,而不是他们的孙子的进程wait()。)

因此,您所看到的行为是不同行为的直接后果:

  • 当您运行. ./test.shsource ./test.sh或其他内容时 - 或者就此而言,当您直接从Bash提示符运行setsid时,Bash将会启动setsid用于job control目的的新进程组ID,因此setsid将具有与其进程组ID相同的进程ID(即,它是进程组组长),因此它将fork() 1}}并且不会wait()
  • 当您运行./test.shbash test.sh或其他内容并启动setsid时,setsid将与运行该脚本的脚本属于同一进程组,因此其进程ID和进程组ID将不同,因此它不会fork(),因此它会给出等待的外观(实际上没有wait())。

答案 1 :(得分:1)

我观察到的行为是我所期望的,尽管与你的不同。您可以使用set -x确保您看到正确的事情吗?

$ ./test.sh 1
child
parent
$ . test.sh 1
child
$ uname -r
3.1.10
$ echo $BASH_VERSION
4.2.20(1)-release

运行./test.sh 1时,脚本的父级 - 交互式shell - 是会话负责人,因此$$ != $SID和条件为真。

运行. test.sh 1时,交互式shell正在执行脚本进程,并且是自己的会话负责人,因此$$ == $SID和条件为false,因此从不执行内部子脚本。< / p>

答案 2 :(得分:0)

我认为您的脚本没有任何问题。我在您的代码中添加了额外的语句,以查看发生的情况:

    #!/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 

相信我运行这个修改过的脚本,你会看到到底发生了什么。如果不是会话负责人的shell运行脚本,那么它只是转到else块。我错过了什么吗?

我现在明白你的意思了:当你用脚本做./test.sh 1时,父母会等待孩子完成。孩子阻止父母。但是如果你在后台启动孩子,那么你会发现父母在孩子面前完成了。所以只需在脚本中进行此更改:

      setsid bash test.sh &