Programmation Système Sous Unix/Linux

Operating Systems · notes

Voir tous les documents en systèmes d'exploitation et cloud

PROGRAMMATION SYSTÈME

SOUS UNIX/LINUX

Chapitre 1 – La gestion des Processus

Najar Yousra

2020/2021 - 2 Ing

Plan de la matière

■ La gestion des processus

■ Les signaux

■ Les tubes

■ Les IPC System V

■ Les sockets

Plan du chapitre 1

■ Introduction

■ Les arguments d’un programme

■ Les processus sous UNIX

■ La terminaison d’un processus

Introduction

■ Ce cours porte sur l’utilisation des appels système des systèmes de la famille

Unix : Linux,MacOS X, AIX, LynxOS, BeOS, QNX, OpenBSD, FreeBSD,NetBSD.

■ Le rôle d’un système d’exploitation est de :

1. Gérer le matériel

2. Fournir une interfaces pour les programmes (un ensemble d’appels système)

3. Fournir une interfaces bas niveau pour l’utilisateur (un ensemble de

commande shell)

4. Eventuellement une interface utilisateur haut niveau par un environnement

graphique(kde,gnome)...

Introduction

■ Un appel système : En informatique, un appel système (en anglais, system

call, abrégé en syscall) est une fonction primitive fournie par le noyau (kernel)

d’un système d’exploitation et utilisée par les programmes s’exécutant dans

l’espace utilisateur (en d’autres termes, tous les processus distincts du

noyau). Ce système permet de contrôler de façon sécurisée les applications

dans l’espace utilisateur.

■ La norme POSIX (Portable Operating System Interface UniX) est un ensemble

de standardsde l’IEEE (Institute of Electrical and Electronics Engineers). POSIX

définit notamment :

1. Les commandes shell de base (ksh, ls, man, ...)

2. L’API (Application Programming Interface) des appels système.

Les arguments d’un programme

■ Il s’agit du passages d’arguments et variables d’environnement qui permettent à un

shell de transmettre des informations à un programme qu’il

lance. Plus

généralement, ces techniques permettent à un programme de transmettre des

informations aux programmes qu’il lance (processus fils ou descendants).

■ Pour écrire un programme C sous Unix, il suffit d’utiliser un éditeur comme vi :

$ vi nom du fichier puis écrire le code et enregistrer avec :wq

■ Pour compiler un programme C sous Linux/Unix, on utilise GCC (GNU C Compiler).

Tout d’abord on doit s’assurer qu’il est installé sinon on doit l’installer.

Publicité

■ gcc s'utilise en console : gcc [arguments] [fichiers à compiler] (-o fichier de sortie)

Exmple : $ gcc prog.c –o prog

$ prog4

Les arguments d’un programme

■ La fonction main d’un programme C doit retourner (return/exit) lorsqu'il se termine «

code de retour » entier. Ce code de retour permet au programme de faire savoir au

système si une erreur a été détectée : un retour 0 indique une sortie normale et un

retour négatif indique un retour avec erreur,

■ La fonction main d’un programme C peut prendre des arguments en ligne de

commande lors de l’appel :

Exemple : $ prog arg1 arg2 arg3

■ Pour récupérer les arguments dans le programme C, on utilise les paramètres argc et

argv dumain.

Les arguments d’un programme

■ L’entier argc donne le nombre d’arguments rentrés dans la ligne de

est un tableau de chaînes de

commande plus1, et le paramètre argv

caractères qui contient comme éléments :

1. Le premier élément argv[0] est une chaîne qui contient le nom du fichier

exécutable du programme

2. Les éléments suivants argv[1], argv[2], etc... sont des chaînes de caractères

qui contiennent les arguments passés en ligne de commande

int main (int argc ,char** argv )

Les arguments d’un programme

Les Processus sous UNIX

■ Tout le logiciel d’un ordinateur est organisé en un certain nombre de processus séquentiels. Un

processus est un programme qui s’exécute et possède des compteurs de son exécution, des

registres, des variables et une pile d’exécution. Son exécution est, en général, une alternance

de calculs effectués par le processeur et de requêtes d’Entrée/Sortie effectuées par les

périphériques.

Il existe une table des informations concernant tous les processus

créés. Il y a une entrée par processus dans la table, appelée le Bloc

de Contrôle de Processus (PCB).

