模拟阻塞系统调用中的进程

时间:2014-12-16 12:37:02

标签: linux process linux-kernel

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

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

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

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

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

所以,问题是:

如何向用户发送SIGKILL时,如何手动模拟未退出的进程,即使是特权用户呢?

6 个答案:

答案 0 :(得分:6)

如果您的流程在执行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时,信号不会被忽略。

答案 1 :(得分:1)

那么......不发送SIGKILL怎么样?所以你的env会表现得像发送的那样,但是这个过程并没有退出。

答案 2 :(得分:1)

一旦进程处于" D"状态(或TASK_UNINTERRUPTIBLE)在内核代码路径中,在执行任务时不能中断执行,这意味着向进程发送任何信号都没有用,将被忽略。

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

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

答案 3 :(得分:0)

我会以相反的方式编码。让您的服务器进程在其中写入其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服务器),您可能应该检测并对其进行syslog(例如,在脚本中使用logger)。

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

答案 4 :(得分:0)

  

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

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

答案 5 :(得分:0)

您可以将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