GNU/Linux >> Linux Esercitazione >  >> Linux

Comprensione delle chiamate di sistema su Linux con strace

Una chiamata di sistema è un modo programmatico in cui un programma richiede un servizio dal kernel e strace è un potente strumento che ti permette di tracciare il sottile strato tra i processi utente e il kernel Linux.

Per capire come funziona un sistema operativo, devi prima capire come funzionano le chiamate di sistema. Una delle funzioni principali di un sistema operativo è fornire astrazioni ai programmi utente.

Un sistema operativo può essere approssimativamente suddiviso in due modalità:

  • Modalità kernel: Una modalità privilegiata e potente utilizzata dal kernel del sistema operativo
  • Modalità utente: Dove viene eseguita la maggior parte delle applicazioni utente

Gli utenti lavorano principalmente con utilità della riga di comando e interfacce utente grafiche (GUI) per svolgere attività quotidiane. Le chiamate di sistema funzionano silenziosamente in background, interfacciandosi con il kernel per portare a termine il lavoro.

Più risorse Linux

  • Comandi Linux cheat sheet
  • Cheat sheet sui comandi avanzati di Linux
  • Corso online gratuito:Panoramica tecnica RHEL
  • Cheat sheet della rete Linux
  • Cheat sheet di SELinux
  • Cheat sheet dei comandi comuni di Linux
  • Cosa sono i container Linux?
  • I nostri ultimi articoli su Linux

Le chiamate di sistema sono molto simili alle chiamate di funzione, il che significa che accettano e lavorano su argomenti e restituiscono valori. L'unica differenza è che le chiamate di sistema entrano in un kernel, mentre le chiamate di funzione no. Il passaggio dallo spazio utente allo spazio del kernel avviene utilizzando uno speciale meccanismo trap.

La maggior parte di questo è nascosta all'utente utilizzando le librerie di sistema (ovvero glibc su sistemi Linux). Anche se le chiamate di sistema sono di natura generica, i meccanismi di emissione di una chiamata di sistema dipendono molto dalla macchina.

Questo articolo esplora alcuni esempi pratici utilizzando alcuni comandi generali e analizzando le chiamate di sistema effettuate da ciascun comando utilizzando strace . Questi esempi utilizzano Red Hat Enterprise Linux, ma i comandi dovrebbero funzionare allo stesso modo su altre distribuzioni Linux:

[root@sandbox ~]# cat /etc/redhat-release 
Red Hat Enterprise Linux Server versione 7.7 (Maipo)
[root@sandbox ~]#
[root@sandbox ~]# uname -r
3.10.0-1062.el7.x86_64
[root@sandbox ~]#

Innanzitutto, assicurati che gli strumenti richiesti siano installati sul tuo sistema. Puoi verificare se strace viene installato utilizzando il comando RPM riportato di seguito; se lo è, puoi controllare la strace numero di versione dell'utilità utilizzando il -V opzione:

[root@sandbox ~]# rpm -qa | grep -i strace
strace-4.12-9.el7.x86_64
[root@sandbox ~]#
[root@sandbox ~]# strace -V
strace -- versione 4.12
[root@sandbox ~]#

Se non funziona, installa strace eseguendo:

yum install strace 

Ai fini di questo esempio, crea una directory di test all'interno di /tmp e crea due file usando il tocco comando utilizzando:

[root@sandbox tmp ~]# cd /tmp/
[root@sandbox tmp]#
[root@sandbox tmp]# mkdir testdir
[root@sandbox tmp]#
[root@sandbox tmp]# touch testdir/file1
[root@sandbox tmp]# touch testdir/file2
[root@sandbox tmp]#

