【问题标题】:Simulating a process stuck in a blocking system call模拟卡在阻塞系统调用中的进程
【发布时间】:2014-12-16 12:37:02
【问题描述】:

我正在尝试测试一种在受控环境中难以重现的行为。

用例: Linux系统;通常是 Redhat EL 5 或 6(我们刚开始使用 RHEL 7 和 systemd,所以目前不在范围内)。

在某些情况下,我需要重新启动服务。我们用于停止服务的脚本通常运行良好;它向进程发送一个 SIGTERM,该进程旨在处理它;如果进程在超时(通常是几分钟)内没有处理 SIGTERM,则脚本会发送一个 SIGKILL,然后再等待几分钟。

问题是:在某些(罕见的)情况下,进程在 SIGKILL 之后不会退出;这通常发生在它严重卡在系统调用上时,可能是由于内核级别的问题(损坏的文件系统,或不工作的 NFS 文件系统,或同样糟糕的需要手动干预的事情)。

当脚本没有意识到“旧”进程实际上并没有退出并在旧进程仍在运行时启动新进程时,就会出现错误;我们正在使用更强大的锁定系统来解决这个问题(这样至少如果旧进程正在运行,新进程不会启动),但是我发现很难测试整个事情,因为我没有' t 找到了模拟困难过程的方法。

所以,问题是:

如何手动模拟在向其发送 SIGKILL 时不退出的进程,即使作为特权用户也是如此?

【问题讨论】:

  • 您是否检查过系统日志并查找处于不可中断或阻塞状态的进程是否可能卡在执行 I/O。
  • @askb 我不是在问“为什么进程卡住了”。我可以处理,通常会有内核无法处理的硬故障。我想让我的服务脚本更好,所以我问“如何在不间断的系统调用中阻止进程”以执行我的测试。

标签: linux process linux-kernel


【解决方案1】:

如果你的进程在做 I/O 时卡住了,你可以这样模拟你的情况:

lvcreate -n lvtest -L 2G vgtest
mkfs.ext3 -m0 /dev/vgtest/lvtest
mount /dev/vgtest/lvtest /mnt
dmsetup suspend /dev/vgtest/lvtest && dd if=/dev/zero of=/mnt/file.img bs=1M count=2048 &

这样 dd 进程将等待 IO 并忽略每个信号,我知道当进程在 nfs 文件系统上等待 IO 时,信号在最新内核中不会被忽略。

【讨论】:

  • 不错的方法。谢谢。但是最后一个命令可能会首先创建一个 bash 进程,并且在当前 bash 会话中不能等待 dd 命令。
【解决方案2】:

嗯...不发送 SIGKILL 怎么样?所以你的环境会表现得像它被发送一样,但进程并没有退出。

【讨论】:

    【解决方案3】:

    一旦进程在内核代码路径中处于“D”状态(或 TASK_UNINTERRUPTIBLE),在处理任务时无法中断执行,这意味着向进程发送任何信号都将无用并且将被忽略.

    这可能是由于设备驱动程序从硬件获得太多中断、获得太多传入网络数据包、来自 NIC 固件的数据或在执行 I/O 的 HDD 上被阻塞。通常情况下,如果这种情况发生得非常快,并且线程会在很短的时间内保持这种状态。

    因此,您需要做的是查看进程卡在 D 状态期间的 syslog 和 sar 报告。如果您在日志中找到堆栈跟踪,请尝试在 kernel.bugzilla.org 中搜索类似问题或寻求 Linux 供应商的支持。

    【讨论】:

      【解决方案4】:

      我会以相反的方式编码。让您的服务器进程将其 pid 写入例如/var/run/yourserver.pid(这是常见做法)。让启动脚本读取该文件并测试该进程是否不存在,例如使用信号 0 的 kill,或使用

      yourserver_pid=$(cat /var/run/yourserver.pid)
      if [ -f /proc/$yourserver_pid/exe ]; then
      

      您可以通过 readlink /proc/$yourserver_pid/exe 改进它并将其与 /usr/bin/yourserver 进行比较

      顺便说一句,在SIGKILL 之后的几秒钟内进程仍然处于活动状态是一种严重的情况(可能发生的常见情况是进程卡在D 状态,等待某个 NFS 服务器),你可能应该检测并记录它(例如在你的脚本中使用logger)。

      我也会尝试先发送SIGTERM,等待几秒钟,发送SIGQUIT,等待几秒钟,最后发送SIGKILL,几秒钟后测试服务器进程已经消失

      【讨论】:

        【解决方案5】:

        当脚本没有意识到“旧”进程实际上并没有退出并在旧进程仍在运行时启动新进程时出现了错误;

        这是操作系统/内核级别的错误,而不是您的服务脚本中的错误。这种情况很少见,而且很难模拟,因为OS is supposed to kill the process when SIGKILL signal happens。所以我猜你的目标是让你的脚本在有缺陷的内核下运行良好。那是对的吗?

        【讨论】:

          【解决方案6】:

          您可以将 gdb 附加到进程,SIGKILL 不会从进程列表中删除此类进程,但会将其标记为僵尸,这对于您的目的可能仍然可以接受。

          void@tahr:~$ ping 8.8.8.8 > /tmp/ping.log &
          [1] 3770
          void@tahr:~$ ps 3770
          PID TTY      STAT   TIME COMMAND
          3770 pts/13   S      0:00 ping 8.8.8.8
          
          void@tahr:~$ sudo gdb -p 3770
          ...
          (gdb)
          

          其他终端

          void@tahr:~$ ps 3770 
          PID TTY      STAT   TIME COMMAND
          3770 pts/13   t      0:00 ping 8.8.8.8
          
          sudo kill -9 3770
          ...
          void@tahr:~$ ps 3770
          PID TTY      STAT   TIME COMMAND
          3770 pts/13   Z      0:00 [ping] <defunct>
          

          又是第一个终端

          (gdb) quit
          

          【讨论】:

            猜你喜欢
            • 2014-07-15
            • 2017-10-03
            • 2013-04-26
            • 1970-01-01
            • 2020-11-11
            • 1970-01-01
            • 2015-05-24
            • 1970-01-01
            • 2012-06-19
            相关资源
            最近更新 更多