[LINUX] Signal Kennen Sie den Absender

Einführung

Das Signal wird häufig für die asynchrone Kommunikation zwischen Prozessen verwendet. Ich muss den Absender kennen und habe versucht herauszufinden, wie es geht, also werde ich es als Memorandum belassen.

Die Umgebung, die ich ausprobiert habe, ist wie folgt.

$ lsb_release -d
Description:	Ubuntu 18.04.4 LTS
$ uname -r
5.3.0-61-generic
$ trace-cmd --version | grep version
trace-cmd version 2.6.1
$ stap --version | head -1
Systemtap translator/driver (version 4.3/0.170, commit release-4.3-0-gc9c23c987d81)

Methode 1. Wissen durch Sigaktion (2)

Dies ist ein Ansatz, um die Quellinformationen beim Empfang des Signals abzurufen.

Sie können den Vorgang des Signalempfangs ändern. Und es ist eine Methode, die angewendet werden kann, wenn das Signal, das im Prozess verarbeitet werden kann, Gegenstand einer Untersuchung ist. Es kann nicht für Signale verwendet werden, die vom Prozess nicht verarbeitet werden können, z. B. SIGKILL.

Die Unterzeichnung (2) kann durch Angabe von "SA_SIGINFO" in "sa_flags" erfolgen. Der Signalhandler kann detaillierte Informationen über das Signal "siginfo_t" empfangen. Sie können die PID des Absenders mit "si_pid" und die tatsächliche Benutzer-ID des Absenders mit "si_uid" kennen.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

static int g_sig;
static siginfo_t g_siginfo;

static void
my_sigaction (int sig, siginfo_t *info, void *ucontext)
{
  g_sig = sig;
  // memcpy(3) is a async-signal-safe function
  // according to man signal-safety(7)
  memcpy (&g_siginfo, info, sizeof(g_siginfo));
}

static char*
code2str (int code)
{
  switch (code)
    {
    case SI_USER: return "SI_USER";
    case SI_KERNEL: return "SI_KERNEL";
    case SI_QUEUE: return "SI_QUEUE";
    case SI_TIMER: return "SI_TIMER";
    case SI_MESGQ: return "SI_MESGQ";
    case SI_ASYNCIO: return "SI_ASYNCIO";
    case SI_SIGIO: return "SI_SIGIO";
    case SI_TKILL: return "SI_TKILL";
    default: return "unknown";
    }
}

int
main (void)
{
  struct sigaction act;
  act.sa_flags = SA_SIGINFO;
  act.sa_sigaction = my_sigaction;
  int ret = sigaction(SIGTERM, &act, NULL);
  if (ret < 0)
    {
      perror ("sigaction");
      exit (EXIT_FAILURE);
    }
  printf ("pid: %d\n", getpid());

  sleep (10000);

  fprintf (stderr, "sig: %d, si_pid: %d, si_uid: %d, si_code: %s\n",
           g_sig,
           g_siginfo.si_pid,
           g_siginfo.si_uid,
           code2str(g_siginfo.si_code));

  return 0;
}

Methode 2. Wissen mit trace-cmd (= ftrace)

Da der Kernel weiß, wer das Signal an wen gesendet hat, ist es ein Ansatz, dies herauszufinden, indem die Ereignisse des Kernels verfolgt werden. Verwenden Sie ftrace für die Kernel-Ereignisverfolgung.

Sie können die Quelle identifizieren, indem Sie das Ereignis signal_generate mit ftrace verfolgen. Sie können ftrace direkt von debugfs aus betreiben, aber hier zeigen wir Ihnen, wie Sie mit trace-cmd verfolgen.

[Starten Sie die Verfolgung]
$ sudo trace-cmd start -e signal_generate -f 'sig==9 && pid == 6985'

[Ende der Spur]
$ sudo trace-cmd stop