(Ho usato il /tmp directory perché tutti possono accedervi, ma puoi scegliere un'altra directory se preferisci.)

Verifica che i file siano stati creati utilizzando ls comando su testdir directory:

[root@sandbox tmp]# ls testdir/
file1  file2
[root@sandbox tmp]#

Probabilmente usi ls comando ogni giorno senza rendersi conto che le chiamate di sistema sono al lavoro sotto di esso. C'è in gioco l'astrazione; ecco come funziona questo comando:

Command-line utility -> Invokes functions from system libraries (glibc) -> Invokes system calls 

Le ls comando richiama internamente funzioni dalle librerie di sistema (ovvero glibc ) su Linux. Queste librerie richiamano le chiamate di sistema che svolgono la maggior parte del lavoro.

Se vuoi sapere quali funzioni sono state chiamate da glibc libreria, usa trace comando seguito dal normale ls testdir/ comando:

ltrace ls testdir/ 

Se traccia non è installato, installalo inserendo:

yum install ltrace 

Un mucchio di output verrà scaricato sullo schermo; non preoccuparti, segui e basta. Alcune delle importanti funzioni della libreria dall'output di ltrace i comandi rilevanti per questo esempio includono:

opendir("testdir/")                                  ={ 3 }
readdir({ 3 })                                       ={ 101879119, "." }
readdir ({3}) ={134, ".."}
readdir ({3}) ={101879120, "File1"}
strlen ("file1") =5
memccy (0x1665be0, "file1 \ 0", 6) =0x1665be0
readdir ({3}) ={101879122, "File2"}
strlen ("file2") =5
MemCPY (0x166DCB0, "File2 \ 0", 6) =0x166DCB0
readdir ({3}) =nil
Closedir ({3})

Osservando l'output sopra, probabilmente puoi capire cosa sta succedendo. Una directory chiamata testdir viene aperto dall'appendir funzione di libreria, seguita da chiamate alla readdir funzione, che sta leggendo il contenuto della directory. Alla fine, c'è una chiamata al closedir funzione, che chiude la directory aperta in precedenza. Ignora l'altro strlen e memcpy funzioni per ora.

Puoi vedere quali funzioni di libreria vengono chiamate, ma questo articolo si concentrerà sulle chiamate di sistema richiamate dalle funzioni di libreria di sistema.

Simile a quanto sopra, per capire quali chiamate di sistema vengono invocate, basta inserire strace prima di ls testdir comando, come mostrato di seguito. Ancora una volta, sul tuo schermo verrà scaricato un mucchio di parole senza senso, che puoi seguire qui:

[root@sandbox tmp]# strace ls testdir/
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) =0
brk(NULL)                               =0x1f12000
<<>>
write(1, "file1  file2\n", 13file1  file2
)          =13
Chiudi (1) =0
MunMap (0x7FD002C8D000, 4096) =0
Chiudi (2) =0
exit_group (0) =?
+++ ExitIted con 0 +++
[root@sandbox tmp]#

L'output sullo schermo dopo aver eseguito la strace comando erano semplicemente chiamate di sistema effettuate per eseguire ls comando. Ciascuna chiamata di sistema ha uno scopo specifico per il sistema operativo e può essere ampiamente classificata nelle seguenti sezioni:

  • Richiami al sistema di gestione dei processi
  • Chiamate al sistema di gestione dei file
  • Chiamate al sistema di gestione di directory e filesystem
  • Altre chiamate di sistema

Un modo più semplice per analizzare le informazioni scaricate sullo schermo è registrare l'output in un file utilizzando strace è a portata di mano -o bandiera. Aggiungi un nome file adatto dopo -o flag ed esegui di nuovo il comando:

[root@sandbox tmp]# strace -o trace.log ls testdir/
file1  file2
[root@sandbox tmp]#

Questa volta, nessun output è stato scaricato sullo schermo:i ls il comando ha funzionato come previsto mostrando i nomi dei file e registrando tutto l'output nel file trace.log . Il file ha quasi 100 righe di contenuto solo per un semplice ls comando:

[root@sandbox tmp]# ls -l trace.log 
-rw-r--r--. 1 radice root 7809 12 ottobre 13:52 trace.log
[root@sandbox tmp]#
[root@sandbox tmp]# wc -l trace.log
114 trace.log
[root@sandbox tmp]#

Dai un'occhiata alla prima riga dell'esempio trace.log:

execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0 
  • La prima parola della riga, execve , è il nome di una chiamata di sistema in esecuzione.
  • Il testo tra parentesi è l'argomento fornito alla chiamata di sistema.
  • Il numero dopo = segno (che è 0 in questo caso) è un valore restituito da execve chiamata di sistema.

