GNU/Linux >> Tutoriels Linux >  >> Linux

Comprendre les appels système sous Linux avec strace

Un appel système est une manière programmatique par laquelle un programme demande un service au noyau, et strace est un outil puissant qui vous permet de tracer la fine couche entre les processus utilisateur et le noyau Linux.

Pour comprendre le fonctionnement d'un système d'exploitation, vous devez d'abord comprendre comment fonctionnent les appels système. L'une des principales fonctions d'un système d'exploitation est de fournir des abstractions aux programmes utilisateur.

Un système d'exploitation peut être grossièrement divisé en deux modes :

  • Mode noyau : Un mode privilégié et puissant utilisé par le noyau du système d'exploitation
  • Mode utilisateur : Emplacement d'exécution de la plupart des applications utilisateur

Les utilisateurs travaillent principalement avec des utilitaires de ligne de commande et des interfaces utilisateur graphiques (GUI) pour effectuer des tâches quotidiennes. Les appels système fonctionnent silencieusement en arrière-plan, s'interfaçant avec le noyau pour faire le travail.

Plus de ressources Linux

  • Aide-mémoire des commandes Linux
  • Aide-mémoire des commandes Linux avancées
  • Cours en ligne gratuit :Présentation technique de RHEL
  • Aide-mémoire sur le réseau Linux
  • Aide-mémoire SELinux
  • Aide-mémoire sur les commandes courantes de Linux
  • Que sont les conteneurs Linux ?
  • Nos derniers articles Linux

Les appels système sont très similaires aux appels de fonction, ce qui signifie qu'ils acceptent et travaillent sur des arguments et des valeurs de retour. La seule différence est que les appels système entrent dans un noyau, contrairement aux appels de fonction. Le passage de l'espace utilisateur à l'espace noyau s'effectue à l'aide d'un mécanisme de déroutement spécial.

La plupart de cela est caché à l'utilisateur en utilisant des bibliothèques système (alias glibc sur les systèmes Linux). Même si les appels système sont de nature générique, les mécanismes d'émission d'un appel système dépendent beaucoup de la machine.

Cet article explore quelques exemples pratiques en utilisant des commandes générales et en analysant les appels système effectués par chaque commande à l'aide de strace . Ces exemples utilisent Red Hat Enterprise Linux, mais les commandes devraient fonctionner de la même manière sur d'autres distributions Linux :

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

Tout d'abord, assurez-vous que les outils requis sont installés sur votre système. Vous pouvez vérifier si strace est installé à l'aide de la commande RPM ci-dessous ; si c'est le cas, vous pouvez vérifier le strace numéro de version de l'utilitaire en utilisant le -V choix :

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

Si cela ne fonctionne pas, installez strace en lançant :

yum install strace

Pour les besoins de cet exemple, créez un répertoire de test dans /tmp et créez deux fichiers en utilisant le toucher commande en utilisant :

[root@sandbox ~]# 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]#

