USR1 信号后可靠地终止睡眠进程

2024-04-23

我正在编写一个 shell 脚本,它定期执行任务并从另一个进程接收 USR1 信号。

该脚本的结构类似于这个答案 https://stackoverflow.com/a/27694965/1402511:

#!/bin/bash

trap 'echo "doing some work"' SIGUSR1

while :
do
    sleep 10 && echo "doing some work" &
    wait $!
done

然而,该脚本存在一个问题,即睡眠进程在后台继续运行,并且仅在超时时终止。 (请注意,当在 wait $! 期间收到 USR1 时,睡眠进程会在其常规超时时间内徘徊,但周期性回显确实会被取消。)例如,您可以使用以下命令查看计算机上的睡眠进程数pkill -0 -c sleep.

I read 这一页 http://mywiki.wooledge.org/SignalTrap#When_is_the_signal_handled.3F,这表明在陷阱操作中杀死挥之不去的睡眠,例如

#!/bin/bash

pid=
trap '[[ $pid ]] && kill $pid; echo "doing some work"' SIGUSR1

while :
do
    sleep 10 && echo "doing some work" &
    pid=$!
    wait $pid
    pid=
done

然而,如果我们快速发送 USR1 信号,该脚本就会出现竞争条件。和:

pkill -USR1 trap-test.sh; pkill -USR1 trap-test.sh

然后它会尝试杀死一个已经被杀死的PID并打印一个错误。更不用说,我不喜欢这段代码。

有没有更好的方法可以在中断时可靠地终止分叉进程?或者实现相同功能的替代结构?


由于后台作业是前台作业的一个分支,因此它们具有相同的名称(trap-test.sh); so pkill两者都匹配并发出信号。这会以不确定的顺序杀死后台进程(留下sleep活着,下面解释)并触发前台陷阱,因此出现竞争条件。

此外,在您链接的示例中,后台作业始终只是一个sleep x,但在你的脚本中是sleep 10 && echo 'doing some work';这需要分叉的子 shell 等待sleep终止并有条件地执行echo。比较这两个:

$ sleep 10 &
[1] 9401
$ pstree 9401
sleep
$
$ sleep 10 && echo foo &
[2] 9410
$ pstree 9410
bash───sleep

因此,让我们从头开始并在终端中重现主要问题。

$ set +m
$ sleep 100 && echo 'doing some work' &
[1] 9923
$ pstree -pg $$
bash(9871,9871)─┬─bash(9923,9871)───sleep(9924,9871)
                └─pstree(9927,9871)
$ kill $!
$ pgrep sleep
9924
$ pkill -e sleep
sleep killed (pid 9924)

我禁用了作业控制以部分模拟非交互式 shell 的行为。

杀死后台作业并没有杀死sleep,我需要手动终止它。发生这种情况是因为发送到进程的信号不会自动广播到其目标的子进程; IE。sleep根本没有收到TERM信号。

To kill sleep以及子shell,我需要将后台作业放入一个单独的进程组—这需要启用作业控制,否则所有作业都会放入主 shell 的进程组中,如下所示pstree上面的输出—,并向其发送 TERM 信号,如下所示。

$ set -m
$ sleep 100 && echo 'doing some work' &
[1] 10058
$ pstree -pg $$
bash(9871,9871)─┬─bash(10058,10058)───sleep(10059,10058)
                └─pstree(10067,10067)
$ kill -- -$!
$
[1]+  Terminated              sleep 100 && echo 'doing some work'
$ pgrep sleep
$

通过对这个概念的一些改进和调整,您的脚本如下所示:

#!/bin/bash -
set -m

usr1_handler() {
  kill -- -$!
  echo 'doing some work'
}

do_something() {
  trap '' USR1
  sleep 10 && echo 'doing some work'
}

trap usr1_handler USR1 EXIT

echo "my PID is $$"

while true; do
  do_something &
  wait
done

这将打印my PID is xxx (where xxx是前台进程的PID)并开始循环。发送 USR1 信号至xxx (i.e kill -USR1 xxx)将触发陷阱并导致后台进程及其子进程终止。因此wait将返回并且循环将继续。

如果你使用pkill相反,它无论如何都会工作,因为后台进程会忽略 USR1。

欲了解更多信息,请参阅:

  • Bash 参考手册§特殊参数 ($$ and $!) https://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html#Special-Parameters,
  • POSIX kill规格 (-$! usage) https://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html,
  • POSIX 定义 § 作业控制(如何在 POSIX shell 中实现作业控制) https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap03.html#tag_21_03_00_36,
  • Bash 参考手册 § 作业控制基础知识(作业控制在 bash 中如何工作) https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html#Job-Control-Basics,
  • POSIX Shell 命令语言§信号和错误处理 https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_11,
  • POSIX wait规格 https://pubs.opengroup.org/onlinepubs/9699919799/utilities/wait.html.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