L'output non sembra troppo intimidatorio ora, vero? E puoi applicare la stessa logica per comprendere altre righe.

Ora restringi la tua attenzione al singolo comando che hai invocato, ovvero ls testdir . Conosci il nome della directory utilizzato dal comando ls , quindi perché non grep per dir test all'interno del tuo trace.log file e vedi cosa ottieni? Guarda ogni riga dei risultati in dettaglio:

[root@sandbox tmp]# grep testdir trace.log
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) =0
stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) =0
openat(AT_FDCWD, "testdir/", O_RDONLY|O_NONBLOCK|O_DIRECTORY| O_CLOEXEC) =3
[root@sandbox tmp]#

Ripensando all'analisi di execve sopra, puoi dire cosa fa questa chiamata di sistema?

execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0 

Non è necessario memorizzare tutte le chiamate di sistema o quello che fanno, perché puoi fare riferimento alla documentazione quando necessario. Pagine man in soccorso! Assicurati che il pacchetto seguente sia installato prima di eseguire man comando:

[root@sandbox tmp]# rpm -qa | grep -i man-pages
man-pages-3.53-5.el7.noarch
[root@sandbox tmp]#

Ricorda che devi aggiungere un 2 tra l'uomo comando e il nome della chiamata di sistema. Se leggi uomo pagina man di utilizzando man man , puoi vedere che la sezione 2 è riservata alle chiamate di sistema. Allo stesso modo, se hai bisogno di informazioni sulle funzioni della libreria, devi aggiungere un 3 tra uomo e il nome della funzione di libreria.

Di seguito sono riportati i numeri delle sezioni del manuale ei tipi di pagine che contengono:

1. Programmi eseguibili o comandi della shell
2. Chiamate di sistema (funzioni fornite dal kernel)
3. Chiamate alle librerie (funzioni all'interno delle librerie dei programmi)
4. File speciali (di solito trovati in /dev)

Esegui il seguente uomo comando con il nome della chiamata di sistema per vedere la documentazione per quella chiamata di sistema:

man 2 execve 

Come da execve man page, questo esegue un programma che viene passato negli argomenti (in questo caso, cioè ls ). Ci sono ulteriori argomenti che possono essere forniti a ls , come testdir in questo esempio. Pertanto, questa chiamata di sistema esegue solo ls con dir test come argomento:

'execve - esegui programma'

'DESCRIPTION
       execve()  esegue  il  programma  indicato dal nome file'

La prossima chiamata di sistema, denominata stat , utilizza la testdir argomento:

stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) = 0 

Usa statistica uomo 2 per accedere alla documentazione. statistica è la chiamata di sistema che ottiene lo stato di un file:ricorda che tutto in Linux è un file, inclusa una directory.

Successivamente, openat la chiamata di sistema apre testdir. Tieni d'occhio i 3 che viene restituito. Questa è una descrizione del file, che verrà utilizzata dalle successive chiamate di sistema:

openat(AT_FDCWD, "testdir/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3 

Fin qui tutto bene. Ora apri il trace.log file e vai alla riga che segue openat chiamata di sistema. Vedrai i getdent chiamata di sistema invocata, che esegue la maggior parte di ciò che è necessario per eseguire la ls testdir comando. Ora, grep getdents dal trace.log file:

[root@sandbox tmp]# grep getdents trace.log 
getdents(3, /* 4 voci */, 32768)     =112
getdents(3, /* 0 voci */, 32768 )     =0
[root@sandbox tmp]#

I getdent la pagina man lo descrive come ottenere voci di directory , che è quello che vuoi fare. Nota che l'argomento per getdents è 3 , che è il descrittore di file da openat chiamata di sistema sopra.

Ora che hai l'elenco delle directory, hai bisogno di un modo per visualizzarlo nel tuo terminale. Quindi, grep per un'altra chiamata di sistema, scrivi , che serve per scrivere sul terminale, nei log:

[root@sandbox tmp]# grep write trace.log
write(1, "file1  file2\n", 13)          =13
[root@sandbox tmp]#

In questi argomenti puoi vedere i nomi dei file che verranno visualizzati:file1 e file2 . Per quanto riguarda il primo argomento (1 ), ricorda in Linux che, quando viene eseguito un processo, per impostazione predefinita vengono aperti tre descrittori di file. Di seguito sono riportati i descrittori di file predefiniti:

  • 0 - Input standard
  • 1 - Standard
  • 2 - Errore standard

Quindi, scrivi la chiamata di sistema sta visualizzando file1 e file2 sul display standard, che è il terminale, identificato da 1 .

Ora sai quali chiamate di sistema hanno svolto la maggior parte del lavoro per ls testdir/ comando. Ma che dire delle altre oltre 100 chiamate di sistema nel trace.log file? Il sistema operativo deve eseguire molte operazioni di pulizia per eseguire un processo, quindi gran parte di ciò che vedi nel file di registro è l'inizializzazione e la pulizia del processo. Leggi l'intero trace.log file e cerca di capire cosa sta succedendo per rendere il ls comando di lavoro.

Ora che sai come analizzare le chiamate di sistema per un determinato comando, puoi utilizzare questa conoscenza per altri comandi per capire quali chiamate di sistema vengono eseguite. traccia fornisce molti utili flag della riga di comando per semplificarti e alcuni di essi sono descritti di seguito.

Per impostazione predefinita, strace non include tutte le informazioni sulle chiamate di sistema. Tuttavia, ha un pratico -v dettagliato opzione che può fornire informazioni aggiuntive su ogni chiamata di sistema:

strace -v ls testdir 

È buona norma utilizzare sempre -f opzione durante l'esecuzione di strace comando. Consente la traccia per tracciare qualsiasi processo figlio creato dal processo attualmente tracciato:

strace -f ls testdir 

Supponi di voler solo i nomi delle chiamate di sistema, il numero di volte in cui sono state eseguite e la percentuale di tempo trascorso in ciascuna chiamata di sistema. Puoi usare -c flag per ottenere quelle statistiche:

strace -c ls testdir/ 

Supponi di volerti concentrare su una chiamata di sistema specifica, ad esempio su aperto chiamate di sistema e ignorando il resto. Puoi usare il -e flag seguito dal nome della chiamata di sistema:

[root@sandbox tmp]# strace -e open ls testdir
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) =3
open("/lib64/libselinux .so.1", O_RDONLY|O_CLOEXEC) =3
open("/lib64/libcap.so.2", O_RDONLY|O_CLOEXEC) =3
open("/lib64/libacl.so.1 ", O_RDONLY|O_CLOEXEC) =3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) =3
open("/lib64/libpcre.so.1", O_RDONLY| O_CLOEXEC) =3
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) =3
open("/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) =3
open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) =3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) =3
file1  file2
++++ terminato con 0 +++
[root@sandbox tmp]#

Cosa succede se vuoi concentrarti su più di una chiamata di sistema? Nessun problema, puoi usare lo stesso -e flag della riga di comando con una virgola tra le due chiamate di sistema. Ad esempio, per vedere la scrittura e getdent chiamate di sistema:

