Rappel: dans ce qui suit nom(numéro) signifie entrée nom de la section de manuel numéro, consultable avec man numéro nom.
Exemple: un socket(7) est un point d'entrée du réseau (signifie: taper
man 7 socket
)
Notions de processus
Un processus est la base de toute exécution. L'espace d'adressage d'un processus est
entièrement séparé de tout autre processus (sauf éventuel partage de page copy-on-write, partage
en lecture-exécution p.ex. pour les shared objects (so, bibliothèques partagées),
options de clônage ou partage de mémoire manuel).
Un processus peut être créé par duplication puis remplacement d'exécutable (fork(2) puis exec(2)), ou par
clônage (p.ex. pour des threads dont partie de l'espace d'adressage peut être partagé) via clone(2).
Tout exécutable UNIX est projeté en mémoire (mmap(2)) et donc lu à la demande. La notion de fichier
est essentielle pour cette projection et le partage éventuel entre plusieurs processus pour les so.
Un processus contient des zones de code (lecture seule, exécutable: TEXT), de données (lecture seule, DATA)
et de variables (BSS). De plus il contient aussi une pile et un tas (croissant dans 2 direction différentes,
avec allocation automatique pour la pile, jusqu'aux limites fixées via ulimit(2) ou manuelle via brk(2) ou plus souvent malloc(3)),
ainsi que tous les fichiers projetés en mémoire (y compris les so).
Pour des raisons techniques, la zone kernel est aussi mappée dans l'adressage mais pas de manière accessible en
mode utilisateur. Le passage entre mode utilisateur et mode kernel est fait via des appels systèmes (section 2 du
manuel); bien souvent ces appels ne sont pas directs mais via des fonctions de la bibliothèque C standard (section 3
du manuel) voire même des utilitaires (sections 1 et 8 du manuel).
Les processus communiquent entre eux par différentes techniques dépendant du degré d'isolation entre eux
(IPC, mémoire partagée, fichiers, signaux, etc)
Les signaux sont des événements asynchrones générés par le système (déréférencement d'un pointeur
d'une mémoire non mappée SIGSEGV; dépassement de diverses limites configurées via ulimit(2); division
par zéro, etc -- voir signal(7) ou kill -l), et/ou par un processus. Certains signaux ont des significations
multiples standardisées, comme par exemple SIGHUP pour recharger la configuration d'un service.
Les signaux peuvent aussi être utilisés pour le temps réel ou pour une classe particulière de
services I/O asynchrones. Tous les signaux peuvent être interceptés par l'application, sauf
le signal SIGKILL.
Debugging
Interactif
Il y a plusieurs manières de debugger rapidement:
- strace permet de lancer un programme en mode traçage des appels systèmes
- ltrace permet de lancer un programme en mode traçage des appels de la bibliothèque C standard
- gdb permet de lancer l'exécutable en mode debugging, de configurer des break points, de consulter les données, etc (il existe des surcouches graphiques à gdb).
Ces outils sont basés sur le traçage générique ptrace(2) et ne fonctionnent donc pas, pour des
raisons de sécurité, avec les exécutables SUID ou SGID.
Il est aussi possible de consulter l'état d'un processus donné (sous /proc/NUMERO-DE-PROCESSUS).
Notamment /proc/PID/maps contient tous les mmap(2)ping mémoires du processus: bibliothèques partagées
(shared-object, so), fichiers projetés (mmap(2)), exécutables, heap, stack, etc.
Enfin, en plus de /proc/PID/fd (référence aux fichiers ouverts par ce processus), les commandes lsofs et
fuser permettent d'analyser plus en détail les fichiers et/ou sockets ouverts dans le système.
Exemples:
schaefer@reliant:~$ strace -e connect telnet mud.alphanet.ch 4242 > /dev/null
[ ... ]
connect(3, {sa_family=AF_INET, sin_port=htons(4242), sin_addr=inet_addr("80.83.54.2")}, 16) = 0
schaefer@reliant:~$ cd /tmp
schaefer@reliant:/tmp$ mkdir tt
schaefer@reliant:/tmp$ cd tt
schaefer@reliant:/tmp/tt$ cat > bla.c
#include <stdlib.h>
int main(int argc, char **argv) {
int a = 0;
return 1/a;
}
CTRL-D
schaefer@reliant:/tmp/tt$ gcc -Wall -g bla.c -o bla
schaefer@reliant:/tmp/tt$ ./bla
Floating point exception
schaefer@reliant:/tmp/tt$ gdb bla
(gdb) run
Starting program: /tmp/tt/bla
Program received signal SIGFPE, Arithmetic exception.
0x080483cb in main (argc=1, argv=0xbffff474) at bla.c:5
5 return 1/a;
(gdb) where
#0 0x080483cb in main (argc=1, argv=0xbffff474) at bla.c:5
(gdb) quit
schaefer@reliant:~$ cat
CTRL-Z
schaefer@reliant:~$ jobs -l
[1]+ 3632 Stopped cat
schaefer@reliant:~$ ls -l /proc/3632/fd
total 0
lrwx------ 1 schaefer schaefer 64 Nov 5 10:20 0 -> /dev/pts/0
lrwx------ 1 schaefer schaefer 64 Nov 5 10:20 1 -> /dev/pts/0
lrwx------ 1 schaefer schaefer 64 Nov 5 10:20 2 -> /dev/pts/0
schaefer@reliant:~$ fg %1
CTRL-C
Non interactif (post-mortem)
Lorsqu'une application termine anormalement (en général en ayant reçu un signal non traité n'étant pas un des signaux de maintenance, voir signal(7)), une image de la mémoire du
processus est sauvegardée dans un fichier core. Ce fichier core peut ensuite être analysé avec gdb, dans la mesure où l'exécutable et de préférence ses sources sont également
disponibles.
Il faut cependant que la génération du core soit activée via ulimit. Il y a d'autres conditions et cas particuliers décrits dans core(5). De plus, les valeurs ulimit(2) sont
configurées par processus et sont héritées. Un script de démarrage est probablement une bonne solution.
schaefer@reliant:/tmp$ cat
^\Quit
schaefer@reliant:/tmp$ ulimit -c unlimited
schaefer@reliant:/tmp$ cat
^\Quit (core dumped)
schaefer@reliant:/tmp$ gdb cat core
Core was generated by `cat'.
Program terminated with signal 3, Quit.
#0 0x00e76416 in __kernel_vsyscall ()
(gdb) where
#0 0x00e76416 in __kernel_vsyscall ()
#1 0x00745e03 in read () from /lib/tls/i686/cmov/libc.so.6
#2 0x0804ce33 in ?? ()
#3 0x0804a21d in ?? ()
#4 0x0069ebd6 in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6
#5 0x08049251 in ?? ()
(ici on a utilisé CTRL-\ pour générer SIGQUIT; on voit que les symboles ne sont
pas tous disponibles -- il y a de nombreuses autres séquences de contrôle qui
sont reconnus par les terminaux interactifs de contrôle pour envoyer des signaux divers
et variés aux processus)
Le debugging post-mortem ne fonctionne bien que si les symboles sont présents:
schaefer@reliant:/tmp$ file $(which cat)
/bin/cat: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, stripped
schaefer@reliant:/tmp$ nm $(which cat)
nm: /bin/cat: no symbols
schaefer@reliant:~$ cd /tmp/tt
schaefer@reliant:/tmp/tt$ file bla
bla: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped
schaefer@reliant:/tmp/tt$ ulimit -c unlimited
schaefer@reliant:/tmp/tt$ ls
bla bla.c
schaefer@reliant:/tmp/tt$ ./bla
Floating point exception (core dumped)
schaefer@reliant:/tmp/tt$ ls -lah core
-rw------- 1 schaefer schaefer 196K Nov 5 10:28 core
schaefer@reliant:/tmp/tt$ file core
core: ELF 32-bit LSB core file Intel 80386, version 1 (SYSV), SVR4-style, from './bla'
schaefer@reliant:/tmp/tt$ gdb bla core
Core was generated by `./bla'.
Program terminated with signal 8, Arithmetic exception.
#0 0x080483cb in main (argc=1, argv=0xbff024c4) at bla.c:5
5 return 1/a;
(gdb)
On voit ci-dessus que dans le premier cas l'exécutable a été strip(1)pé (le recompiler avec l'option -g de gcc(1) et surtout ne pas lancer strip(1) dessus). Dans le deuxième
cas c'est bon.
Debugging avancé
De nombreuses options peuvent être configurés dans la libc(7) pour debugger des problèmes comme la gestion de la mémoire, ou dans le dynamic linker (ld.so(8)). Des
outils comme matrace(1) ou valgrind(1) permettent de debugger ces problèmes plus en détail.
Journaux
Il est d'usage que les applications produisent des entrées de log (p.ex. via syslog(3)), qui sont ensuite centralisées et triées par syslogd(8) de manière locale ou centralisée.
Des outils comme logcheck(8) permettent d'analyser ces fichiers et de générer en cas de nécessité des alarmes. Enfin, logrotate(8) archive et/ou efface les anciens journaux.
On peut aussi renoncer au passage par syslogd(8) et créer des fichiers locaux, mais il faut aussi les archiver et considérer la problématique de leur accès (sécurité).
Références
--
MarcSCHAEFER - 05 Nov 2011