$ ps ax
PID PPID S COMMAND
1 0 S /sbin/init
338 1 S /sbin/udevd --daemon
481 338 S /sbin/udevd --daemon
1200 880 S lightdm --session-child
1301 1200 S gnome-session
1528 1 S /usr/lib/gvfs/gvfsd
1532 1301 S nm-applet
1556 1301 S /usr/lib/gnome-settings
1590 1 S /usr/lib/gvfs/gvfs-gdu
1627 1 S /usr/lib/bamf/bamfdaemon
1636 1635 S /usr/bin/gtk-window
1639 1 S /usr/lib/unity/unity
1845 1 S /usr/lib/gnome-online
1853 1 R gnome-terminal
2414 1 S /usr/lib/dconf/dconf
2431 1853 S bash
2597 2431 R ps ax
Au démarrage de l'ordinateur, le processus init est lancé
ps permet d'obtenir le PID et le PPID d'un processus$ pstree -p
init(1)─┬─NetworkManager(793)─┬─dhclient(911)
│ └─dnsmasq(1012)
├─accounts-daemon(1214)───{accounts-daemon}(1215)
├─cron(855)
├─cupsd(729)───dbus(3916)
├─dconf-service(1997)─┬─{dconf-service}(1998)
│ └─{dconf-service}(2000)
├─gnome-keyring-d(1407)─┬─{gnome-keyring-d}(1408)
│ └─{gnome-keyring-d}(1970)
├─gnome-terminal(1737)─┬─bash(1746)───pstree(20545)
│ ├─gnome-pty-helpe(1745)
│ └─{gnome-terminal}(1747)
├─goa-daemon(1895)───{goa-daemon}(1911)
├─gvfs-afc-volume(1612)───{gvfs-afc-volume}(1614)
├─mission-control(1890)─┬─{mission-control}(1893)
│ └─{mission-control}(1909)
├─sh(884)───initctl(886)
└─udisks-daemon(1604)───udisks-daemon(1609)
int main(int argc, char **argv) {
pid_t pid = fork();
if (pid == -1) {
perror("fork");
} else if (pid == 0) {
for (int i=0; i<3; i++) {
printf("F%d\n", i);
}
} else {
for (int i=0; i<3; i++) {
printf("P%d ", i);
}
}
}
$ ./a.out
P0 P1 P2 F0 F1 F2
// fork bomb ! int main() { while (1) fork(); }
Pour créer un nouveau processus, on clone un processus existant à l'aide de l'appel fork()
Le système copie :
Différences entre les processus :
fork renvoie 0 dans le processus fils, et le PID du fils dans le processus pèreSYNOPSIS
#include <sys/types.h>
#include </wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
Les commandes wait et waitpid demandent à un processus d'attendre que ses processus fils changent d'état
wait permet au parent de recevoir des informations sur la terminaision du fils (code de retour entre autres)int main(int argc, char **argv) { pid_t pid; pid = fork(); if (pid == 0) { // fils sleep(10); printf("Fin fils\n"); } else { // père sleep(20); printf("Fin père\n"); } exit(1); }$ ps PID TTY STAT COMMAND 1859 pts/0 Ss bash 2570 pts/0 S ./prog 2571 pts/0 Z [prog] <defunct>
Lorsqu'un processus se termine, il n'est pas immédiatement supprimé de la table
waitinit (qui appelle wait régulièrement)int main() { int pid1; pid1 = fork(); if (pid1) { // père /* Tâche principale */ } else { // fils /* Tâche secondaire */ } }static void handler(int signo) { wait(NULL); } int main() { signal(SIGCHLD, handler); int pid1; pid1 = fork(); if (pid1) { // père /* Tâche principale */ } else { // fils /* Tâche secondaire */ } }int main() { int pid1, pid2; pid1 = fork(); if (pid1) { // père waitpid(pid1, NULL, 0); /* Tâche principale */ } else { // fils pid2 = fork(); if (pid2) { // fils exit(0); } else { // petit- fils /* Tâche secondaire */ } } }
Parfois, la tâche exécutée par le fils est longue et on ne veut pas bloquer le père en attente de complétion
init
La fonction fork est appelée par un processus mais renvoie deux résultats, dans deux processus distincts
getppid()#include <unistd.h> extern char **environ; int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ..., char * const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]);int main(int argc, char **argv) { pid_t pid; int status; pid = fork(); if (pid == 0) { execlp("ls", "ls", "-l", NULL); } else { printf("Père \n"); wait(&status); } }int main(int argc, char **argv) { pid_t pid; int status; pid = fork(); if (pid == 0) { char *args[3]; args[0] = "ls"; args[1] = "-l"; args[2] = NULL; execvp("ls", args); } else { printf("Père \n"); wait(&status); } }
La famille de fonctions exec permet de remplacer le code d'un processus par un autre
Il existe plusieurs variantes :
l (list) : les arguments sont passés un par un à la fonction, avec un pointeur nul (NULL) en dernierv (vector) : les arguments sont passés dans un unique tableaup (path) : l'exécutable est cherché dans les répertoires du chemin d'exécutione (environment) : permet de passer un tableau contenant des variables d'environnement pour l'exécution du nouveau programmeLe shell veut exécuter la commande ls
fork() pour créer le processus filsexec pour remplacer son code par celui du programme ls
L'utilisation de fork/exec est le moyen le plus classique (et parfois le seul disponible) pour créer des nouveaux processus
exec) il est inutile de copier toute sa mémoire au moment du forkexec suit fork, la mémoire n'est pas copiée