[root@sandbox tmp]# strace -e write,getdents ls testdir
getdents(3, /* 4 voci */, 32768)     =112
getdents(3, /* 0 voci * /, 32768)     =0
write(1, "file1  file2\n", 13file1  file2
)          =13
+++ terminato con 0 +++
[root@ sandbox tmp]#

Gli esempi finora hanno tracciato in modo esplicito i comandi di esecuzione. Ma che dire dei comandi che sono già stati eseguiti e sono in esecuzione? Cosa succede, ad esempio, se si desidera tracciare demoni che sono solo processi di lunga durata? Per questo, strace fornisce uno speciale -p flag a cui puoi fornire un ID processo.

Invece di eseguire una strace su un demone, prendi l'esempio di un gatto comando, che di solito mostra il contenuto di un file se si assegna un nome file come argomento. Se non viene fornito alcun argomento, il gatto Il comando attende semplicemente su un terminale che l'utente inserisca del testo. Una volta inserito il testo, ripete il testo specificato finché un utente non preme Ctrl+C per uscire.

Corri il gatto comando da un terminale; ti mostrerà un messaggio e aspetterà semplicemente lì (ricorda cat è ancora in esecuzione e non è terminato):

[root@sandbox tmp]# cat 

Da un altro terminale, trova l'identificatore di processo (PID) utilizzando il ps comando:

[root@sandbox ~]# ps -ef | grep cat
root      22443  20164  0 14:19 pts/0    00:00:00 cat
root      22482  20300  0 14:20 pts/1    00:00:00 grep --color=auto cat
[root@sandbox ~]#

Ora esegui strace sul processo in esecuzione con il -p flag e il PID (che hai trovato sopra utilizzando ps ). Dopo aver eseguito strace , l'output indica a cosa era collegato il processo insieme al numero PID. Ora, traccia sta tracciando le chiamate di sistema effettuate dal cat comando. La prima chiamata di sistema che vedi è read , che è in attesa di input da 0, o input standard, che è il terminale in cui il cat comando eseguito:

[root@sandbox ~]# strace -p 22443
strace:processo 22443 allegato
read(0,

Ora torna al terminale dove hai lasciato il gatto comando in esecuzione e inserire del testo. Ho inserito x0x0 a scopo dimostrativo. Nota come gatto semplicemente ripetuto quello che ho inserito; quindi, x0x0 appare due volte. Ho inserito il primo e il secondo è stato l'output ripetuto dal cat comando:

[root@sandbox tmp]# cat
x0x0
x0x0

Torna al terminale dove strace era attaccato al gatto processi. Ora vengono visualizzate due chiamate di sistema aggiuntive:la precedente lettura chiamata di sistema, che ora legge x0x0 nel terminale e un altro per scrivi , che ha scritto x0x0 di nuovo al terminale e di nuovo una nuova lettura , che è in attesa di lettura dal terminale. Tieni presente che l'input standard (0 ) e Standard fuori (1 ) sono entrambi nello stesso terminale:

[root@sandbox ~]# strace -p 22443
strace:Process 22443 allegato
read(0, "x0x0\n", 65536)                =5
write(1, " x0x0\n", 5)                   =5
read(0,

Immagina quanto sia utile quando si esegue strace contro i demoni per vedere tutto ciò che fa in background. Uccidi il gatto comando premendo Ctrl+C; questo uccide anche il tuo strace sessione poiché il processo non è più in esecuzione.

Se vuoi vedere un timestamp per tutte le tue chiamate di sistema, usa semplicemente il -t opzione con strace :

[root@sandbox ~]#strace -t ls testdir/

14:24:47 execve("/usr/bin/ls", ["ls", "testdir/"] , [/* 40 vars */]) =0
14:24:47 brk(NULL)                      =0x1f07000
14:24:47 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =0x7f2530bc8000
14:24:47 access("/etc/ld.so.preload", R_OK) =-1 ENOENT (nessun file o directory di questo tipo)
14:24:47 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) =3

E se volessi conoscere il tempo trascorso tra le chiamate di sistema? traccia ha un pratico -r comando che mostra il tempo impiegato per eseguire ciascuna chiamata di sistema. Abbastanza utile, vero?

[root@sandbox ~]#strace -r ls testdir/

0.000000 execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) =0
0.000368 brk(NULL)                 =0x1966000
0.000073 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =0x70004b<7.0/>7 access("/etc/ld.so.preload", R_OK) =-1 ENOENT (Nessun file o directory di questo tipo)
0.000119 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) =3

Conclusione

La traccia L'utilità è molto utile per comprendere le chiamate di sistema su Linux. Per ulteriori informazioni sugli altri flag della riga di comando, fare riferimento alle pagine man e alla documentazione in linea.


Linux
  1. Monitora il tuo sistema Linux nel tuo terminale con procps-ng

  2. Comando di arresto di Linux (con esempi)

  3. Pianificazione delle attività di sistema con Cron su Linux

  4. Bilanciare la sicurezza di Linux con l'usabilità

  5. Perché il mio gatto funziona con le chiamate di sistema più lentamente rispetto al gatto di Linux?

Come visualizzare le statistiche del sistema Linux con Saidar

Comando 11 Strace con esempio in Linux

Come usare il comando Linux Strace

Comprensione dei processi su Linux

Capire Crontab in Linux con esempi

Che cosa sono le chiamate di sistema Linux e le funzioni di libreria?