首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >超时暂停挂起

超时暂停挂起
EN

Unix & Linux用户
提问于 2021-09-03 21:27:28
回答 1查看 641关注 0票数 0

我不明白为什么执行以下脚本的行为:

代码语言:javascript
运行
复制
$ cat z.sh
saved_stty=$(stty -g)
echo "saved_stty: ${saved_stty}"
stty "${saved_stty}"
$ ./z.sh
saved_stty: 500:5:bf:8a3b:3:1c:7f:15:4:0:1:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
$ echo "${?}"
0

使用超时调用stty后发生的更改:

代码语言:javascript
运行
复制
$ cat z.sh
saved_stty=$(stty -g)
echo "saved_stty: ${saved_stty}"
timeout 10 stty "${saved_stty}"
$ ./z.sh
saved_stty: 500:5:bf:8a3b:3:1c:7f:15:4:0:1:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
$ echo "${?}"
124
$ . ./z.sh
saved_stty: 500:5:bf:8a3b:3:1c:7f:15:4:0:1:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
$ echo "${?}"
0

在第一种情况下,调用./z.sh立即结束(返回值为0),而在第二种情况下,调用./z.sh为10s (超时值为124)。但是,调用. ./z.sh仍然会立即完成(返回值为0)。

通过调试stty,我观察到执行挂起系统调用。

代码语言:javascript
运行
复制
return INLINE_SYSCALL (ioctl, 3, fd, cmd, &k_termios);

在函数int __tcsetattr (int fd, int optional_actions, const struct termios *termios_p) of glibc/sysdeps/unix/sysv/linux/tcsetattr.c中。

我已经给出了一个最小化的问题例子。现在让我让它更加现实:

代码语言:javascript
运行
复制
$ cat z.scala
object Test {
  def main(args: Array[String]): Unit = {
    var j = 0
    val k = 1000
    for (i <- 1 to k) {
      for (i <- 1 to k) {
        j += i
      }
    }
    println(j)
  }
}
$ cat run.sh
timeout 10 scala ./z.scala
echo "${?}"
$ ./run.sh
500500000
124
$ cat z.scala
object Test {
  def main(args: Array[String]): Unit = {
    var j = 0
    val k = 1000000
    for (i <- 1 to k) {
      for (i <- 1 to k) {
        j += i
      }
    }
    println(j)
  }
}
$ cat run.sh
timeout --foreground 10 scala ./z.scala
echo "${?}"
$ ./run.sh
124

scala的第一个调用会立即计算结果,但会挂起stty的内部调用,直到超时。scala的第二次调用应该通过超时来终止,但是执行scala的进程在脚本返回后仍在运行。这与timeout --foreground的文档相匹配,但我仍然想知道如何正确地超时scala。

EN

回答 1

Unix & Linux用户

发布于 2021-09-03 22:09:20

这是因为timeout在后台在单独的进程组中运行命令。

当一个进程是( a)附加到控制终端和( b)不在前台组并试图用tcsetattr()改变终端设置时,它得到一个SIGTTOU信号来阻止它。

这正是在你的例子中发生的。

GNU timeout有一个选项:--foreground,您可以安全地使用这些简单的程序,这些程序试图扰乱控制终端,但是do不是叉,因为这样他们的子程序就不会被杀死。

可能的解决办法是

( a)将程序的stdin/stdout/stderr重定向到其他地方,希望它们不会显式地打开/dev/tty,而不会让控制终端单独使用。

( b)如果他们不能被a说服,那么给他们自己的伪终端来运行,你可以用script(1)这样的程序来完成。请注意,您必须将a)处理应用于script(1),因为它尝试从stdin (这将获得一个SIGTTIN信号)读取,并试图使它与tcseattr() (这将获得SIGTTOU信号)原始:

代码语言:javascript
运行
复制
% cat <<'EOT' > sample.sh; chmod +x sample.sh
#!/bin/sh
t=$(stty -g -F /dev/tty)
sleep 1000 &
echo BEFORE; stty -F /dev/tty "$t"; echo AFTER
EOT

% sh -c 'timeout 2 ./sample.sh'
BEFORE
  # hangs for 2 seconds and exits without writing 'AFTER'
%

% sh -c 'timeout 2 script /dev/null 

请注意,script(1)是不标准化的,它的语法在其他系统上与Linux不同,因此您必须修改这个示例。

( c)如果您有systemd,那么使用systemd-run -t --user,它(与timeout不同)能够捕获和杀死命令生成的任何子级,即使他们试图转义他们的进程组或会话。

票数 1
EN
页面原文内容由Unix & Linux提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://unix.stackexchange.com/questions/667543

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档