(J'ai utilisé le /tmp car tout le monde y a accès, mais vous pouvez choisir un autre répertoire si vous préférez.)

Vérifiez que les fichiers ont été créés à l'aide de ls commande sur le testdir répertoire :

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

Vous utilisez probablement le ls commande tous les jours sans se rendre compte que des appels système sont à l'œuvre en dessous. Il y a de l'abstraction en jeu ici; voici comment cette commande fonctionne :

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

Les ls la commande appelle en interne les fonctions des bibliothèques système (alias glibc ) sous Linux. Ces bibliothèques invoquent les appels système qui effectuent la majeure partie du travail.

Si vous voulez savoir quelles fonctions ont été appelées depuis la glibc bibliothèque, utilisez ltrace commande suivie de la commande habituelle ls testdir/ commande :

ltrace ls testdir/

Si ltrace n'est pas installé, installez-le en saisissant :

yum install ltrace

Un tas de sortie sera vidé à l'écran; ne vous inquiétez pas, suivez simplement. Certaines des fonctions importantes de la bibliothèque à partir de la sortie de ltrace commandes pertinentes pour cet exemple incluent :

opendir("testdir/")                                  = { 3 }
readdir({ 3 })                                       = { 101879119, "." }
readdir({ 3 })                                       = { 134, ".." }
readdir({ 3 })                                       = { 101879120, "file1" }
strlen("file1")                                      = 5
memcpy(0x1665be0, "file1\0", 6)                      = 0x1665be0
readdir({ 3 })                                       = { 101879122, "file2" }
strlen("file2")                                      = 5
memcpy(0x166dcb0, "file2\0", 6)                      = 0x166dcb0
readdir({ 3 })                                       = nil
closedir({ 3 })                      

En regardant la sortie ci-dessus, vous pouvez probablement comprendre ce qui se passe. Un répertoire appelé testdir est ouvert par opendir fonction de bibliothèque, suivie d'appels au readdir fonction, qui lit le contenu du répertoire. A la fin, il y a un appel au closedir fonction, qui ferme le répertoire qui a été ouvert précédemment. Ignorer les autres strlen et memcpy fonctions pour le moment.

Vous pouvez voir quelles fonctions de bibliothèque sont appelées, mais cet article se concentrera sur les appels système qui sont invoqués par les fonctions de bibliothèque système.

Semblable à ce qui précède, pour comprendre quels appels système sont invoqués, mettez simplement strace avant le ls testdir commande, comme indiqué ci-dessous. Une fois de plus, un tas de charabia sera déversé sur votre écran, que vous pouvez suivre ici :

[root@sandbox tmp]# strace ls testdir/
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
brk(NULL)                               = 0x1f12000
<<< truncated strace output >>>
write(1, "file1  file2\n", 13file1  file2
)          = 13
close(1)                                = 0
munmap(0x7fd002c8d000, 4096)            = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++
[root@sandbox tmp]#

La sortie à l'écran après l'exécution de la strace la commande était simplement des appels système effectués pour exécuter le ls commande. Chaque appel système sert un objectif spécifique pour le système d'exploitation, et ils peuvent être globalement classés dans les sections suivantes :

  • Appels système de gestion de processus
  • Appels du système de gestion de fichiers
  • Appels système de gestion des répertoires et des systèmes de fichiers
  • Autres appels système

Un moyen plus simple d'analyser les informations affichées sur votre écran consiste à enregistrer la sortie dans un fichier à l'aide de strace c'est pratique -o drapeau. Ajoutez un nom de fichier approprié après le -o flag et relancez la commande :

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

Cette fois, aucune sortie n'est projetée à l'écran :le ls la commande a fonctionné comme prévu en affichant les noms de fichiers et en enregistrant toute la sortie dans le fichier trace.log . Le fichier contient près de 100 lignes de contenu juste pour un simple ls commande :

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

Examinez la première ligne du fichier trace.log de l'exemple :

execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
  • Le premier mot de la ligne, execve , est le nom d'un appel système en cours d'exécution.
  • Le texte entre parenthèses correspond aux arguments fournis à l'appel système.
  • Le nombre après le = signe (qui est 0 dans ce cas) est une valeur renvoyée par execve appel système.

La sortie ne semble pas trop intimidante maintenant, n'est-ce pas ? Et vous pouvez appliquer la même logique pour comprendre d'autres lignes.

Maintenant, concentrez-vous sur la seule commande que vous avez invoquée, c'est-à-dire ls testdir . Vous connaissez le nom du répertoire utilisé par la commande ls , alors pourquoi ne pas grep pour testdir dans votre trace.log déposer et voir ce que vous obtenez? Regardez chaque ligne des résultats en détail :

[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]#

En repensant à l'analyse de execve ci-dessus, pouvez-vous dire ce que fait cet appel système ?

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

Vous n'avez pas besoin de mémoriser tous les appels système ou ce qu'ils font, car vous pouvez vous référer à la documentation quand vous en avez besoin. Les pages d'homme à la rescousse ! Assurez-vous que le package suivant est installé avant d'exécuter le man commande :

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

N'oubliez pas que vous devez ajouter un 2 entre l'homme commande et le nom de l'appel système. Si vous lisez homme la page de manuel de utilisant man man , vous pouvez voir que la section 2 est réservée aux appels système. De même, si vous avez besoin d'informations sur les fonctions de la bibliothèque, vous devez ajouter un 3 entre homme et le nom de la fonction de bibliothèque.

Voici les numéros de section du manuel et les types de pages qu'ils contiennent :

1. Executable programs or shell commands
2. System calls (functions provided by the kernel)
3. Library calls (functions within program libraries)
4. Special files (usually found in /dev)

Exécutez l'homme suivant commande avec le nom de l'appel système pour voir la documentation de cet appel système :

man 2 execve

Conformément à l'exécutif man page, cela exécute un programme qui est passé dans les arguments (dans ce cas, c'est ls ). Des arguments supplémentaires peuvent être fournis à ls , comme testdir dans cet exemple. Par conséquent, cet appel système exécute simplement ls avec testdir comme argument :

