rid*_*ish 10 shell process signals tty process-groups
(Re-posting in unix per the suggestion in /sf/ask/960287611/)
The short question is, what should a shell do if it is in an orphaned process group that doesn't own the tty? But I recommend reading the long question because it's amusing.
Here is a fun and exciting way to turn your laptop into a portable space heater, using your favorite shell (unless you're one of those tcsh weirdos):
#include <unistd.h>
int main(void) {
if (fork() == 0) {
execl("/bin/bash", "/bin/bash", NULL);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
This causes bash to peg the CPU at 100%. zsh and fish do the same, while ksh and tcsh mumble something about job control and then keel over, which is a bit better, but not much. Oh, and it's a platform agnostic offender: OS X and Linux are both affected.
My (potentially wrong) explanation is as follows: the child shell detects it is not in the foreground: tcgetpgrp(0) != getpgrp()
. Therefore it tries to stop itself: killpg(getpgrp(), SIGTTIN)
. But its process group is orphaned, because its parent (the C program) was the leader and died, and SIGTTIN
sent to an orphaned process group is just dropped (otherwise nothing could start it again). Therefore, the child shell is not stopped, but it's still in the background, so it does it all again, right away. Rinse and repeat.
My question is, how can a command line shell detect this scenario, and what is the right thing for it to do? I have two solutions, neither of which is ideal:
ESRCH
, it means we're probably orphaned./dev/tty
. If that fails with EIO
, it means we're probably orphaned.(Our issue tracking this is https://github.com/fish-shell/fish-shell/issues/422 )
Thanks for your thoughts!
我同意你的分析,并且我同意听起来你必须检测你的进程组是否是孤立的。
tcsetattr
如果进程组是孤立的,也意味着返回EIO
(并且我们不会阻止/忽略 SIGTT OUread
。这可能是比终端上的a 更少侵入的方式。
请注意,您可以使用以下命令重现它:
(bash<&1 &)
Run Code Online (Sandbox Code Playgroud)
您需要重定向,否则在后台运行命令时 stdin 会重定向到 /dev/null 。
(bash<&1 & sleep 2)
Run Code Online (Sandbox Code Playgroud)
给出更奇怪的行为,因为你最终会从终端读取两个 shell。他们会忽略SIGTTIN
,并且新的进程不会检测到,一旦启动它就不再位于前台进程组中。
ksh93
的解决方案还不错:在放弃之前最多只执行该循环 20 次(而不是无限次)。
归档时间: |
|
查看次数: |
1137 次 |
最近记录: |