Chaque entrée de la table PCB comporte des informations sur :

– Le pid du processus.

– L’état du processus.

– Son compteur ordinal (adresse de la prochaine instruction devant

être exécutée par ce processus).

– Son allocation mémoire.

– Les fichiers ouverts.

– Les valeurs contenues dans les registres du processeur.

Les Processus sous UNIX

■ Chaque processus possède un numéro de processus (PID).Sous unix, on peut voir la liste des

processus en cours d’exécution, ainsi que leur PID, parla commande ps, qui comporte

différentes options. Pour voir ses propres processus en cours d’exécution on peut utiliser le

commande

$ ps –x

Publicité

Pour voir l’ensemble des processus du système, on peut utiliser la commande

$ ps –aux

Pour voir l’ensemble des attributs des processus, on peut utiliser la commande

$ ps –f –x

La gestion des processus sous UNIX

Le standard Posix définit un nombre relativement petit d’appels système pour la gestion

de processus.

Le type pid_t( équivalent à entier long)

#include <sys/types.h>

■ pid_t fork(): Création de processus fils.

■ int execl(), int execlp(), int execvp(), int execle(), int execv(): Les services exec()

permettent à un processus d’exécuter un programme (code) différent.

■ pid_t wait(): Attendre la terminaison d’un processus.

■ void exit(): Finir l’exécution d’un processus.

■ pid_t getpid(): Retourne l’identifiant du processus.

■ pid_t getppid(): Retourne l’identifiant du père du processus.

La création d’un processus

■ Un processus peut créer un ou plusieurs processus fils qui, à leur tour, peuvent créer des

processus fils sous une forme de structure arborescente. Le processus créateur est appelé

processus père.

■ Le père continue à s’exécuter en concurrence avec ses fils. C’est ce qu’on appelle exécution

asynchrone. Un processus fils créé peut partager certaines ressources comme la mémoire ou

les fichiers avec son processus père ou avoir ses propres ressources. Le processus père peut

contrôler l’usage des ressources partagées et peut avoir une certaine autorité sur ses

processus fils. Également, il peut suspendre ou détruire ses processus fils. Il peut également

se mettre en attente de la fin de l’exécution de ses fils. L’espace d’adressage du processus fils

est obtenu par duplication de celui du père. Il peut exécuter le même programme que son père

ou charger un autre programme.

fork() est le seul moyen de créer des processus, par duplication d’un processus existant.

L’appel système fork() crée une copie exacte du processus original. Mais maintenant il se

pose un problème, car les deux processus père et fils exécutent le même code.

La création d’un processus

Comment distinguer alors le processus père du processus fils ? Pour résoudre ce problème,

on regarde la valeur de retour de fork(), qui peut être :

  • 0 pour le processus fils
  • Strictement positive pour le processus père et qui correspond au pid du processus fils
  • Négative si

mémoire ou si bien le nombre maximal de créations autorisées est atteint.

la création de processus a échoué, s’il n’y a pas suffisamment d’espace

La création d’un processus

L’appel système fork est fournit par la bibliothèque <unistd . h>

Exemple

$ gcc -o filsfils.c fils

$ Je suis le père avec pid 4130

$ Je suis le fils avec pid 4131

La création d’un processus

Exercice d’ application :

Ecrire un programme tfork.c qui crée

un processus fils. Le processus fils et père

modifient

Publicité

la même variable globale a (

initialisée à 20 le fils lui ajoute 10 et le père lui

ajoute 100)

Les appels systèmes exec

Le processus fils créé peut remplacer son code de programme par un autre programme. Le

système Unix offre une famille d’appels système exec qui permettent de changer l’image d’un

processus. Tous les appels système exec remplacent le processus courant par un nouveau

processus construit à partir d’un fichier ordinaire exécutable. Les segments de texte et de

données du processus sont remplacés par ceux du fichier exécutable.

#include <unistd.h>

■ int execl(const char path, const char argv, ...);

■ int execv(const char path, const char argv[]);

■ int execlp(const char file, const char argv, ...);

■ int execvp(const char file, const char argv[]);

Les appels systèmes exec

■ execl(): permet de passer un nombre fixé de paramètres au nouveau programme.

■ execv(): permet de passer un nombre libre de paramètres au nouveau programme.

■ exelp(): Interface et action identiques à celles d’execl(), mais la différence vient du fait que si