'execve - execute program'

'DESCRIPTION
       execve()  executes  the  program  pointed to by filename'

L'appel système suivant, nommé stat , utilise le testdir argument :

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

Utiliser la stat de l'homme 2 pour accéder à la documentation. statistiques est l'appel système qui obtient le statut d'un fichier - rappelez-vous que tout sous Linux est un fichier, y compris un répertoire.

Ensuite, l'openat l'appel système ouvre testdir. Gardez un œil sur les 3 qui est retourné. Il s'agit d'une description de fichier, qui sera utilisée par des appels système ultérieurs :

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

Jusqu'ici tout va bien. Maintenant, ouvrez le trace.log fichier et allez à la ligne suivant le openat appel système. Vous verrez les getdents appel système invoqué, qui fait la plupart de ce qui est nécessaire pour exécuter le ls testdir commande. Maintenant, grep getdents à partir du trace.log fichier :

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

Les getdents la page de manuel le décrit comme obtenir des entrées de répertoire , c'est ce que tu veux faire. Notez que l'argument pour getdents est 3 , qui est le descripteur de fichier de l'openat appel système ci-dessus.

Maintenant que vous avez la liste des répertoires, vous avez besoin d'un moyen de l'afficher dans votre terminal. Alors, grep pour un autre appel système, écrire , qui sert à écrire sur le terminal, dans les logs :

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

Dans ces arguments, vous pouvez voir les noms de fichiers qui seront affichés :fichier1 et fichier2 . Concernant le premier argument (1 ), rappelez-vous sous Linux que, lorsqu'un processus est exécuté, trois descripteurs de fichiers sont ouverts par défaut. Voici les descripteurs de fichier par défaut :

  • 0 - Saisie standard
  • 1 - Écart standard
  • 2 - Erreur standard

Ainsi, le écrire l'appel système affiche file1 et fichier2 sur l'afficheur standard, qui est le terminal, identifié par 1 .

Vous savez maintenant quels appels système ont fait le plus de travail pour ls testdir/ commande. Mais qu'en est-il des 100 autres appels système dans le trace.log ? dossier? Le système d'exploitation doit faire beaucoup de ménage pour exécuter un processus, donc une grande partie de ce que vous voyez dans le fichier journal est l'initialisation et le nettoyage du processus. Lire l'intégralité de trace.log fichier et essayez de comprendre ce qui se passe pour faire le ls travail de commande.

Maintenant que vous savez comment analyser les appels système pour une commande donnée, vous pouvez utiliser ces connaissances pour d'autres commandes afin de comprendre quels appels système sont exécutés. trace fournit de nombreux indicateurs de ligne de commande utiles pour vous faciliter la tâche, et certains d'entre eux sont décrits ci-dessous.

Par défaut, strace n'inclut pas toutes les informations d'appel système. Cependant, il a un -v verbose pratique option qui peut fournir des informations supplémentaires sur chaque appel système :

strace -v ls testdir

Il est recommandé de toujours utiliser le -f option lors de l'exécution de la strace commande. Il permet le traçage pour tracer tous les processus enfants créés par le processus en cours de trace :

strace -f ls testdir

Supposons que vous souhaitiez uniquement les noms des appels système, le nombre de fois qu'ils ont été exécutés et le pourcentage de temps passé sur chaque appel système. Vous pouvez utiliser le -c flag pour obtenir ces statistiques :

strace -c ls testdir/

Supposons que vous souhaitiez vous concentrer sur un appel système spécifique, par exemple sur open appels système et en ignorant le reste. Vous pouvez utiliser le -e drapeau suivi du nom de l'appel système :

