Bash linux命令setsid

Bash linux命令setsid,bash,sessionid,Bash,Sessionid,我正在尝试编写一个包装器,它将作为会话引导执行脚本。 我对linux命令的行为感到困惑setsid。考虑这个脚本,叫做 Test.S/: #!/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=$$ ppi

我正在尝试编写一个包装器,它将作为会话引导执行脚本。 我对linux命令的行为感到困惑
setsid
。考虑这个脚本,叫做<代码> Test.S/<代码>:

#!/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报告。

我观察到的行为是我所期望的,尽管与您的不同。你能用
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 小孩 父母亲 $ . 测试1 小孩 $uname-r 3.1.10 $echo$BASH_版本 4.2.20(1)-发布 运行
/test.sh 1
时,脚本的父脚本(交互式shell)是会话的领导者,因此
$$!=$SID
且条件为true


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

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

    #!/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 &
其实很简单。您将注意到,只有当它看到它的进程ID和进程组ID相等时(即,如果它看到它是一个进程组组长),它才会
fork()
s,并且它从不
wait()
s等待它的子进程:如果它
fork()
s,那么父进程就会立即返回。如果它不是
fork()
,那么它为一个孩子提供了
wait()
ing的外观,但真正发生的是它就是这个孩子,而Bash就是
wait()
ing(就像它一直做的那样)。(当然,当Bash确实执行
fork()
时,它不能为它创建的子进程
等待()
,因为进程
为它们的子进程等待()
,而不是为它们的孙辈。)

所以你所看到的行为是不同行为的直接结果:

  • 当您运行
    时/test.sh
    source./test.sh
    或诸如此类-或者,当您直接从Bash提示符运行
    setsid
    时,Bash将使用新的进程组ID启动
    setsid
    ,因此
    setsid
    将具有与其进程组ID相同的进程ID(即,它是一个进程组组长),因此它将
    fork()
    ,而不会
    wait()
  • 当您运行
    /test.sh
    bash test.sh
    或诸如此类的程序并启动
    setsid
    时,
    setsid
    将与运行它的脚本属于同一进程组,因此它的进程ID和进程组ID将不同,因此它不会
    fork()
    ,因此它将呈现等待的外观(实际上没有
    wait()
    ing)

你说得对;但我关心的是,当启动脚本(执行或源代码)的shell不是会话负责人时会发生什么。请尝试添加另一级别的
bash
,就像我在原始示例中所做的那样。(我试图实现的是以一个有保证的会话负责人的身份运行脚本。如果我假设是这样,那么将脚本放在首位就没有意义了。)我想要这段代码的原因是要将它合并到一个更复杂的脚本中,而这个脚本反过来可以手动执行,或者手动源代码,甚至可以由SGE作业引擎运行。我需要的是父级和子级的一致行为。理想情况下:if
setId
是必需的(即,我还不是会话负责人)然后,我希望家长等待孩子。在我的示例中,if部分在两种情况下都是真的:parent
PID
!=parent
SID
。让我感到不安的是,当脚本来源时,then部分不起作用:parent在child之前退出。你是对的。我想知道是否值得建议
setid
采用extra标志,比如说
-w
,如果有孩子的话,它应该在这个标志上等待。事实上,我觉得它的行为是不一致的:当且仅当它由一个组长运行时,它会立即返回(并且会分叉)另外,就像你说的,只有
setId
可以等待它的孩子,调用
bash
的人不能等待孙子。是的;一般来说,没有
wait
fork
有点奇怪。我想我的大学操作系统教授不会同意的-P@MateiDavid链接的源代码contains条目2008-08-20 Daniel Kahn Gillmor-如果分叉,等待子进程并发出其返回代码。(
-w
选项),这就是您建议在四年后写的上述注释中添加的内容。我缺少什么?