[Überprüfen Sie das Trace-Ergebnis]
$ sudo trace-cmd show
# tracer: nop
#
# entries-in-buffer/entries-written: 1/1   #P:8
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
     test_sender-6993  [001] d...  1660.447368: signal_generate: sig=9 errno=0 code=0 comm=test_receiver pid=6985 grp=1 res=0

Sie können sehen, dass der Prozess mit PID test_sender, test_sender, sig = 9 an den Prozess mit pid = 6985 gesendet hat.

Da das Ereignis "signal_generate" hier in großer Zahl auftritt, ist es besser, die Option "-f" wie oben beschrieben zu verwenden und entsprechend zu filtern. Oben filtern wir nach der Signalnummer und der PID, die das Signal empfängt.

Sie können die Feldnamen, die in der Filterfunktion verwendet werden können, mit dem Befehl trace-cmd list überprüfen.

$ trace-cmd list -F -e signal_generate
system: signal
name: signal_generate
ID: 194
format:
	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
	field:unsigned char common_flags;	offset:2;	size:1;	signed:0;
	field:unsigned char common_preempt_count;	offset:3;	size:1;	signed:0;
	field:int common_pid;	offset:4;	size:4;	signed:1;

	field:int sig;	offset:8;	size:4;	signed:1;
	field:int errno;	offset:12;	size:4;	signed:1;
	field:int code;	offset:16;	size:4;	signed:1;
	field:char comm[16];	offset:20;	size:16;	signed:1;
	field:pid_t pid;	offset:36;	size:4;	signed:1;
	field:int group;	offset:40;	size:4;	signed:1;
	field:int result;	offset:44;	size:4;	signed:1;

Methode 3. Wissen Sie mit System Tap

Ähnlich wie bei Methode 2 ist dies ein Ansatz zur Überprüfung, indem die Ereignisse des Kernels verfolgt werden. Verwenden Sie systemtap für die Kernel-Ereignisverfolgung.

Sie können mit einem Liner wie unten gezeigt überwachen.

$ stap -e 'probe signal.send { if (sig==9 && sig_pid==8722) printf("%s: %s(%d) -> %s(%d)\n", sig_name, execname(), pid(), pid_name, sig_pid) }'
SIGKILL: test_sender(9191) -> test_receiver(8722)

Wenn Sie ein Skript erstellen und dann Argumente verwenden, sieht es wie folgt aus. (Es scheint, dass Qiita keinen System-Tap-Modus hat, daher verwende ich den C-Sprachmodus.)

signal_sender.stp


#!/usr/bin/env stap
probe signal.send
{
  if (sig==strtol(@1,10) && sig_pid==strtol(@2,10))
    printf("%s: %s(%d) -> %s(%d)\n", sig_name, execname(), pid(), pid_name, sig_pid)
}

Systemtap kann auf einem PC so intelligent sein, Der Nachteil der Einbettung besteht darin, dass die Prozedur etwas kompliziert ist, da die Kompilierungsumgebung und die Ausführungsumgebung getrennt sind.

Zusammenfassung

Ich denke, es gibt andere, aber ich habe drei Optionen eingeführt. Eine der Möglichkeiten, nach denen ich gesucht habe, war die Verwendung von audit.log von SELinux (siehe hier, wenn Sie interessiert sind). Persönlich denke ich, dass ftrace (trace-cmd) ein gutes Gleichgewicht zwischen Benutzerfreundlichkeit und Umwelthürden aufweist. Ich bin etwas erschöpft, aber ich denke, ich kann es mit eBPF machen, deshalb möchte ich bald die eBPF-Version hinzufügen.

Referenz

Man page of SIGACTION trace-cmd(1) - Linux man page stap(1): systemtap script translator/driver - Linux man page Verwendung des Befehls trace-cmd --Qiita So verfolgen Sie die Quelle von SIGKILL in einem Prozess-Red Hat-Kundenportal (https://access.redhat.com/en/solutions/385753) takeoverjp/test_signal: signal simple test program

Recommended Posts

Signal Kennen Sie den Absender
Ich kenne den Wertfehler nicht
Ich kannte die Grundlagen von Python nicht