[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
+++ exited with 0 +++
[root@sandbox tmp]#

Et si vous voulez vous concentrer sur plus d'un appel système ? Pas de soucis, vous pouvez utiliser le même -e indicateur de ligne de commande avec une virgule entre les deux appels système. Par exemple, pour voir le écrire et getdents appels système :

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

Jusqu'à présent, les exemples ont tracé explicitement les commandes d'exécution. Mais qu'en est-il des commandes déjà exécutées et en cours d'exécution ? Que se passe-t-il, par exemple, si vous souhaitez tracer des démons qui ne sont que des processus de longue durée ? Pour cela, strace fournit un -p spécial drapeau auquel vous pouvez fournir un ID de processus.

Au lieu d'exécuter un strace sur un démon, prenons l'exemple d'un chat commande, qui affiche généralement le contenu d'un fichier si vous donnez un nom de fichier comme argument. Si aucun argument n'est donné, le chat La commande attend simplement sur un terminal que l'utilisateur saisisse du texte. Une fois le texte saisi, il répète le texte donné jusqu'à ce qu'un utilisateur appuie sur Ctrl+C pour quitter.

Exécutez le chat commande depuis un terminal ; il vous montrera une invite et attendra simplement là (rappelez-vous cat est toujours en cours d'exécution et n'a pas quitté) :

[root@sandbox tmp]# cat

Depuis un autre terminal, trouvez l'identifiant de processus (PID) en utilisant le ps commande :

[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 ~]#

Maintenant, exécutez strace sur le processus en cours avec le -p flag et le PID (que vous avez trouvé ci-dessus en utilisant ps ). Après avoir exécuté strace , la sortie indique à quoi le processus était attaché avec le numéro PID. Maintenant, strace trace les appels système effectués par le chat commande. Le premier appel système que vous voyez est read , qui attend l'entrée de 0, ou l'entrée standard, qui est le terminal où le cat commande exécutée :

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

Maintenant, revenez au terminal où vous avez laissé le chat commande en cours d'exécution et saisissez du texte. J'ai entré x0x0 à des fins de démonstration. Remarquez comment chat simplement répété ce que j'ai entré; donc, x0x0 apparaît deux fois. J'ai entré le premier, et le second était la sortie répétée par le cat commande :

[root@sandbox tmp]# cat
x0x0
x0x0

Revenez au terminal où strace était attaché au chat processus. Vous voyez maintenant deux appels système supplémentaires :le précédent read appel système, qui lit maintenant x0x0 dans le terminal, et un autre pour écrire , qui écrivait x0x0 retour au terminal, et encore une nouvelle lecture , qui attend de lire depuis le terminal. Notez que l'entrée standard (0 ) et sortie standard (1 ) sont tous les deux dans le même terminal :

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

Imaginez à quel point cela est utile lors de l'exécution de strace contre les démons pour voir tout ce qu'il fait en arrière-plan. Tuez le chat commande en appuyant sur Ctrl+C ; cela tue également votre strace session puisque le processus n'est plus en cours d'exécution.

Si vous voulez voir un horodatage pour tous vos appels système, utilisez simplement le -t option avec 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 (No such file or directory)
14:24:47 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3

Et si vous voulez connaître le temps passé entre les appels système ? trace a un -r pratique commande qui affiche le temps passé à exécuter chaque appel système. Assez utile, n'est-ce pas ?

[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) = 0x7fb6b1155000
0.000047 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
0.000119 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3

Conclusion

La trace est très pratique pour comprendre les appels système sous Linux. Pour en savoir plus sur ses autres indicateurs de ligne de commande, veuillez consulter les pages de manuel et la documentation en ligne.


Linux
  1. Surveillez votre système Linux dans votre terminal avec procps-ng

  2. Planification des tâches système avec Cron sous Linux

  3. Équilibrer la sécurité Linux avec la convivialité

  4. Howto :Programmation en C avec des répertoires sous Linux

  5. Pourquoi mon chat fonctionne-t-il plus lentement avec les appels système que le chat de Linux ?

Comment afficher les statistiques du système Linux avec Saidar

11 Commande Strace avec exemple sous Linux

Comment utiliser la commande Linux Strace

Comprendre les processus sous Linux

Comprendre Crontab sous Linux avec des exemples

Qu'est-ce que les appels système Linux et les fonctions de bibliothèque ?