Minimalny możliwy do uruchomienia przykład
Jeśli pojęcie nie jest jasne, istnieje prostszy przykład, którego nie widziałeś, który to wyjaśnia.
W tym przypadku przykładem jest niezależny zestaw Linux x86_64 (bez libc) hello world:
przywitania
.text
.global _start
_start:
/* write */
mov $1, %rax /* syscall number */
mov $1, %rdi /* stdout */
mov $msg, %rsi /* buffer */
mov $len, %rdx /* buffer len */
syscall
/* exit */
mov $60, %rax /* exit status */
mov $0, %rdi /* syscall number */
syscall
msg:
.ascii "hello\n"
len = . - msg
GitHub w górę .
Złóż i uruchom:
as -o hello.o hello.S
ld -o hello.out hello.o
./hello.out
Wyprowadza oczekiwane:
hello
Teraz użyjmy strace na tym przykładzie:
env -i ASDF=qwer strace -o strace.log -s999 -v ./hello.out arg0 arg1
cat strace.log
Używamy:
strace.log teraz zawiera:
execve("./hello.out", ["./hello.out", "arg0", "arg1"], ["ASDF=qwer"]) = 0
write(1, "hello\n", 6) = 6
exit(0) = ?
+++ exited with 0 +++
Przy tak minimalnym przykładzie każdy znak wyjściowy jest oczywisty:
execvewiersz: pokazuje sposób stracewykonania hello.out, w tym argumenty CLI i środowisko, zgodnie z dokumentacją wman execve
writelinia: pokazuje wykonane przez nas wywołanie systemowe zapisu. 6jest długością łańcucha "hello\n".
= 6jest zwracaną wartością wywołania systemowego, która zgodnie z dokumentacją man 2 writejest liczbą zapisanych bajtów.
exitlinia: pokazuje wykonane przez nas wywołanie systemowe wyjścia. Nie ma wartości zwracanej, ponieważ program został zamknięty!
Bardziej złożone przykłady
Zastosowanie strace ma oczywiście na celu sprawdzenie, które wywołania systemowe faktycznie wykonują złożone programy, aby pomóc w debugowaniu / optymalizacji programu.
Warto zauważyć, że większość wywołań systemowych, które możesz napotkać w Linuksie, ma opakowania glibc, wiele z nich z POSIX .
Wewnętrznie owijki glibc używają mniej więcej takiego wbudowania: Jak wywołać wywołanie systemowe za pomocą sysenter w zestawie wbudowanym?
Następnym przykładem, który powinieneś przestudiować, jest POSIX writehello world:
main.c
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *msg = "hello\n";
write(1, msg, 6);
return 0;
}
Skompiluj i uruchom:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
Tym razem zobaczysz, że glibc wykonuje kilka wywołań systemowych, mainaby skonfigurować ładne środowisko dla main.
Wynika to z faktu, że nie używamy teraz programu wolnostojącego, ale bardziej popularnego programu glibc, który umożliwia funkcjonalność libc.
Następnie na każdym końcu strace.logzawiera:
write(1, "hello\n", 6) = 6
exit_group(0) = ?
+++ exited with 0 +++
Dochodzimy zatem do wniosku, że writefunkcja POSIX używa, niespodzianka ! write, wywołania systemowego Linux .
Obserwujemy również, że return 0prowadzi to do exit_grouppołączenia zamiast exit. Ha, nie wiedziałem o tym! Właśnie dlatego stracejest taki fajny. man exit_groupnastępnie wyjaśnia:
To wywołanie systemowe jest równoważne z exit (2), z tym wyjątkiem, że kończy nie tylko wątek wywołujący, ale wszystkie wątki w grupie wątków procesu wywołującego.
A oto kolejny przykład, w którym badałem, które dlopenwykorzystuje wywołanie systemowe : /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
Testowane w Ubuntu 16.04, GCC 6.4.0, jądro Linuksa 4.4.0.