我正在尝试编写一个包装器,它将执行脚本作为会话负责人。
我对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报告。
答案 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.sh
或source ./test.sh
或其他内容时 - 或者就此而言,当您直接从Bash提示符运行setsid
时,Bash将会启动setsid
用于job control目的的新进程组ID,因此setsid
将具有与其进程组ID相同的进程ID(即,它是进程组组长),因此它将fork()
1}}并且不会wait()
。./test.sh
或bash 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 &