USR1 信号后可靠地终止睡眠进程 的相关文章

  • bash 调整图像尺寸以适合特定大小

    我到处搜索但找不到这个问题的答案 我想精确输出一个文件夹中的所有图像 大小为 50Kb 并保持原始的宽高比 I tried ImageMagick并将大小调整为 250x250 例如 但它对我不起作用 它所做的是更改第一个尺寸并适应另一个尺
  • 配置错误:无法链接到 boost_system

    我正在尝试在 Debian 上安装一个软件包 足球模拟器 2d 当我进入目录并运行时 configure 我得到以下信息 reza debian soccer rcssserver 15 0 1 configure checking for
  • 在bash中将两个变量相除

    我试图在 bash 中划分两个 var 这就是我得到的 var1 3 var2 4 echo var1 var2 我总是遇到语法错误 有谁知道出了什么问题吗 shell 解析仅对整数除法有用 var1 8 var2 4 echo var1
  • bash 用变量值替换字符串中的变量名

    这有点奇怪 我有以下字符串 我有一个名为 REDIRECT 的变量设置为 https working MYDOMAIN blah blah 我需要将 MYDOMAIN 替换为分配给 MYDOMAIN 的变量的实际值 不确定 bash 还是
  • Node exec 无权执行脚本

    直到最近 它都运行良好 但是当我今天尝试使用它时 它无法正常运行 它返回以下错误 错误 命令失败 bin sh c home pi RPi Computer Power RPi Server routes scripts hash js 1
  • 有没有比使用 backtrace() 更便宜的方法来查找调用堆栈的深度?

    我的日志记录代码使用的返回值回溯 http linux die net man 3 backtrace确定当前堆栈深度 出于漂亮的打印目的 但我可以从分析中看到这是一个相当昂贵的调用 我不认为有更便宜的方法吗 请注意 我不关心帧地址 只关心
  • 如何在 Linux 上调用 Python 中的内联机器代码?

    我正在尝试从 Linux 上的纯 Python 代码调用内联机器代码 为此 我将代码嵌入到字节文字中 code b x55 x89 xe5 x5d xc3 然后打电话mprotect http www kernel org doc man
  • 在 UNIX 时间戳 Shell/Bash 中将日期与时区转换

    我需要将日期从格式为 yyyy mm dd hh mm ss TZ 的字符串转换为 UNIX 时间 TZ 时区 到目前为止我所做的是将没有时区的 yyyy mm dd hh mm ss 格式的日期转换为时间戳 dateYMD 2019 2
  • __libc_start_main 发生了什么?

    我真的很想理解从高级代码到可执行文件的步骤 但是遇到了一些困难 我写了一个空的int main C 文件并尝试通过以下方式破译反汇编objdump d 这是发生的事情 in start 设置对齐方式 将参数压入堆栈 调用 libc star
  • 从 bash 脚本返回值

    我想创建一个返回值的 Bash 文件 意思是 在脚本 script a bash 中我有一定的计算 脚本 script b bash 会调用它 script a bash return 1 5 script b bash a value s
  • 在类中使用静态互斥体

    我有一个可以有很多实例的类 它在内部创建并初始化来自第三方库 使用一些全局变量 的一些成员 并且不是线程安全的 我考虑过使用 static boost mutex 它将被锁定在我的类构造函数和析构函数中 因此 在我的线程中创建和销毁实例对于
  • $PATH 中 /usr/bin 和 /usr/local/bin 等的顺序

    在我的 Mac 上 我经常使用 bash 对于我的环境设置 我添加了 usr bin and usr local bin into PATH就像我平常做的那样 虽然我知道什么 usr bin and usr local bin关于 我很好奇
  • 想要运行命令列表,但能够在运行时编辑该列表

    我有一个要运行的 bash 命令列表
  • 在linux x86平台上学习ARM所需的工具[关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我有一个 x86 linux 机器 在阅读一些关于 ARM 的各种信息时 我很好奇 现在我想花一些时间学
  • 每当调用 malloc/free 时输出到 stderr

    使用 Linux GCC C 每当调用 malloc free new delete 时 我想向 stderr 记录一些内容 我试图了解库的内存分配 因此我想在运行单元测试时生成此输出 我使用 valgrind 进行内存泄漏检测 但我找不到
  • Linux 的 gcc __attribute__((selectany)) 替代方案?

    我想知道是否有替代方案 attribute selectany 在Linux中 我想定义这样的东西 char a qwe zxc 并将其包含在许多链接在一起的 c 文件中 因此链接器将看到 a 的多个定义 因此不会链接 我读过这个属性 se
  • 使用 GitHub 时防止将大文本文件添加到提交

    我们想要防止 非常大的文本文件 每个文件 gt 50MB 被提交到git代替git lfs 因为它们夸大了 git 历史 问题是 其中 99 大小差异的原因 这些是 YAML 文件 它们支持通过 Base64 编码进行二进制序列化 我们无法
  • 除了 iptables 之外还有数据包管理实用程序吗? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个 Linux 实用程序 它可以根据一组规则更改网络数据包的有效负载 理想情况下 我会使用
  • 从 Java 读取 /dev/input/js0

    我正在尝试阅读 dev input js0来自Java 但我不断得到 java io IOException Invalid argument at java io FileInputStream read0 Native Method a
  • 将 sudo 与 Python 脚本结合使用

    我正在尝试编写一个小脚本来在每次执行脚本时安装 VirtualBox 共享文件夹 我想用Python 来做这件事 因为我正在尝试学习它来编写脚本 问题是我需要特权才能启动挂载命令 我可以将脚本作为 sudo 运行 但我更喜欢它自己创建 su

随机推荐