le nom du fichier n’est pas un nom complet — par rapport à la racine — le système utilisera

le chemin de recherche des commandes — les chemins indiqués par la variable PATH— pour

trouver dans quel répertoire se trouve le programme.

■ exevp(): Interface et action identiques à celles d’execv(), mais la différence vient du fait que

si le nom de fichier n’est pas un nom complet, la commande utilise les répertoires spécifiés

dans PATH.

Les appels systèmes exec

■ Exemple de passage de paramètres :

char *arguments[4]

...

arguments[0]="/bin/ls";

arguments[1]="-l";

arguments[2]="/etc";

arguments[3]="NULL";

execv("/bin/ls", arguments);

execl("/bin/ls", "/bin/ls " ,"-l" , "/etc" , "NULL");

execvp("ls", arguments);

execlp("ls", "ls " ,"-l" , "/etc" , "NULL");

Les appels systèmes exec

Exemple soit le programme suivant :

La fonction system

■ Il y a une façon de créer sous-processus en Unix/Linux, en utilisant la commande

system(), de la bibliothèque standard de C <stdlib.h>. Comme arguments elle reçoit

le nom de la commande : system.c

Il crée un processus fils en lançant la commande : ls -l /

Pour lancer ce programme :

gcc –o system system.c

system

Il faut retenir que system() n’est pas un appel système, mais

une fonction C. Ce qui rend l’utilisation de la fonction system()

Publicité

moins performante qu’un appel

système de création de

processus.

Les appels systèmes wait, waitpid et

exist

■ Ces appels système permettent au processus père d’attendre la fin d’un de ses

processus fils et de récupérer son statuts de fin (état de terminaison).

• void exit(int return_code ): Permet de finir volontairement l’exécution d’un processus et donne son état de

terminaison avec code return_code : ce paramètre est reçu par le père à l'aide de la primitive wait.

Wait et waitpid…..

■ wait(int *status): Permet à un processus père d’attendre jusqu’à ce qu’un processus fils

termine. Il retourne l’identifiant du processus fils et son état de terminaison dans &status s’il

n’est pas NULL.

■ waitpid(int pid, int *status): Permet à un processus père d’attendre jusqu’à ce que le

processus fils numéro pid termine. Il retourne l’identifiant du processus fils et son état

de terminaison dans &status. La valeur de pid peut être l'une des suivantes :

Wait et waitpid…

L’état de terminaison d’un processus

Le processus appelant est mis en attente jusqu’à ce que l’un de ses fils termine. Quand cela se

produit, il revient de la fonction. Si status est différent de 0, alors 16 bits d’information sont

rangés dans les 16 bits de poids faible de l’entier pointé par status. Ces informations permettent

de savoir comment s’est terminé le processus selon les conventions suivantes :

■ Si le fils est stoppé, les 8 bits de poids fort contiennent le numéro du signal qui a arrêté le

processus et les 8 bits de poids faible ont la valeur octale 0177.

■ Si le fils s’est terminé avec un exit(), les 8 bits de poids faible de status sont nuls et les 8 bits

de poids fort contiennent les 8 bits de poids faible du paramètre utilisé par le processus fils

lors de l’appel de exit().

■ Si le fils s’est terminé sur la réception d’un signal, les 8 bits de poids fort de status sont nuls

et les 7 bits de poids faible contiennent le numéro du signal qui a causé la fin du processus.

Wait et waitpid ……

■ Si status n'est pas NULL, wait() et waitpid() enregistrent les informations sur l'état dans l'entier

int sur lequel il pointe. Cet entier est analysé avec les macros suivantes (qui prennent en

argument l'entier lui-même, pas un pointeur sur lui, comme cela est fait dans wait() et

waitpid()) , exemple :

■ WIFEXITED(status) : vrai si le processus s'est terminé de façon normale.

■ WEXITSTATUS(status) : le code de sortie, c.à.d. status dans exit(status).

■ WIFSIGNALED(status): vrai si le processus s'est terminé à cause d'un signal (par exemple, un

CTRL-C)

■ WTERMSIG(status), le signal qui a provoqué la terminaison.

■ WIFSTOPPED(status) : vrai si le processus est arrêté à cause d'un signal (primitive waitpid

avec option WUNTRACED)

■ WSTOPSIG(code), le signal qui a provoqué l' arrêt.

Wait et waitpid ……

Exemple :