bash linux命令setsid
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9677306/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
linux command setsid
提问by Matei David
I am trying to write a wrapper which will execute a script as a session leader.
I am confused by the behaviour of the linux command setsid
. Consider this script, called test.sh
:
我正在尝试编写一个包装器,它将作为会话负责人执行脚本。我对 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
The output differs depending on whether it is executed or sourced:
输出因执行或来源而异:
$ 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
So, it seems to me that setsid
returns immediately when the script is sourced, but it waits for its child when the script is executed.
Why would the presence of a controlling tty have anything to do with setsid
? Thanks!
所以,在我看来,setsid
当脚本被获取时会立即返回,但是当脚本被执行时它会等待它的子进程。为什么控制 tty 的存在与 有任何关系setsid
?谢谢!
Edit: For clarification I added pid/ppid/sid reporting to all relevant commands.
编辑:为了澄清起见,我向所有相关命令添加了 pid/ppid/sid 报告。
回答by ruakh
The source code of the setsid
utilityis actually very straightforward. You'll note that it only fork()
s if it sees that its process ID and process-group ID are equal (i.e., if it sees that it's a process group leader) — and that it never wait()
s for its child process: if it fork()
s, then the parent process just returns immediately. If it doesn'tfork()
, then it gives the appearance of wait()
ing for a child, but really what happens is just that it isthe child, and it's Bash that's wait()
ing (just as it always does). (Of course, when it really does fork()
, Bash can't wait()
for the child it creates, because processes wait()
for their children, not their grandchildren.)
该setsid
实用程序的源代码实际上非常简单。你会注意到它只有fork()
在它看到它的进程 ID 和进程组 ID 相等时(即,如果它看到它是一个进程组领导)——并且它永远不会wait()
为它的子进程fork()
s :如果它,然后父进程立即返回。如果不是fork()
,那么它会wait()
为孩子提供ing的外观,但实际上发生的只是它是孩子,而wait()
ing是 Bash (就像往常一样)。(当然,当它真的这样做时fork()
,Bash 不能wait()
为它创建的子进程,因为进程是wait()
为他们的子进程,而不是他们的孙子进程。)
So the behavior that you're seeing is a direct consequence of a different behavior:
所以你看到的行为是不同行为的直接结果:
- when you run
. ./test.sh
orsource ./test.sh
or whatnot — or for that matter, when you just runsetsid
directly from the Bash prompt — Bash will launchsetsid
with a new process-group-ID for job controlpurposes, sosetsid
will have the same process-ID as its process-group-ID (that is, it's a process group leader), so it willfork()
and won'twait()
. - when you run
./test.sh
orbash test.sh
or whatnot and it launchessetsid
,setsid
will be part of the same process group as the script that's running it, so its process-ID and process-group-ID will be different, so it won'tfork()
, so it'll give the appearance of waiting (without actuallywait()
ing).
- 当您运行
. ./test.sh
orsource ./test.sh
或诸如此类时——或者就此而言,当您setsid
直接从 Bash 提示符运行时——Bash 将setsid
使用新的进程组 ID启动以进行作业控制,因此setsid
将具有与其进程相同的进程 ID—— group-ID(也就是说,它是一个进程组领导),所以它会fork()
也不会wait()
。 - 当您运行
./test.sh
或bash test.sh
或诸如此类并启动时setsid
,setsid
它将与运行它的脚本属于同一进程组,因此其进程 ID 和进程组 ID 将不同,因此不会fork()
,因此它会给人一种等待的感觉(实际上没有wait()
ing)。
回答by ephemient
The behavior I observe is what I expect, though different from yours. Can you use set -x
to make sure you're seeing things right?
我观察到的行为是我所期望的,尽管与你的不同。你能用它set -x
来确保你看到的东西是正确的吗?
$ ./test.sh 1 child parent $ . test.sh 1 child $ uname -r 3.1.10 $ echo $BASH_VERSION 4.2.20(1)-release
When running ./test.sh 1
, the script's parent — the interactive shell — is the session leader, so $$ != $SID
and the conditional is true.
运行时./test.sh 1
,脚本的父级——交互式外壳——是会话领导者,因此$$ != $SID
条件为真。
When running . test.sh 1
, the interactive shell is executing the script in-process, and is its own session leader, so $$ == $SID
and the conditional is false, thus never executing the inner child script.
运行时. test.sh 1
,交互式 shell 正在执行进程内脚本,并且是它自己的会话领导者,因此$$ == $SID
条件为假,因此永远不会执行内部子脚本。
回答by Ankur Agarwal
I do not see any problem with your script as is. I added extra statements in your code to see what is happening:
我没有看到您的脚本有任何问题。我在您的代码中添加了额外的语句以查看发生了什么:
#!/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
The case that concerns you is:
您关心的情况是:
./test.sh 1
And trust me run this modified script and you will see exactly what is happening. If the shell which is not a session leader runs the script then it simply goes to else
block. Am I missing something?
相信我运行这个修改过的脚本,你会看到到底发生了什么。如果不是会话领导者的 shell 运行该脚本,那么它只会进入else
阻塞状态。我错过了什么吗?
I see now what you mean: When you do ./test.sh 1
with your script as is then parent waits for the child to complete. child blocks the parent. But if you start the child in background then you will notice that parent completes before child. So just make this change in your script:
我现在明白你的意思了:当你./test.sh 1
按原样处理脚本时,父母会等待孩子完成。孩子阻止了父母。但是如果你在后台启动孩子,那么你会注意到父母在孩子之前完成。因此,只需在您的脚本中进行此更改:
setsid bash test.sh &