如何诊断发送到 sshd 的信号 15 的发起者

如何诊断发送到 sshd 的信号 15 的发起者

我最近遇到一个问题,我的 sshd 反复死机,我做了以下检查。

  1. /var/log/message,我可以看到 sshd 不断重新启动

    Apr 23 12:05:39 host1  systemd: Stopping OpenSSH server daemon...
    Apr 23 12:05:39 host1  systemd: Stopped OpenSSH server daemon.
    Apr 23 12:05:39 host1  systemd: Starting OpenSSH server daemon...
    Apr 23 12:05:39 host1  systemd: Started OpenSSH server daemon.
    
  2. /var/log/secure,我发现sshd有时会收到singal 15

    Apr 23 11:32:52 host1  sshd[105049]: Received signal 15; terminating.
    Apr 23 11:32:52 host1  sshd[111278]: Server listening on 0.0.0.0 port 36000.
    Apr 23 11:32:52 host1  sshd[111278]: Received signal 15; terminating.
    Apr 23 11:32:52 host1  sshd[111306]: Server listening on 0.0.0.0 port 36000.
    
  3. 我查了一下sshd -t,没有语法错误

  4. 为了检查谁正在向 sshd 发送信号 15,我尝试了以下操作,下次应该如何尝试?

    [root@host1 /sys/kernel/debug/tracing/events/signal/signal_deliver]# echo 1 > /sys/kernel/debug/tracing/events/signal/enable
    [root@host1 /sys/kernel/debug/tracing/events/signal/signal_deliver]# echo "sig == 15" > /sys/kernel/debug/tracing/events/signal/filter 
    

答案1

没有通用的 Unix 方法可以做到这一点。信号本身不携带该信息。这取决于实际的操作系统。因此,正如您已经意识到的那样,您需要使用内核跟踪来找出 Linux 上的这一问题。

您决定的跟踪点可能不是最佳的 - 您正在观察信号的传递位置,而不是信号的起源位置!因此,signal_deliver您可能想要跟踪kill系统调用的入口(因为我们知道谁发送信号)和它的出口(因为我们知道它是否实际上已被传递),而不是跟踪系统调用的入口。

虽然那听起来很复杂,但有一个工具已经可以做到这一点:
bpftrace实用程序附带了很多示例。我不知道您的 Linux 发行版,但是当我安装该bpftrace软件包时,/usr/share/bpftrace/tools 包含很多很好的跟踪实用程序。

根据您的情况,您需要做什么(如果您已经这样做root,请删除sudo):

cd /usr/share/bpftrace/tools  # or whichever place these tools have been installed to
sudo ./killsnoop

假设你想要仅有的观看信号 15,您将需要像这样修改脚本:

#!/usr/bin/bpftrace
/*
 * signoop    Trace who issues signal 15 to whom
 * Base on Brendan Gregg's killsnoop, "Trace signals issued by the kill() syscall",

 * USAGE: sigsnoop.bt

 * Copyright 2018 Netflix, Inc.
 * Copyright 2023 Marcus Müller
 * Licensed under the Apache License, Version 2.0 (the "License")
 *
 * 07-Sep-2018  Brendan Gregg   Created this.
 * 23-Apr-2023  Marcus Müller made it worse :)
 */

BEGIN
{
    printf("Tracing signal 15... Hit Ctrl-C to end.\n");
    printf("%-9s %-6s %-16s %-4s %-6s %s\n", "TIME", "PID", "COMM", "SIG",
        "TPID", "RESULT");
}

tracepoint:syscalls:sys_enter_kill
{
    /* here's the relevant part: filter by sig */
  if (args->sig == 15) {
        @tpid[tid] = args->pid;
        @tsig[tid] = args->sig;
    }
}

tracepoint:syscalls:sys_exit_kill
/@tpid[tid]/
{
    time("%H:%M:%S  ");
    printf("%-6d %-16s %-4d %-6d %d\n", pid, comm, @tsig[tid], @tpid[tid],
        args->ret);
    delete(@tpid[tid]);
    delete(@tsig[tid]);
}

将其另存为sigsnoop.bt,授予它任何人都可以执行的权限 ( chmod 755 sigsnoop.bt),并以 root 身份运行它 ( sudo ./signsnoop.bt)

答案2

另一种选择是使用系统水龙头。您可以利用signal.send探测。

例如,要监视发送到进程名称的信号 15 sshd

$ stap -e 'probe begin { print("Starting monitoring...\n")} 
probe signal.send { 
  if (sig == 15 && pid_name == "sshd") 
    printf ("%s(%d) sent signal 15 to %s(%d)\n", execname(), pid(), pid_name, sig_pid)
}'

输出将类似于:

Starting monitoring...
bash(22405) sent signal 15 to sshd(15261)

您甚至可以检查发送信号的进程的命令行

$ stap -e 'probe begin { print("Starting monitoring...\n")}
probe signal.send {
  if (sig == 15 && pid_name == "sshd")
    printf ("%s(%d) (cmdline: \"%s\") sent signal 15 to %s(%d)\n", execname(), pid(),  cmdline_str(), pid_name, sig_pid)
}'

Starting monitoring...
kill(20811) (cmdline: "/usr/bin/kill -15 20809") sent signal 15 to sshd(20809)

您可以在需要时添加更多信息。看看还有哪些水龙头组由 提供systemtap

答案3

感谢大家的热心帮助。

我终于发现有一个脚本调用了

service sshd restart

多次重复导致了问题。通过注释掉该声明,sshd不再有任何问题了。

相关内容