Strace unter Linux: Geschichte, Design und Verwendung
In Unix-ähnlichen Betriebssystemen erfolgt die Kommunikation eines Programms mit der Außenwelt und dem Betriebssystem über eine kleine Reihe von Funktionen – Systemaufrufe. Das bedeutet, dass es für Debugging-Zwecke nützlich sein kann, Systemaufrufe auszuspionieren, die von Prozessen ausgeführt werden.
Ein Dienstprogramm hilft Ihnen, das „intime Leben“ von Programmen unter Linux zu überwachen strace, das Gegenstand dieses Artikels ist. Beispiele für den Einsatz von Spionageausrüstung werden von einer kurzen Geschichte begleitet strace und eine Beschreibung des Designs solcher Programme.
Die Hauptschnittstelle zwischen Programmen und dem Betriebssystemkernel in Unix sind Systemaufrufe. Systemaufrufe, Systemaufrufe) erfolgt die Interaktion von Programmen mit der Außenwelt ausschließlich über sie.
Aber in der ersten öffentlichen Version von Unix (Version 6 Unix, 1975) gab es keine bequemen Möglichkeiten, das Verhalten von Benutzerprozessen zu verfolgen. Um dieses Problem zu beheben, wird Bell Labs auf die nächste Version aktualisieren (Version 7 Unix, 1979) schlug einen neuen Systemaufruf vor - ptrace.
Ptrace wurde hauptsächlich für interaktive Debugger entwickelt, aber Ende der 80er Jahre (im Zeitalter der kommerziellen System V-Release 4) Auf dieser Grundlage entstanden eng fokussierte Debugger – Systemaufruf-Tracer – und wurden weit verbreitet.
erste Dieselbe Version von Strace wurde 1992 von Paul Cronenburg auf der Mailingliste comp.sources.sun als Alternative zu einem geschlossenen Dienstprogramm veröffentlicht trace von Sun. Sowohl der Klon als auch das Original waren für SunOS gedacht, allerdings schon seit 1994 strace wurde auf System V, Solaris und das immer beliebter werdende Linux portiert.
Heute unterstützt Strace nur Linux und verlässt sich auf dasselbe ptrace, überwuchert mit vielen Erweiterungen.
Moderner (und sehr aktiver) Betreuer strace - Dmitry Lewin. Dank ihm erhielt das Dienstprogramm erweiterte Funktionen wie Fehlerinjektion in Systemaufrufe, Unterstützung für eine breite Palette von Architekturen und, was am wichtigsten ist, Maskottchen. Inoffizielle Quellen behaupten, dass die Wahl aufgrund der Übereinstimmung zwischen dem russischen Wort „ostrich“ und dem englischen Wort „strace“ auf den Strauß fiel.
Es ist auch wichtig, dass der Ptrace-Systemaufruf und die Tracer trotz einer langen Geschichte und Implementierung in Linux, FreeBSD, OpenBSD und traditionellem Unix nie in POSIX enthalten waren.
Das Strace-Gerät auf den Punkt gebracht: Piglet Trace
„Von Ihnen wird nicht erwartet, dass Sie das verstehen“ (Dennis Ritchie, Kommentar im Unix-Quellcode der Version 6)
Seit meiner frühen Kindheit kann ich Blackboxen nicht ausstehen: Ich habe nicht mit Spielzeug gespielt, sondern versucht, ihre Struktur zu verstehen (Erwachsene benutzten das Wort „kaputt“, glauben aber den bösen Zungen nicht). Vielleicht liegt mir deshalb die informelle Kultur des ersten Unix und der modernen Open-Source-Bewegung so nahe.
Für die Zwecke dieses Artikels ist es unzumutbar, den über Jahrzehnte gewachsenen Quellcode von Strace zu zerlegen. Aber es sollten keine Geheimnisse für die Leser übrig bleiben. Um das Funktionsprinzip solcher Strace-Programme zu zeigen, werde ich daher den Code für einen Miniatur-Tracer bereitstellen - Ferkelspur (ptr). Es weiß nicht, wie man etwas Besonderes macht, aber die Hauptsache sind die Systemaufrufe des Programms – es gibt Folgendes aus:
Piglet Trace erkennt etwa Hunderte von Linux-Systemaufrufen (siehe. Diagramm) und funktioniert nur auf x86-64-Architektur. Dies ist für Bildungszwecke ausreichend.
Schauen wir uns die Arbeit unseres Klons an. Im Fall von Linux verwenden Debugger und Tracer, wie oben erwähnt, den Systemaufruf ptrace. Es funktioniert, indem im ersten Argument die Befehlsbezeichner übergeben werden, die wir nur benötigen PTRACE_TRACEME, PTRACE_SYSCALL и PTRACE_GETREGS.
Der Tracer startet im üblichen Unix-Stil: fork(2) startet einen untergeordneten Prozess, der wiederum verwendet exec(3) startet das zu studierende Programm. Die einzige Feinheit hier ist die Herausforderung ptrace(PTRACE_TRACEME) vor exec: Der untergeordnete Prozess erwartet, dass der übergeordnete Prozess ihn überwacht:
pid_t child_pid = fork();
switch (child_pid) {
case -1:
err(EXIT_FAILURE, "fork");
case 0:
/* Child here */
/* A traced mode has to be enabled. A parent will have to wait(2) for it
* to happen. */
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
/* Replace itself with a program to be run. */
execvp(argv[1], argv + 1);
err(EXIT_FAILURE, "exec");
}
Der übergeordnete Prozess sollte nun aufgerufen werden wait(2) Stellen Sie im untergeordneten Prozess sicher, dass der Wechsel in den Trace-Modus erfolgt ist:
/* Parent */
/* First we wait for the child to set the traced mode (see
* ptrace(PTRACE_TRACEME) above) */
if (waitpid(child_pid, NULL, 0) == -1)
err(EXIT_FAILURE, "traceme -> waitpid");
An diesem Punkt sind die Vorbereitungen abgeschlossen und Sie können direkt mit der Verfolgung von Systemaufrufen in einer Endlosschleife fortfahren.
Вызов ptrace(PTRACE_SYSCALL) garantiert, dass anschließend wait Das übergeordnete Element wird entweder vor der Ausführung des Systemaufrufs oder unmittelbar nach dessen Abschluss abgeschlossen. Zwischen zwei Aufrufen können Sie beliebige Aktionen ausführen: den Aufruf durch einen alternativen ersetzen, die Argumente oder den Rückgabewert ändern.
Wir müssen den Befehl nur zweimal aufrufen ptrace(PTRACE_GETREGS)um den Registerstatus zu erhalten rax vor dem Aufruf (Systemrufnummer) und unmittelbar danach (Rückgabewert).
Eigentlich ist der Zyklus:
/* A system call tracing loop, one interation per call. */
for (;;) {
/* A non-portable structure defined for ptrace/GDB/strace usage mostly.
* It allows to conveniently dump and access register state using
* ptrace. */
struct user_regs_struct registers;
/* Enter syscall: continue execution until the next system call
* beginning. Stop right before syscall.
*
* It's possible to change the system call number, system call
* arguments, return value or even avoid executing the system call
* completely. */
if (ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL) == -1)
err(EXIT_FAILURE, "enter_syscall");
if (waitpid(child_pid, NULL, 0) == -1)
err(EXIT_FAILURE, "enter_syscall -> waitpid");
/* According to the x86-64 system call convention on Linux (see man 2
* syscall) the number identifying a syscall should be put into the rax
* general purpose register, with the rest of the arguments residing in
* other general purpose registers (rdi,rsi, rdx, r10, r8, r9). */
if (ptrace(PTRACE_GETREGS, child_pid, NULL, ®isters) == -1)
err(EXIT_FAILURE, "enter_syscall -> getregs");
/* Note how orig_rax is used here. That's because on x86-64 rax is used
* both for executing a syscall, and returning a value from it. To
* differentiate between the cases both rax and orig_rax are updated on
* syscall entry/exit, and only rax is updated on exit. */
print_syscall_enter(registers.orig_rax);
/* Exit syscall: execute of the syscall, and stop on system
* call exit.
*
* More system call tinkering possible: change the return value, record
* time it took to finish the system call, etc. */
if (ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL) == -1)
err(EXIT_FAILURE, "exit_syscall");
if (waitpid(child_pid, NULL, 0) == -1)
err(EXIT_FAILURE, "exit_syscall -> waitpid");
/* Retrieve register state again as we want to inspect system call
* return value. */
if (ptrace(PTRACE_GETREGS, child_pid, NULL, ®isters) == -1) {
/* ESRCH is returned when a child terminates using a syscall and no
* return value is possible, e.g. as a result of exit(2). */
if (errno == ESRCH) {
fprintf(stderr, "nTracee terminatedn");
break;
}
err(EXIT_FAILURE, "exit_syscall -> getregs");
}
/* Done with this system call, let the next iteration handle the next
* one */
print_syscall_exit(registers.rax);
}
Das ist der ganze Tracer. Jetzt wissen Sie, wo Sie mit der nächsten Portierung beginnen müssen DTrace unter Linux.
Grundlagen: Ausführen eines Programms, das Strace ausführt
Als erster Anwendungsfall strace, vielleicht lohnt es sich, die einfachste Methode zu nennen – das Starten einer laufenden Anwendung strace.
Um nicht in die endlose Liste der Aufrufe eines typischen Programms einzutauchen, schreiben wir Mindestprogramm vokrug write:
int main(int argc, char *argv[])
{
char str[] = "write me to stdoutn";
/* write(2) is a simple wrapper around a syscall so it should be easy to
* find in the syscall trace. */
if (sizeof(str) != write(STDOUT_FILENO, str, sizeof(str))){
perror("write");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Lassen Sie uns das Programm erstellen und sicherstellen, dass es funktioniert:
$ gcc examples/write-simple.c -o write-simple
$ ./write-simple
write me to stdout
Und zum Schluss lassen Sie es uns unter Strace-Kontrolle ausführen:
Sehr „wortreich“ und nicht sehr lehrreich. Hier gibt es zwei Probleme: Die Programmausgabe wird mit der Ausgabe vermischt strace und eine Fülle von Systemaufrufen, die uns nicht interessieren.
Sie können den Standardausgabestream des Programms und die Strace-Fehlerausgabe mithilfe des Schalters -o trennen, der die Liste der Systemaufrufe in eine Argumentdatei umleitet.
Es bleibt noch, sich mit dem Problem der „zusätzlichen“ Anrufe zu befassen. Nehmen wir an, dass wir nur an Anrufen interessiert sind write. Taste -e ermöglicht Ihnen die Angabe von Ausdrücken, nach denen Systemaufrufe gefiltert werden. Die beliebteste Bedingungsoption ist natürlich trace=*, mit dem Sie nur die Anrufe hinterlassen können, die uns interessieren.
Bei gleichzeitiger Verwendung -o и -e wir bekommen:
$ strace -e trace=write -owrite-simple.log ./write-simple
write me to stdout
$ cat write-simple.log
write(1, "write me to stdoutn", 20
) = 20
+++ exited with 0 +++
Sie sehen also, es ist viel einfacher zu lesen.
Sie können auch Systemaufrufe entfernen, beispielsweise solche im Zusammenhang mit der Speicherzuweisung und -freigabe:
$ strace -e trace=!brk,mmap,mprotect,munmap -owrite-simple.log ./write-simple
write me to stdout
$ cat write-simple.log
execve("./write-simple", ["./write-simple"], 0x7ffe9972a498 /* 69 vars */) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=124066, ...}) = 0
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "177ELF21133>1260342"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f00f0be74c0) = 0
write(1, "write me to stdoutn", 20) = 20
exit_group(0) = ?
+++ exited with 0 +++
Beachten Sie das Ausrufezeichen mit Escapezeichen in der Liste der ausgeschlossenen Aufrufe: Dies ist für die Befehlsshell erforderlich. Schale).
In meiner Version von glibc beendet ein Systemaufruf den Prozess exit_group, nicht traditionell _exit. Das ist die Schwierigkeit bei der Arbeit mit Systemaufrufen: Die Schnittstelle, mit der der Programmierer arbeitet, steht nicht in direktem Zusammenhang mit Systemaufrufen. Darüber hinaus ändert es sich regelmäßig je nach Implementierung und Plattform.
Grundlagen: Direkter Beitritt zum Prozess
Zunächst der Ptrace-Systemaufruf, auf dem es aufgebaut wurde strace, konnte nur verwendet werden, wenn das Programm in einem speziellen Modus ausgeführt wurde. Diese Einschränkung mag in den Tagen von Version 6 Unix vernünftig geklungen haben. Heutzutage reicht das nicht mehr aus: Manchmal muss man die Probleme eines funktionierenden Programms untersuchen. Ein typisches Beispiel ist ein Prozess, der an einem Griff oder im Ruhezustand blockiert ist. Daher modern strace kann Prozesse im laufenden Betrieb verbinden.
$ ./write-sleep &
[1] 15329
write me
$ strace -p 15329
strace: Process 15329 attached
pause(
^Cstrace: Process 15329 detached
<detached ...>
Programm durch Aufruf blockiert pause. Mal sehen, wie sie auf die Signale reagiert:
$ strace -o write-sleep.log -p 15329 &
strace: Process 15329 attached
$
$ kill -CONT 15329
$ cat write-sleep.log
pause() = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGCONT {si_signo=SIGCONT, si_code=SI_USER, si_pid=14989, si_uid=1001} ---
pause(
$
$ kill -TERM 15329
$ cat write-sleep.log
pause() = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGCONT {si_signo=SIGCONT, si_code=SI_USER, si_pid=14989, si_uid=1001} ---
pause() = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=14989, si_uid=1001} ---
+++ killed by SIGTERM +++
Wir haben das eingefrorene Programm gestartet und sind mit beigetreten strace. Zwei Dinge wurden deutlich: Der Pause-Systemaufruf ignoriert Signale ohne Handler und, was noch interessanter ist, Strace überwacht nicht nur Systemaufrufe, sondern auch eingehende Signale.
Beispiel: Untergeordnete Prozesse verfolgen
Arbeiten mit Prozessen über einen Anruf fork - die Basis aller Unixe. Sehen wir uns am Beispiel eines einfachen „Züchtens“ an, wie Strace mit einem Prozessbaum funktioniert. Programm:
int main(int argc, char *argv[])
{
pid_t parent_pid = getpid();
pid_t child_pid = fork();
if (child_pid == 0) {
/* A child is born! */
child_pid = getpid();
/* In the end of the day printf is just a call to write(2). */
printf("child (self=%d)n", child_pid);
exit(EXIT_SUCCESS);
}
printf("parent (self=%d, child=%d)n", parent_pid, child_pid);
wait(NULL);
exit(EXIT_SUCCESS);
}
Hier erstellt der ursprüngliche Prozess einen untergeordneten Prozess, der beide in die Standardausgabe schreibt:
Das Flag hilft Ihnen, den gesamten Prozessbaum zu verfolgen -f, mit dem strace Überwacht Systemaufrufe in untergeordneten Prozessen. Dies wird zu jeder Ausgabezeile hinzugefügt pid Prozess, der eine Systemausgabe erzeugt:
Welcher Systemaufruf wird übrigens verwendet, um einen neuen Prozess zu erstellen?
Beispiel: Dateipfade statt Handles
Die Kenntnis der Dateideskriptoren ist sicherlich nützlich, aber auch die Namen der spezifischen Dateien, auf die ein Programm zugreift, können nützlich sein.
Die nächste Programm schreibt die Zeile in die temporäre Datei:
void do_write(int out_fd)
{
char str[] = "write me to a filen";
if (sizeof(str) != write(out_fd, str, sizeof(str))){
perror("write");
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[])
{
char tmp_filename_template[] = "/tmp/output_fileXXXXXX";
int out_fd = mkstemp(tmp_filename_template);
if (out_fd == -1) {
perror("mkstemp");
exit(EXIT_FAILURE);
}
do_write(out_fd);
return EXIT_SUCCESS;
}
Während eines normalen Anrufs strace zeigt den Wert der an den Systemaufruf übergebenen Deskriptornummer an:
$ strace -e trace=write -o write-tmp-file.log ./write-tmp-file
$ cat write-tmp-file.log
write(3, "write me to a filen", 20) = 20
+++ exited with 0 +++
Mit einer Fahne -y Das Dienstprogramm zeigt den Pfad zu der Datei an, der der Deskriptor entspricht:
$ strace -y -e trace=write -o write-tmp-file.log ./write-tmp-file
$ cat write-tmp-file.log
write(3</tmp/output_fileCf5MyW>, "write me to a filen", 20) = 20
+++ exited with 0 +++
Beispiel: Dateizugriffsverfolgung
Eine weitere nützliche Funktion: Zeigt nur Systemaufrufe an, die einer bestimmten Datei zugeordnet sind. Nächste Programm hängt eine Zeile an eine beliebige Datei an, die als Argument übergeben wurde:
void do_write(int out_fd)
{
char str[] = "write me to a filen";
if (sizeof(str) != write(out_fd, str, sizeof(str))){
perror("write");
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[])
{
/*
* Path will be provided by the first program argument.
* */
const char *path = argv[1];
/*
* Open an existing file for writing in append mode.
* */
int out_fd = open(path, O_APPEND | O_WRONLY);
if (out_fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
do_write(out_fd);
return EXIT_SUCCESS;
}
Standardmäßig strace zeigt viele unnötige Informationen an. Flagge -P mit einem Argument bewirkt, dass Strace nur Aufrufe an die angegebene Datei ausgibt:
$ strace -y -P/tmp/test_file.log -o write-file.log ./write-file /tmp/test_file.log
$ cat write-file.log
openat(AT_FDCWD, "/tmp/test_file.log", O_WRONLY|O_APPEND) = 3</tmp/test_file.log>
write(3</tmp/test_file.log>, "write me to a filen", 20) = 20
+++ exited with 0 +++
Beispiel: Multithread-Programme
Dienstprogramm strace kann auch bei der Arbeit mit Multithreading hilfreich sein Programm. Das folgende Programm schreibt aus zwei Streams in die Standardausgabe:
void *thread(void *arg)
{
(void) arg;
printf("Secondary thread: workingn");
sleep(1);
printf("Secondary thread: donen");
return NULL;
}
int main(int argc, char *argv[])
{
printf("Initial thread: launching a threadn");
pthread_t thr;
if (0 != pthread_create(&thr, NULL, thread, NULL)) {
fprintf(stderr, "Initial thread: failed to create a thread");
exit(EXIT_FAILURE);
}
printf("Initial thread: joining a threadn");
if (0 != pthread_join(thr, NULL)) {
fprintf(stderr, "Initial thread: failed to join a thread");
exit(EXIT_FAILURE);
};
printf("Initial thread: done");
exit(EXIT_SUCCESS);
}
Natürlich muss es mit einer besonderen Begrüßung an den Linker kompiliert werden – dem Flag -pthread:
$ gcc examples/thread-write.c -pthread -o thread-write
$ ./thread-write
/thread-write
Initial thread: launching a thread
Initial thread: joining a thread
Secondary thread: working
Secondary thread: done
Initial thread: done
$
Flagge -ffügt wie bei regulären Prozessen die PID des Prozesses am Anfang jeder Zeile hinzu.
Dabei handelt es sich natürlich nicht um eine Thread-ID im Sinne der Implementierung des POSIX-Threads-Standards, sondern um die Nummer, die der Task-Scheduler unter Linux verwendet. Aus letzterer Sicht gibt es keine Prozesse oder Threads – es gibt Aufgaben, die auf die verfügbaren Kerne der Maschine verteilt werden müssen.
Bei der Arbeit in mehreren Threads werden die Systemaufrufe zu zahlreich:
Übrigens, Fragen. Welcher Systemaufruf wird verwendet, um einen neuen Thread zu erstellen? Wie unterscheidet sich dieser Aufruf von Threads vom Aufruf von Prozessen?
Meisterklasse: Prozessstapel zum Zeitpunkt eines Systemaufrufs
Einer der kürzlich erschienenen strace Fähigkeiten – Anzeige des Stapels von Funktionsaufrufen zum Zeitpunkt des Systemaufrufs. Einfach Beispiel:
void do_write(void)
{
char str[] = "write me to stdoutn";
if (sizeof(str) != write(STDOUT_FILENO, str, sizeof(str))){
perror("write");
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[])
{
do_write();
return EXIT_SUCCESS;
}
Natürlich wird die Programmausgabe sehr umfangreich und zusätzlich zur Flagge -k (Call-Stack-Anzeige), ist es sinnvoll, Systemaufrufe nach Namen zu filtern:
$ gcc examples/write-simple.c -o write-simple
$ strace -k -e trace=write -o write-simple.log ./write-simple
write me to stdout
$ cat write-simple.log
write(1, "write me to stdoutn", 20) = 20
> /lib/x86_64-linux-gnu/libc-2.27.so(__write+0x14) [0x110154]
> /home/vkazanov/projects-my/strace-post/write-simple(do_write+0x50) [0x78a]
> /home/vkazanov/projects-my/strace-post/write-simple(main+0x14) [0x7d1]
> /lib/x86_64-linux-gnu/libc-2.27.so(__libc_start_main+0xe7) [0x21b97]
> /home/vkazanov/projects-my/strace-post/write-simple(_start+0x2a) [0x65a]
+++ exited with 0 +++
Meisterklasse: Fehlerinjektion
Und noch eine neue und sehr nützliche Funktion: Fehlerinjektion. Hier Programmund schreibt zwei Zeilen in den Ausgabestream:
Es ist interessant, welche Fehler zurückgegeben werden alle Herausforderungen write, einschließlich des hinter perror versteckten Aufrufs. Es ist nur sinnvoll, beim ersten Aufruf einen Fehler zurückzugeben:
$ strace -e trace=write -e inject=write:error=EBADF:when=1 -owrite-twice.log ./write-twice
write: Bad file descriptor
$ cat write-twice.log
write(1, "write me 1n", 12) = -1 EBADF (Bad file descriptor) (INJECTED)
write(3, "write: Bad file descriptorn", 27) = 27
+++ exited with 1 +++
Oder der zweite:
$ strace -e trace=write -e inject=write:error=EBADF:when=2 -owrite-twice.log ./write-twice
write me 1
write: Bad file descriptor
$ cat write-twice.log
write(1, "write me 1n", 12) = 12
write(1, "write me 2n", 12) = -1 EBADF (Bad file descriptor) (INJECTED)
write(3, "write: Bad file descriptorn", 27) = 27
+++ exited with 1 +++
Die Angabe des Fehlertyps ist nicht erforderlich:
$ strace -e trace=write -e fault=write:when=1 -owrite-twice.log ./write-twice
$ cat write-twice.log
write(1, "write me 1n", 12) = -1 ENOSYS (Function not implemented) (INJECTED)
write(3, "write: Function not implementedn", 32) = 32
+++ exited with 1 +++
In Kombination mit anderen Flags können Sie den Zugriff auf eine bestimmte Datei „unterbrechen“. Beispiel:
$ strace -y -P/tmp/test_file.log -e inject=file:error=ENOENT -o write-file.log ./write-file /tmp/test_file.log
open: No such file or directory
$ cat write-file.log
openat(AT_FDCWD, "/tmp/test_file.log", O_WRONLY|O_APPEND) = -1 ENOENT (No such file or directory) (INJECTED)
+++ exited with 1 +++
Neben der Fehlerinjektion kann man Dies kann zu Verzögerungen beim Tätigen von Anrufen oder beim Empfangen von Signalen führen.
Nachwort
Dienstprogramm strace - ein einfaches und zuverlässiges Werkzeug. Aber neben Systemaufrufen können auch andere Aspekte der Funktionsweise von Programmen und des Betriebssystems debuggt werden. Es kann beispielsweise Aufrufe dynamisch verknüpfter Bibliotheken verfolgen. ltracekönnen sie einen Blick in die Funktionsweise des Betriebssystems werfen SystemTap и ftraceund ermöglicht es Ihnen, die Programmleistung eingehend zu untersuchen perf. Dennoch ist es so strace - die erste Verteidigungslinie bei Problemen mit meinen eigenen Programmen und denen anderer Leute, und ich verwende es mindestens ein paar Mal pro Woche.
Kurz gesagt, wenn Sie Unix lieben, lesen Sie man 1 strace und werfen Sie gerne einen Blick auf Ihre Programme!