<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="generator" content="pandoc">
<meta name="author" content="Guillaume Chanel">
<title>Processus et virtualisation</title>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui">
<link rel="stylesheet" href="../../../dist/reset.css">
<link rel="stylesheet" href="../../../dist/reveal.css">
<link rel="stylesheet" href="../../../dist/theme/white.css" id="theme">
<!-- Higlight theme -->
<link rel="stylesheet" href="../css/googlecode.css" id="highlight-theme">
<!-- Add my own theme on top of classical reveal.js theme -->
<link rel="stylesheet" href="../css/mytheme.css">
<!-- Printing and PDF exports -->
<script>
var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match(/print-pdf/gi) ? '../../../css/print/pdf.scss' : '../../../css/print/paper.scss';
document.getElementsByTagName('head')[0].appendChild(link);
</script>
<!--[if lt IE 9]>
<script src="../../../lib/js/html5shiv.js"></script>
<![endif]-->
<script type="text/javascript">
function add_text_and_highlight(response, id) {
response.text().then(function(text) {
element = document.getElementById(id);
element.textContent = text;
var event = new Event('focusout');;
element.dispatchEvent(event);
});
}
window.onload=function() {
fetch('examples/espaceAddr.c').then(function(response) {
add_text_and_highlight(response, 'ex-espaceAddr');
});
fetch('examples/execAll.c').then(function(response) {
add_text_and_highlight(response, 'ex-execAll');
});
}
</script>
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<section>
<h1 class="title">Processus et virtualisation</h1>
<p style="text-align: center;" class="author">Guillaume Chanel</p>
</section>
<section>
<h2>Brain-storming</h2>
<p>Pour vous qu’est-ce qu’un processus?</p>
<p>Quelles informations contient-il?</p>
<p>Où réside ces informations en mémoire?</p>
<p>Que permet les systèmes multi-processus ?</p>
</section>
<section>
<h2>Le processus en bref</h2>
<p>Un processus représente l’exécution courante d’un programme. Il contient donc toutes les
informations nécessaire à l’exécution du programme.</p>
<img src="img/exe_to_proc.svg" alt="De l'executable au processus" height="400px"
style="padding: 10px;background-color: whitesmoke;">
</section>
</section>
<section>
<section>
<h1>Mémoire virtuelle</h1>
</section>
<section>
<h2>Espace d’adressage du processus (mémoire virtuelle)</h2>
<img src="img/virtual_mem.svg" alt="Espace d'adressage" height="450px"
style="padding: 10px;">
</section>
<section>
<h2>Exercice</h2>
<p>Créer un programme en C qui:</p>
<ul>
<li>déclare des variables globales</li>
<li>déclare des variables locales (e.g. dans une fonction)</li>
<li>utilise la fonction malloc pour allouer de la mémoire</li>
<li>appelle une fonction autre que la fonction main</li>
<li>affiche TOUTES les adresses des objects déclarés ci-dessus (y compris les fonctions et les adresses des pointeurs)</li>
<li>attend une entrée utilisateur ou se met en pause</li>
</ul>
</section>
<section>
<h2>Correction</h2>
<pre style="height: 500px; font-size: 13px;"><code id='ex-espaceAddr' ></code></pre>
<a href="examples/espaceAddr.c"> Click here to download</a>
</section>
<section>
<h2>Exercice</h2>
<p>En utilisant la commande <code>pmap</code>:</p>
<ul>
<li>observer les différents segments du processus</li>
<li>comparer les adresses des segments avec les adresses des variables de votre programme</li>
<li>confirmer la bonne répartition des données dans les segments</li>
</ul>
</section>
<section>
<h2>Objectif de la mémoire virtuelle</h2>
<p>Grâce la mémoire virtuelle on va pouvoir:</p>
<ul>
<li>définir un espace d’adressage indépendant pour chaque processus;</li>
<li>adresser plus de mémoire que la mémoire physique disponible;</li>
<li>partager facilement des zones de mémoire entre processus;</li>
<li>adresser le contenu de fichiers comme s’il étaient en mémoire.</li>
</ul>
</section>
<section>
<h2>Virtualisation de la mémoire</h2>
<p style="font-size: 0.8em">L’espace d’adressage est divisé en pages (en général de 4Ko). Une page virtuelle peut être associée à une page de mémoire vive (page valide) ou morte (page invalide).</p>
<img src="img/virtualization.svg" alt="Virtualisation" height="450px"
style="padding: 10px;">
</section>
<section>
<h3>Conversion adr. virtuelle -> adr. physique</h3>
<p>Elle est réalisée par le matériel (Memory Management Unit - MMU):</p>
<img src="img/convert.svg" alt="Convertion">
</section>
<section>
<h2>Table des pages</h2>
<div class="text-block">
<p>Une table des pages existe pour chaque processus.</p>
<p>Chaque table est maintenue par le système (i.e. Linux, MacOSX, Windows, etc…) et utilisée par le MMU.</p>
<p>Quelques informations généralement contenues dans une entrée de la table:</p>
<ul>
<li>numéro de page physique;</li>
<li>taille d’une page;</li>
<li>permissions d’accès;</li>
<li>bit «page valide» ou «page présente en RAM»;</li>
<li>bit «page sale» (i.e. modifiée depuis sa dernière présence sur disque);</li>
<li>…</li>
</ul>
</div>
<aside class="notes">
<p>Si la page est invalide le processeur le communique à l’OS -> page fault</p>
</aside>
</section>
<section>
<h2>Défaut de page</h2>
<div class="text-block">
<p>Un défaut de page arrive lorsque le MMU ne peut pas satisfaire une demande de page car elle n’est pas
référencée dans la table du processus (bit «page valide» = false).</p>
<p>Il y a alors 3 cas possibles:</p>
<ul>
<li>l’<strong>accès mémoire est illégal</strong> -> le noyaux termine le processus en
«segmentation fault» (SIGSEG);</li>
<li>La page est présente en mémoire physique , c’est un <strong>défaut de page mineur</strong>
-> il suffit de mettre à jour la table du processus pour la faire pointée sur la page en mémoire
physique;</li>
<li>La page n’est pas présente en mémoire physique, c’est un <strong>défaut de page majeur
</strong>.</li>
</ul>
</div>
<aside class="notes">
<p>Demander pour le point 2 quand est-ce que ca arrive ? -> cela arrive dans le cas ou une page est partagée avec un autre processus mais n’était pas encore utilisée par le processus courant.</p>
<p>Faire un graphe sur le coté pour expliquer cette page.</p>
</aside>
</section>
<section>
<h2>Défaut de page majeur</h2>
<div class="text-block" style="font-size: 0.9em;">
<p>Pour un <strong>défaut de page majeur </strong>il faut charger la page manquante:</p>
<ul>
<li>on sauvegarde l’état du processus et on le mets «en attente»;</li>
<li>si il n’y a pas de place en mémoire physique on libère une page peu utilisée;</li>
<li>on charge la page manquante en mémoire depuis le disque;</li>
<li>on mets à jour la table des pages du processus;</li>
<li>on charge l’état du processus et on repart de l’instruction ayant provoquée la faute de page (cette fois
satisfaite).</li>
</ul>
<p>A noter que lorsqu’une page est libérée en mémoire physique soit:</p>
<ul>
<li>elle existe déjà sur le disque car elle n’a pas été modifié (i.e. bit «page sale» = 0), dans ce cas il
suffit de remplacer cette page physique par la nouvelle</li>
<li>elle à été modifiée et est mise en swap pour conserver les modifications.</li>
</ul>
</div>
<aside class="notes">
<p>Faire un graphe sur le côté pour expliquer ce slide</p>
</aside>
</section>
<section>
<h2>Exercice</h2>
<p>En utilisant la commande <code>pmap -X</code> sur le processus précédent, observer et expliquer les champs Size, RSS, PSS et Swap</p>
<br>
<p>Comment ces champs evoluent-t-ils lorsque plusieurs processus identiques sont lancés ?</p>
<br>
<p>Que faudrait-il faire pour que la champ Swap commence à augmenter ?</p>
<br>
<p>Pourquoi dans certain cas Size est différent de RSS mais Swap vaut 0 ?</p>
<aside class="notes">
<p>Les champs de pmap -X</p>
<ul>
<li>Size=Taille du segment</li>
<li>RSS=Taille du segment en mémoire (i.e. pas toutes ne sont forcément chargées)</li>
<li>PSS=Taille des pages privées + (taille des pages partagées / N), N étant le nombre de processus partagant ces pages</li>
</ul>
<p>RSS reste identique, PSS va diminuer (c.f. formule ci-dessus)</p>
<p>Allouer enormément de mémoire. Dans ce cas RSS va diminuer</p>
<p>C'est dans le cas ou toutes les pages du segment ne sont pas encore chargée en mémoire</p>
</aside>
</section>
</section>
<section>
<section>
<h1>Processus</h1>
</section>
<section>
<h2>Structure d'un processus</h2>
<div class="text-block">
<p>Un processus est identifié grâce à son PID (Process ID). Il est unique pour chaque processus mais un PID libéré peut être réutilisé.</p>
<p style="margin-bottom: 2px;">Chaque processus est décrit par son contexte:</p>
<ul>
<li>l’état du processeur qui l’exécute:</li>
<ul>
<li>les registres accessibles au programme;</li>
<li>l’instruction courante (compteur ordinal);</li>
<li>les informations de pagination (tables des pages...);</li>
</ul>
<li>son espace mémoire virtuel -> les données et le programme;</li>
<li>les ressources dont il dispose;</li>
<li>des informations administratives:</li>
<ul>
<li>PID, utilisateur(s), Session ID, Groupe ID;</li>
<li>priorités (statique et dynamique);</li>
<li>consommation de ressources.</li>
</ul>
</ul>
</div>
</section>
<section>
<h2>Structure d'un processus</h2>
<div class="text-block">
<p>Dans le noyaux Linux (3.7.10) un processus est définit par la structure <strong>task_struct</strong> (/usr/src/linux/include/linux/sched.h).</p>
<pre><code class="c" data-trim data-noescape style="font-size: 0.8em;">
struct task_struct {
/* ... */
/* PID du processus */
pid_t pid;
/* Description de la mémoire virtuelle + table de page */
struct mm_struct *mm;
/* Etat du CPU / registres (specifique à la platforme) */
struct thread_struct thread;
/* Information sur l'ordonnancement du processus */
struct sched_info sched_info;
/* Contient notament la table des descripteur de fichier ainsi
qu'une liste des descripteurs "close on-exec" */
struct files_struct *files;
/* ... (+ de 350 lignes au total) */
};
/* Dans /usr/src/linux/include/linux/mm_types.h */
struct mm_struct {
/* ... */
unsigned long start_code, end_code, start_data, end_data; /* segments de code / données */
unsigned long start_brk, brk, start_stack; /* segment du tas et de la pile */
/* ... */
}
</code></pre>
</div>
</section>
<section>
<h2>Création de processus</h2>
<div class="text-block" style="font-size: 0.8em;">
<p><strong>Lors du démarrage du système, le processus systemd (ou init) est créé par le
noyau. Il est donc le premier processus et porte le PID 1.</strong></p>
<p><strong>Tous les autres processus sont créés par un appel à la fonction fork.</strong> Chaque
processus a donc un parent (excepté systemd, c.f. commande <code>pstree</code>).</p>
<pre><code class="c" data-trim data-noescape>
#include <unistd.h>
#include <sys/types.h>
pid_t fork(void); // Crée un nouveau processus enfant
pid_t getpid(void); // retourne le PID du processus
pid_t getppid(void); // retourne le PID du parent
</code></pre>
<p>Cette fonction crée un nouveau processus qui est une <strong>réplique du processus
parent</strong> (e.g. copie de la table des pages, état du processeur, descripteurs de fichier, etc...), et va
continuer son exécution à partir du fork.</p>
<p><strong>La fonction fork retourne 0 pour le processus enfant, le PID de l’enfant dans
le processus parent, -1 en cas d’erreur.</strong></p>
</div>
</section>
<section>
<h2>Création de processus</h2>
<div class="text-block" style="font-size: 0.8em;">
<p>L’implémentation d’un fork peut donc ce faire de la manière suivante:</p>
<pre><code class="c" data-trim data-noescape>
#include <unistd.h>
pid_t pid = fork()
if(pid > 0) {
// Code du parent
}
else if(pid == 0){
// Code de l’enfant
}
else // Error
</code></pre>
<p>Le processus enfant n’est <strong>pas une réplique exacte</strong> du parent (see man fork),
notamment:</p>
<ul>
<li>l’enfant a son propre PID et son PPID est égale au PID du parent;</li>
<li>pas d’héritage des verrous mémoire et fichiers (mlock, flock).</li>
</ul>
</div>
</section>
<section>
<h2>Fork et descripteurs de fichiers</h2>
<p>La table des descripteurs de fichier est copiée</p>
<p>En conséquence, que ce passe-t-il si le processus parent écrit sur le même descripteur que le processus enfant ?</p>
</section>
<section>
<h2>Fork et mémoire virtuelle</h2>
<p style="text-align: left; margin-bottom: 0px;">Le nouveau processus va donc partager des pages avec son processus parent.</p>
<img src="img/fork.svg" alt="Création de processus et mémoire virtuelle" height="480px">
</section>
<section>
<h2>Copy on write</h2>
<p style="text-align: left; margin-bottom: 0px;">Ces pages seront copiées uniquement lors de modifications de la mémoire.
C’est ce que l’on appelle le <strong>«copy on write»</strong>.</p>
<img src="img/copy-on-write.svg" alt="Mécanisme de copy on write" height="480px">
</section>
<section>
<h2>Terminaison de processus</h2>
<div class="text-block" style="font-size: 0.85em;">
<p style="margin-bottom: 5px;">La fonction exit permet de terminer un processus à tout moment:</p>
<pre><code>exit(int status);</code></pre>
<p style="margin-bottom: 0px;">Il existe deux constantes souvent utilisées EXIT_SUCCESS et EXIT_FAILURE.</p>
<p>Avant de terminer le processus la fonction exit:</p>
<ul>
<li>ferme les descripteurs de fichiers ouverts (inclus STDIN, STDOUT, STDERR);</li>
<li>envoi le signal SIGCHLD au parent pour l’informer de la mort de l’enfant;</li>
<li>tous les enfants du processus deviennent enfant du processus 1 (systemd /init), <strong>on dit qu’ils sont orphelins</strong>;</li>
<li>appelle les fonctions enregistrées par atexit (c.f. man).</li>
</ul>
<p style="margin-bottom: 0px;">Il existe d’autres fonction pour terminer un programme:</p>
<pre style="margin-bottom: 0px;"><code data-noescape data-trim>
void _exit(int status); // appel système direct, sans appel aux fonction enregistrées avec atexit
void abort(void); // génération d’un core dump
</code></pre>
</div>
</section>
<section>
<h2>Processus zombies</h2>
<div class="text-block">
<p>
Lorsqu’un processus se termine, le noyau garde certaines informations de la
<code>task_struct</code> (pid, statut de terminaison, etc...). <strong>On dit alors que le
processus est un zombie</strong>.
</p>
<p>
Ces information sont conservées en mémoire tant que le parent du processus
n’y a pas accédé.
</p>
<p>
Dans le cas ou le parent du processus est mort c’est le processus 1 (systemd
ou init) qui va se charger de détruire la <code>task_struct</code>.
</p>
</div>
</section>
<section>
<h2>Eviter les zombies</h2>
<div class="text-block" style="font-size: 0.9em;">
<p>Lorsqu’un processus effectue un fork il doit donc prendre soins d’éviter les
zombies en appelant une des fonctions suivantes:</p>
<pre><code class="c" data-noescape data-trim>
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
</code></pre>
<p>Ces fonctions permettent d’attendre la terminaison d’un enfant pour
récupérer son statut. Si un enfant est déjà terminé (i.e. est un zombie), ces
fonctions retournent immédiatement.</p>
<p>Plusieurs macros permettent de tester le statut de retour (c.f. man wait)
dont:</p>
<ul>
<li><code>WIFEXITED(status)</code> : indique si l’enfant c’est terminé normalement;</li>
<li><code>WCOREDUMP(status)</code> : indique si un core dump de l’enfant a été créé.</li>
</ul>
</div>
</section>
<section>
<h2>Questions</h2>
<p>Un processus orphelin peut-il rester un zombie longtemps</p>
<p>Dans quels cas un processus peut rester un zombie longtemps ?</p>
</section>
</section>
<section>
<section>
<h1>Execution de processus</h1>
</section>
<section>
<h2>Exec*</h2>
<div class="text-block" style="font-size: 0.9em;">
<p style="text-align: left;margin-bottom: 5px;">L’<strong>execution d’un nouveau programme</strong> ce fait par les fonctions <code>exec*</code>, dont:</p>
<pre><code class="c" data-trim data-noescape>
#include <unistd.h>
int execve(const char *filename, char *const argv[], char *const envp[]);
</code></pre>
<p>Cette fonction ne retourne pas de valeur en cas de succès mais elle:</p>
<ul>
<li>retourne -1 en cas d'erreur (+ errno mis à jour)</li>
<li><strong>remplace les segments du processus courant par les segments de l’éxécutable filename (c.f. Fichiers ELF);</strong></li>
<li>les paramètres argv et envp sont disponibles dans le main du programme appelé.
</ul>
<p>Si filename est un script, le shell correspondant est chargé et le fichier executé par le shell.</p>
<p><strong>C'est donc cette fonction qui se charge de construire l'espace de mémoire virtuel d'un processus à partir du fichier executable.</strong></p>
</div>
</section>
<section>
<h2>Execve: exemple</h2>
<pre style="height: 500px;"><code id="ex-execAll" class="c" data-trim data-noescape style="font-size: 0.85em;"></code></pre>
</section>
<section>
<h2>Les types de fichiers compilés</h2>
<table style="font-size: 0.88em;">
<thead>
<tr>
<th>Système</th>
<th>Nom</th>
<th>Commentaires</th>
</tr>
</thead>
<tbody>
<tr style="border: none;">
<td style="border-bottom: 0;">MSDOS / Windows</td>
<td>COM</td>
<td>Exécutable très limité, n’est quasi plus utilisé</td>
</tr>
<tr>
<td></td>
<td>PE (Portable Executable)</td>
<td>
Fichiers exécutables: .EXE<br>
Librairies partagées : .DLL<br>
ActiveX: .OCX
</td>
</tr>
<tr>
<td>OS X</td>
<td>Mach-O</td>
<td>Apps., frameworks, bib., etc.</td>
</tr>
<tr>
<td style="border-bottom: 0;">Unix/Linux</td>
<td>a.out</td>
<td>Format original des objets et exécutable Unix, non adapté au librairies partagées</td>
</tr>
<tr>
<td style="border-bottom: 0;"></td>
<td>COFF (Common Object File Format)</td>
<td>Ancien format des objets et exécutable Unix, non adapté au librairies partagées</td>
</tr>
<tr style="color: green;">
<td></td>
<td>ELF (Executable and Linkable Format)</td>
<td>
Fichiers Exécutables: .o<br>
Librairies partagées: .so<br>
Fichiers core (coredump)<br>
Utilisable sur plusieurs plateformes
</td>
</tr>
</tbody>
</table>
<aside class="notes">
<p>ELF: Utilisable sur plusieurs platformes car il permet de gérer par exemple les littleendia et big endian. Aussi utiliser sur les consoles de jeux (Wii, PS, …), ce qui ne veux pas dire qu’un fichier ELF compiler pour 8086 va fonctionner sur une console.</p>
<p>Mach-O is close to ELF</p>
</aside>
</section>
<section>
<h2>Organization d'un fichier ELF</h2>
<ul style="font-size: 0.9em;">
<li>des segments qui:</li>
<ul>
<li>permettent de <strong>préparer le programme pour son exécution</strong> (c.f. <code><a href="#/3/1">exec*</a></code>);</li>
<li>contiennent une ou plusieurs sections;</li>
</ul>
<br>
<li>des sections qui:</li>
<ul>
<li>contiennent <strong>TOUTES les informations</strong> du programme (pas forcément nécessaire à l’exécution – e.g. débogage);</li>
<li>sont nécessaires pour effectuer les liens lors de l’execution;</li>
</ul>
<br>
<li>Des entêtes et tables qui:</li>
<ul>
<li>indiquent la position de chaque section;</li>
<li>indiquent la position de chaque segment;</li>
<li>indiquent la position de la table des sections et de la table des segments.</li>
</ul>
</ul>
</section>
<section>
<h2>Fichier ELF</h2>
<p style="text-align: left; font-size: 0.8em;">On peut observer le contenu d’un fichier ELF avec les commandes <code>objdump</code> et <code>readelf</code>.</p>
<svg height="700px" width="800px">
<style type="text/css">
svg {
font-size: 20px;
text-anchor: middle;
}
rect {
stroke: black;
stroke-width: 2;
fill: white;
}
path {
stroke: black;
stroke-width: 2;
fill-opacity: 0;
}
.sec-name {
fill: green;
}
.bracket {
fill-opacity: 0;
}
</style>
<defs>
<marker id="arrow" markerHeight="5" markerWidth="5" markerUnits="strokeWidth" orient="auto-start-reverse" refX="50" refY="50" viewBox="0 0 100 100">
<path d="m100,50l-100,40l30,-40l-30,-40z" stroke-width="10" style="fill-opacity: 1;"/>
</marker>
<path id="bracket"
class="bracket"
d="m10 52s-4-0-6-1c-6-6 4-16 0-23-1-1-4-2-4-2s3-1 4-2c4-7-6-17-0-22 1-1 5-1 5-1"
vector-effect="non-scaling-stroke"
/>
<g id="elf">
<g transform="translate(2,2)">
<rect width="70" height="100" />
<text x="35" y="50" alignment-baseline="middle">Entête</text>
</g>
<g transform="translate(72,2)">
<rect width="100" height="100" />
<text x="50" y="35" alignment-baseline="middle">Table des</text>
<text x="50" y="65" alignment-baseline="middle">segments</text>
</g>
<g transform="translate(172,2)">
<rect width="84" height="100" />
<text x="42" y="35" alignment-baseline="middle">Section</text>
<text x="42" y="65" alignment-baseline="middle" class="sec-name">.text</text>
</g>
<g transform="translate(256,2)">
<rect width="84" height="100" />
<text x="42" y="35" alignment-baseline="middle">Section</text>
<text x="42" y="65" alignment-baseline="middle" class="sec-name">.rodata</text>
</g>
<g transform="translate(339,2)">
<rect width="24" height="100" />
<text x="12" y="50" alignment-baseline="middle">...</text>
</g>
<g transform="translate(363,2)">
<rect width="84" height="100" />
<text x="42" y="35" alignment-baseline="middle">Section</text>
<text x="42" y="65" alignment-baseline="middle" class="sec-name">.data</text>
</g>
<g transform="translate(447,2)">
<rect width="84" height="100" />
<text x="42" y="35" alignment-baseline="middle">Section</text>
<text x="42" y="65" alignment-baseline="middle" class="sec-name">.bss</text>
</g>
<g transform="translate(531,2)">
<rect width="84" height="100" />
<text x="42" y="35" alignment-baseline="middle">Section</text>
<text x="42" y="65" alignment-baseline="middle" class="sec-name">.debug</text>
</g>
<g transform="translate(614,2)">
<rect width="24" height="100" />
<text x="12" y="50" alignment-baseline="middle">...</text>
</g>
<g transform="translate(638,2)">
<rect width="100" height="100" />
<text x="50" y="35" alignment-baseline="middle">Table des</text>
<text x="50" y="65" alignment-baseline="middle">sections</text>
</g>
</g>
</defs>
<use xlink:href="#elf" x="30" y="0"/>
<foreignObject x="30" y="200" width="100%" height="100%">
<body xmlns="http://www.w3.org/1999/xhtml">
<pre style="font-size: 12pts"><code class="c" data-noescape data-trim>
#include <elf.h>
typedef struct {
//les variables ci-dessous ne sont pas listée dans l’ordre de l’entête
...
uint16_t e_type; /* Executable, bibliothèque, objet, ... */
uint_16 e_machine; /* Intel, HP,...*/
ElfN_Addr e_entry; /* Première instruction à exécuter par le processus */
ElfN_Off e_phoff; /* Offset de départ de la table des segments */
uint16_t e_phentsize; /* Taille d’une entrée dans la table des segments*/
uint16_t e_phnum; /* nombre d’entrées dans la table des segments */
ElfN_Off e_shoff; /* Offset de départ de la table des sections */
uint16_t e_shentsize; /* Taille d’une entrée dans la table des sections*/
uint16_t e_shnum; /* nombre d’entrées dans la table des sections*/
...
} ElfN_Ehdr;
</code></pre>
</body>
</foreignObject>
<!-- bracket + arrow to header -->
<g transform="translate(40,205) scale(2 6)">
<use xlink:href="#bracket" x="0" y="0"/>
</g>
<path d="M50 110 L5 360 L30 360" marker-end="url(#arrow)" />
<!-- bracket + arrow to segments -->
<g transform="translate(580,407) rotate(180 0 0)">
<use xlink:href="#bracket" x="0" y="0"/>
</g>
<path d="M590 382 L650 382 L650 150 L160 150 L160 110" marker-end="url(#arrow)" />
<!-- bracket + arrow to sections -->
<g transform="translate(580,475) rotate(180 0 0)">
<use xlink:href="#bracket" x="0" y="0"/>
</g>
<path d="M590 450 L720 450 L720 110" marker-end="url(#arrow)" />
<!-- arrow to code entry -->
<path d="M570 328 L600 328 L600 180 L230 180 L230 110" marker-end="url(#arrow)" style="stroke: gray"/>
</svg>
</section>
<section>
<h2>Fichier ELF</h2>
<p style="text-align: left; font-size: 0.8em;">La table des sections permet de définir les sections dans le fichier. Une
section peut contenir des informations de liage, du code, des données.</p>
<svg height="120px" width="800px">
<use xlink:href="#elf" x="30" y="0"/>
</svg>
<pre style="font-size: small;"><code class="c" data-noescape data-trim>
typedef struct {
...
uint32_t sh_name; /*Index spécifiant le nom de la section (.text, .data, etc.)*/
ElfN_Addr sh_addr; /* Adresse de la section en mémoire virtuelle */
ElfN_Off sh_offset; /* Offset de la section dans le fichier ELF*/
uintN_t sh_size; /* Taille de la section */
...
} ElfN_Shdr;
</code></pre>
<p style="font-size: 0.8em;">Exercice: ajouter les flêches</p>
</section>
<section>
<h2>Fichier ELF</h2>
<p style="text-align: left; font-size: 0.8em;">La table des segments (program header) permet de regrouper les sections en
plusieurs segments. <strong>Ces segments peuvent être chargés en mémoire virtuelle lors de l'exécution</strong>.</p>
<svg height="240px" width="800px">
<use xlink:href="#elf" x="30" y="0"/>
<path d="M30 170 L768 170"/>
<text x="400" y="190">Espace virt. proc.</text>
<path d="M30 238 L768 238"/>
</svg>
<pre style="font-size: small;"><code class="c" data-noescape data-trim>
typedef struct {
uint32_t p_type; /* if == PT_LOAD -> le segment doit être placé en mémoire */
ElfN_Off p_offset; /* Offset du segment dans le fichier */
uintN_t p_filesz; /* Taille du segment dans le fichier*/
ElfN_Addr p_vaddr; /* Adresse où charger le segment en mémoire virtuelle */
uint32_t p_memsz; /* Taille du segment en mémoire, si >= p_filesz, complété par des 0 */
uintN_t p_flags; /* Exec, write, read */
...
} ElfN_Phdr;
</code></pre>
<p style="text-align: left; font-size: 0.8em;">Exercice (ensemble): représenter comment ces informations permettent de définir l'espace de mémoire virtuelle du processus</p>
</section>
</section>
<section data-markdown="scheduling.md" data-separator-vertical="^\r?\n--\r?\n$"></section>
</div>
</div>
<script src="../../../js/reveal.js"></script>
<!-- Initialize reveal.js with common configuration -->
<!-- TODO find a way to have chalkboard script included from the config to avoid redundancy in each presentation -->
<script src="../../../plugin/reveal.js-plugins/chalkboard/plugin.js"></script>
<script src="../config.js" type="module"></script>
</body>
</html>