Skip to content
Snippets Groups Projects
Commit cf18e713 authored by Guillaume Chanel's avatar Guillaume Chanel
Browse files

Starting from before last commit of...

parents
No related branches found
No related tags found
No related merge requests found
Showing
with 1685 additions and 0 deletions
*
!*/
!*.html
!*.md
!.gitignore
!*.c
!*.h
!Makefile
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Objectifs</title>
<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 with a fallback on local monokai in case of connexion pb -->
<link rel="stylesheet" href="../../../plugin/highlight/monokai.css" id="highlight-theme">
<link rel="stylesheet" href="https://highlightjs.org/static/demo/styles/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.css' : '../../../css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h1>Systèmes d'exploitation</h1>
<p class="author">Guillaume Chanel</p>
<ol>
<li><a href="../1.processes/">Processus et virtualisation</a></li>
<li class="slide-menu-item"><a href="../6.files">Les fichiers et les répertoires sous UNIX</a></li>
<li class="slide-menu-item"><a href="../7.io">Les entrées / sorties (I/O)</a></li>
<li>Communications inter-processus</li>
<ul>
<li><a href="../2.signals/">Les signaux</a></li>
<li><a href="../3.sharedmem/">Les mémoires partagées</a></li>
<li><a href="../4.sockets/">Les sockets / le réseau</a></li>
</ul>
<li><a href="../5.drivers/">Les modules</a></li>
<!-- <li style="color: darkgray">(Systèmes de fichier et disques dures)</li> -->
</ol>
</section>
<section>
<section>
<h1>Introduction & Objectifs</h1>
</section>
<section>
<h2>Qu'est-ce q'un système d'exploitation ?</h2>
<div class="text-block">
<p><strong>Offre aux programmes des services (par exemple une API) leur permettant de facilement exploiter les ressources disponibles.</strong></p>
<p>Examples de ressources exploitées par le système:</p>
<ul>
<li>la mémoire vive;</li>
<li>la mémoire de masse;</li>
<li>les processeurs;</li>
<li>les périphériques.</li>
</ul>
</div>
</section>
<section>
<h2>Vue générale</h2>
<div class="cols">
<img src="FullView.svg" alt="Full view of an operating system (at least linux)" width="180%" style="border:none; background-color: white">
<p class="fragment">Où placeriez-vous l'utilisateur dans ce schema ?</p>
</div>
</section>
<section>
<h2>Une autre vue d'un système d'exloitation</h2>
<img src="https://upload.wikimedia.org/wikipedia/commons/e/e2/Syst%C3%A8me-exploitation_%28relations%29.gif" alt="Wikipedia view of an OS" width="55%">
<small>Taken from <a href="https://fr.wikipedia.org/wiki/Syst%C3%A8me_d%27exploitation">wikipedia</a></small>
</section>
<section>
<h2>Objectifs</h2>
<div class="cols">
<div>
<h4>Objectifs théoriques</h4>
<ul class="fragment" data-fragment-index="1">
<li>Décrire ce qu'est un processus</li>
<li>Résumer les mécanismes de mémoire virtuelle</li>
<li>Lister les moyens de communication entre processus</li>
<li>Définir le rôle d'un module</li>
<!-- <li style="color: darkgray;">Expliquer la représentation sur disque d'un système de fichiers</li> -->
</ul>
</div>
<div>
<h4>Objectifs pratiques</h4>
<ul class="fragment" data-fragment-index="2">
<li>Coder en C, analyser et comprendre un code existant</li>
<li>Identifier un appel système</li>
<li>Créer des processus</li>
<li>Communiquer par réseau, signaux et mémoires partagées</li>
<li>Implémenter un module</li>
</ul>
</div>
</div>
</section>
</section>
<section>
<section>
<h1>Organisation</h1>
</section>
<section>
<h2>Informations et contact</h2>
<ul>
<li>Information sur le cours</li>
<ul style="margin-bottom:20px">
<li>Toutes les informations sont sur <a href="https://cyberlearn.hes-so.ch/course/view.php?id=15177">cyberlearn</a></li>
<li><strong>Merci de vous inscrire au bon groupe: jour vs. soir</strong></li>
<li>Le cours est disponible <a href="http://cui.unige.ch/~chanel/prez/presentations/sys-exploitation/">en ligne</a></li>
</ul>
<li>Si vous avez des questions (emails sur cyberlearn):</li>
<ul>
<li>Enseignant: Guillaume Chanel</li>
<li>Assistants: Strano Gabriel et Gonzalez David</li>
</ul>
</ul>
</section>
<section>
<h2>Cours et horaires</h2>
<ul>
<li>1h30/45 de cours</li>
<ul style="margin-bottom:20px">
<li>du cours avec des questions <strong>et des réponses</strong></li>
<li>un TP long sur les shells qui sera noté</li>
<li>d'autres TPs notés mais plus petits</li>
</ul>
<li>2h de travail autonome réalisé sous forme de TP</li>
<ul>
<li>travailler ce qui n'est pas finit en cours</li>
<li>travailler les TPs à rendre</li>
</ul>
</ul>
</section>
<section>
<h2>Matériel</h2>
<p>Les cours sont aussi disponibles au format HTML ici:</p>
<p><small><a href="http://cui.unige.ch/~chanel/prez/presentations/sys-exploitation/">http://cui.unige.ch/~chanel/prez/presentations/sys-exploitation/</a></small></p>
<p>Les cours au format HTML sont imprimables en PDF via chromium/chrome</p>
<p>Pour cela il faut ajouter <code>?print-pdf</code> à la fin de l'adresse d'un cours, puis imprimer comme habituellement:</p>
<p><small><a href="http://cui.unige.ch/~chanel/prez/presentations/sys-exploitation/0.objectifs/?print-pdf">http://cui.unige.ch/~chanel/prez/presentations/sys-exploitation/0.objectifs/?print-pdf</a></small></p>
</section>
</section>
<section>
<section>
<h1>Evaluation</h1>
</section>
<section>
<h2>Evaluation pratique</h2>
<ul style="line-height: 150%">
<li>Les évaluation suivantes seront effectuées (coefficient 1 chacune):</li>
<ul>
<li>Un TP sur les shells</li>
<li>Deux ou 3 TPs (mémoire, sockets, modules) (coefficient 1 pour le tout)</li>
<li>Un examen sous forme de quizz cyberlearn en fin de session</li>
</ul>
</ul>
</section>
<!--
<section>
<h2>Remarques</h2>
<ul class="text-block">
<li>Les questions sont de haut niveau (e.g. qu’est-ce qu’un processus) ET de bas niveau (quels sont les valeurs possible de tel paramètre)</li>
<li>Eviter de parler de l’évident (include, initialisation a 0, etc.) pour se concentrer sur les notions vues en cours</li>
<li>Mieux vaux passer une question que de tenter l’impossible</li>
</ul>
</section>
<section>
<h2>Matériel autorisé</h2>
<div class="text-block">
<p>A disposition</p>
<ul>
<li>manuel electronique</li>
<li>crayon</li>
<li>marqueur pour la tableau</li>
</ul>
<br>
<p>Non disponible</p>
<ul>
<li>cours</li>
<li>connexion internet</li>
</ul>
</div>
</section>
-->
</section>
</div>
</div>
<!-- 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>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define MALLOC_SIZE 0x10 //en octets
#define STACK_ALLOC_SIZE 256*1024 //en octets
uint32_t nonInitData; //Variable globale non initialisée
uint32_t initData = 0x12; //Variable globale initialisée
char* ptChar = "Where is this pointed string?"; // ???
char tabChar[] = "Where is this char array?"; // ???
void useStack()
{
//Augmentation de la pile
char tab[STACK_ALLOC_SIZE];
//Pause pour examiner le processus
printf("Stack size has been increase and the program paused...\n");
fgetc(stdin);
}
int main()
{
//Décalaration de variables locales
uint32_t localVar1, i;
char *pt_malloc; //pointeur sur la zone allouée
// Just print my pid
printf("My PID = %d\n\n", getpid());
//Récuperer l'adresse du brk avant et après allocation de mémoire
pt_malloc = malloc(MALLOC_SIZE);
//Affichage de l'adresse de la fonction main dans le segment de code
printf("--- code segment ---\n");
printf("main function adress: %p\n", main);
printf("use stack function adress: %p\n", useStack);
printf("Fixed string adress(ptChar): %p\n", ptChar); //La chaine est constante donc dans .bss
//Affichage des differente variable presentes dans le segment de donnée (dabord initialisées puis non initialisées)
printf("--- data segment ---\n");
printf("initData address: %p\n", &initData);
printf("Pointer to fixed string address (ptChar adress): %p\n", &ptChar); //Ce pointeur est alloué en donnée globale initialisée
printf("Character array address (tabChar): %p\n", tabChar); //Le tableau est une variables (avec equivalence de pointeur) -> données initialisées
printf("--\n");
printf("nonInitData address: %p\n", &nonInitData);
//Affichage des donnée du tas (brk avant et apres allocation de mémoire et pointeur sur donnée allouées)
printf("--- Heap ---\n");
printf("Address allocated variable: %p\n", pt_malloc);
//Affichage des données locales affichées dans la piles
printf("--- Stack ---\n");
printf("First Local variable address: %p\n", &localVar1);
printf("Second Local variable address: %p\n", &i);
printf("pt_malloc variable address: %p\n", &pt_malloc);
printf("-------------\n");
//Pause pour examiner le processus
fgetc(stdin);
//utilisation de la pile et pause
useStack();
//try to modify the pointed string
ptChar[0] = 'e';
return EXIT_SUCCESS;
}
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[], char*env[]) {
int i;
for(i=1; i < argc; i++) {
pid_t pid;
if((pid = fork()) == 0) {
char *new_argv[] = {argv[i], NULL};
if(execve(argv[i], new_argv, env) == -1) {
perror(argv[i]);
exit(EXIT_FAILURE);
}
printf("This process was succesfully spawned");
//WILL THE LINE ABOVE BE EXECUTED ?
}
}
return 0;
}
This diff is collapsed.
<!-- TODO: move in mytheme -->
<style>
.reveal p {
text-align: left;
}
</style>
# Ordonnancement
--
## Ordonancement préemptif
Le noyaux se charge de distribuer les processus sur les différents CPU:
- l’ordonnanceur attribue un CPU à un processus, généralement **pour une tranche de temps précise appelée quantum**;
- l’ordonnanceur choisi quel est le nouveau processus qui va être alloué à ce CPU une fois le quantum ou le processus terminé;
<div class="text-block">
Toutefois un processus peut libérer un CPU volontairement avant la fin du quantum si:
- il se met en attente d’une ressource;
- il reçoit un signal de suspension (c.f. SIGSTOP, SIGSTP);
- il appel la fonction `sched_yield` (c.f. man).
</div>
--
## Odonancement sous Linux
Implémentation:
- il existe une liste de processus pour différentes priorités statiques [0-99];
- les processus de hautes priorités sont toujours exécutés d’abord.
![](img/sched-priority.svg) <!-- .element: width="180%"-->
--
## Priorité statique 0
Trois modes d’ordonnancement sont disponibles en priorité statique 0:
- **standard (i.e. par défaut – SCHED_OTHER)**;
- pour processus à lourde charge de calcul (SCHED_BATCH);
- pour processus à très très faible priorité (SCHED_IDLE).
--
## Priorité statique 0 - SCHED_OTHER
Cette stratégie permet de s’assurer que chaque processus sera traité après avoir eu un certain nombre de déni de CPU qui dépends de sa priorité et de celle des autres processus.
- le processus de la liste est choisi par rapport à une **priorité dynamique**;
- priorité dynamique = *valeur nice* + nombre de quantum en état prêt sans avoir de processeur à disposition;
- La *valeur nice* d’un processus est attribuée par la commande `nice` ou par la fonction C `setpriority`.
--
## Ordonnancement "TR" - FIFO
Implémentation:
- les processus sont rangés dans une liste lors de leur soumission;
- un nouveau processus est placé en queue de liste;
- chaque processus est exécuté sans interruption (i.e. non préemptif, sauf cas de mise en attente).
![](img/sched-fifo.svg) <!-- .element: width="120%"-->
--
## Odonnancement "TR" - Round Robin
Implémentation:
- les processus sont placés en queue de liste lors de leur soumission (idem FIFO);
- chaque processus est exécuté uniquement pour un quantum de temps (préemptif) puis est replacé en fin de liste.
![](img/sched-RR.svg) <!-- .element: width="120%"-->
--
## Contrôle d'ordonnancement
L’ordonnancement d’un processus peut être contrôlé par les fonctions et structures suivantes pour la priorité statique:
```c
#include <sched.h>
int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param);
int sched_getscheduler(pid_t pid);
struct sched_param {
...
int sched_priority; /* prioritée statique */
...
};
```
Policy peut prendre les valeurs: SCHED_OTHER,SCHED_BATCH, SCHED_IDLE,SCHED_FIFO, SCHED_RR.
La priorité dynamique peut être contrôlé en utilisant:
```c
int setpriority(int which, int who, int prio); /* priorité dynamique, nice */
int getpriority(int which, int who);
```
--
## Commutation de contexte
Lorsque qu’un processus doit en remplacer un autre sur un CPU une commutation de contexte a lieu:
- suspension de l’exécution du processus et sauvegarde de son contexte;
- rétablissement de l’état du CPU à l’état sauvegardé lors de la suspension du processus qui reprend son exécution;
- mise en exécution du nouveau processus.
Au passage il faut:
- Écrire sur le disque les pages modifiées (pages sales);
- Mettre à jour les informations du noyau pour tenir compte du changement de processus actif (par exemple re-calcul de la priorité des processus).
N.B.: Un changement de contexte est couteuse d’où l’avantage des threads.
--
## Le processus dans tous ses états
<p style="text-align:center;"><img src="img/proc-states.svg" width=55% alt="Process states"></p>
| ps | Constante noyau | Description de l’état |
| -- | -------------------- | --------------------- |
| R | TASK_RUNNING | En exécution ou prêt à être exécuté (i.e. dans une file d’attente) |
| S | TASK_INTERRUPTIBLE | En attente d’une ressource (Sleep) et interruptible par un signal |
| D | TASK_UNINTERRUPTIBLE | En attente d’une ressource mais ne peut pas être interrompu |
| T | TASK_STOPPED | Suspendu (sToppé) |
| Z | EXIT_ZOMBIE | Zombie en attente d’un wait de la part du parent. |
| X | EXIT_DEAD | Terminé, mort, ne devrais jamais être observé. |
<!-- .element: style="font-size: 0.7em;" -->
\ No newline at end of file
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>reveal.js</title>
<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 with a fallback on local monokai in case of connexion pb -->
<!-- <link rel="stylesheet" href="../../../plugin/highlight/monokai.css" id="highlight-theme"> -->
<link rel="stylesheet" href="https://highlightjs.org/static/demo/styles/googlecode.css" id="highlight-theme">
<!-- Add my own theme on top of classical reveal.js theme -->
<link rel="stylesheet" href="../css/mytheme.css">
<!-- TODO: move in mytheme -->
<style>
.reveal p {
text-align: left;
}
</style>
</head>
<body>
<div class="reveal">
<div class="slides">
<section data-markdown="signals.md" data-separator-vertical="^\r?\n--\r?\n$"></section>
</div>
</div>
<!-- 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>
# Les signaux
Guillaume Chanel <!-- .element: style="text-align:center;" -->
---
# Envois de signaux
--
## Fonctionnement de l'envois
Un signal est un **événement asynchrone** (i.e. qui peux arriver à tout moment), **identifié par un numéro** (constantes C définies), et **envoyé par le noyau à un processus**.
Un signal est envoyé lorsque:
- le noyaux souhaite signaler un événement (e.g. `SIGCHLD`, `SIGSEGV`);
- un autre processus a demandé à envoyer le signal à travers **un appel système** (e.g. `kill`);
- il s'agit alors de communication inter-processus;
- c'est le noyau qui determine si le processus est autorisé à faire cet envois (`errno = EACCESS`)
Un processus recevant un signal exécute générallement une action par défaut définie pour chaque signal.
--
## Question
Quel est le signal permettant de terminer une application ? <!-- .element: style="text-align:center;" -->
--
## Exemples de signaux
Les signaux suivants sont particulièrement importants (`man 7 signal`).
| Identifiant | Définition | Action par défaut | Remarque |
| ----------- | ------------------------------- | ----------------- | -------- |
| SIGKILL | Terminaison forcée | terminer | __***__ |
| SIGTERM | Terminaison logicielle | terminer | |
| SIGABRT | Terminaison anormale | core-dump |
| SIGSTOP | Demande d’attente | mise en attente | __***__
| SIGCONT | Demande de reprise | reprendre | après SIGSTOP/SIGSTP
| SIGSEGV | Référence mémoire invalide | core-dump | erreur de segmentation
| SIGCHLD | Process. enfant terminé | ignoré | utilisé par wait()
| SIGXCPU | Temps CPU dépassé | core-dump | c.f. limites des ressources
| SIGUSR1/2 | Définit par le programmeur | terminer | |
<!-- .element: style="font-size:0.8em;" -->
__*** ne peut pas être masqué ou ignoré; n’est pas modifiable par un handler.__
--
## Envoyer un signal
Pour envoyer le signal sig à un processus pid on utilise:
```c
#include <signal.h>
int kill (pid_t pid, int sig);
```
ATTENTION le nom prête à confusion: on peut envoyer tout type de signal avec cette fonction pas uniquement SIGKILL. <!-- .element: style="text-align: center; font-size: 0.8em" -->
Pour envoyer le signal sig au processus courant on utilise:
```c
#include <signal.h>
int raise (int sig);
```
Pour envoyer le signal SIGALRM au processus courant après un délais de seconds secondes:
```c
#include <unistd.h>
unsigned int alarm (unsigned int seconds);
```
---
# Masquage et attente de signaux
--
## Masquage des signaux
Un processus recevant un signal est **interrompu** pour exécuter une action.
Toutefois, un signal peut être masqué, dans ce cas:
- l'action sera suspendue jusqu'à ce que le masque du signal soit retiré;
- une fois le masque retiré l'action est exécutée;
- si un signal masqué est reçu plusieurs fois l'action ne sera exécutée qu'une seule fois lorsque le masque sera retiré.
--
## Programmation des masques
1. Définir l’ensemble des signaux concernés avec les fonctions dédiées:
```c
#include <signal.h>
int sigemptyset (sigset_t * set); //un ensemble vide
int sigfillset (sigset_t * set); //l'ensemble de tous les signaux
int sigaddset (sigset_t * set, int signum); //signum est ajouté a set
int sigdelset (sigset_t * set, int signum); //signum est supprimé de set
int sigismember (const sigset_t * set, int signum); //signum est membre de set ?
```
<!-- .element: style="font-size:0.5em;" -->
--
## Programmation des masques
2. Appeler la fonction de masquage:
```c
int sigprocmask (int how, const sigset_t * set, sigset_t * oldset);
```
<!-- .element: style="width:100%;" -->
`set` contient l'ensemble de signaux à masquer et `oldset` contient l’ensemble des signaux initialement masqués (utile pour remettre le masque dans l'état initial).
`how` peut avoir pour valeur:
- `SIG_BLOCK`: les signaux de set sont ajoutés au signaux actuellement bloqués;
- `SIG_UNBLOCK`: les signaux de set sont retirés des signaux actuellement bloqués;
- `SIG_SETMASK`: les signaux actuellement bloqués sont remplacés par set.
--
## Attente de signaux
Deux fonctions existent pour mettre un processus en attente de signaux:
```c
#include <unistd.h>
int pause(void);
```
Cette fonction met le processus en mode « sleep » (TASK_ INTERRUPTIBLE) jusqu’à ce qu’un signal soit reçu.
```c
#include <signal.h>
int sigsuspend(const sigset_t *mask);
```
Cette fonction masque temporairement les signaux de l’ensemble mask et met le processus en mode « sleep » jusqu’à réception d’un signal non masqué.
--
## Question
Le deux codes ci-dessous semblent équivalents
Code 1:
```c
sigfillset(&blockall);
sigprocmask (SIG_SETMASK, &blockall, &oldset);
//..
//Ici s’éxécute un code critique qui ne doit pas être interrompu...
//..
sigsuspend(&oldset);
```
Code 2:
```c
sigfillset(&blockall);
sigprocmask (SIG_SETMASK, &blockall, &oldset);
//..
//Ici s’éxécute un code critique qui ne doit pas être interrompu...
//..
sigprocmask (SIG_SETMASK, &oldset, NULL);
pause();
```
Pourtant le deuxième code peut ne pas fonctionner, pourquoi ?
---
# Reception de signaux
--
## Actions des signaux
Un processus recevant un signal est **interrompu** pour exécuter l’action correspondante.
L’action peut-être:
- une action par défaut;
- d'ignorer le signal: dans ce cas aucune action n'est exécutée (attention cela est différent du masquage);
- **un «handler» programmé par l’utilisateur**.
Un handler est une fonction appellée à chaque recéption d'un signal.
--
## Associer une action à un signal
Pour définir l'action d’un signal on utilise:
```c
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
```
- `signum` : est le numero du signal auquel on souhaite associer une action (e.g. SIGUSR1);
- `act`: la configuration de la nouvelle action à effectuer sur récéption du signal `signum`, peut être `NULL` pour recevoir uniquement `oldact`;
- `oldact`: la configuration initial du signal `signum`.
La stucture `sigaction` a les champs suivants:
```c
struct sigaction {
void *sa_handler (int);
void *sa_sigaction (int, siginfo_t *, void*);
sigset_t sa_mask;
int sa_flags;
}
```
--
## Associer une action à un signal
```c [2]
struct sigaction {
void *sa_handler (int);
void *sa_sigaction (int, siginfo_t *, void*);
sigset_t sa_mask;
int sa_flags;
}
```
`sa_handler` peut prendre pour valeur soit:
- SIG_DFL: l’action par default est mise en place;
- SIG_IGN: le signal sera ignoré;
- l’adresse du handler à exécuter (en fait simplement le nom de la fonction). Cette fonction recevra un entier qui correpond au numéro de signal reçu.
Example:
```c
void mon_handler(int signum) {};
struct sigaction sa;
sa.sa_handler = mon_handler;
```
--
## Associer une action à un signal
```c [3]
struct sigaction {
void *sa_handler (int);
void *sa_sigaction (int, siginfo_t *, void*);
sigset_t sa_mask;
int sa_flags;
}
```
<div style="font-size:0.9em">
Une autre manière de déclarer un handler plus complet. La fonction reçevra les informations suivantes:
- `int`: le numéro du signal reçu
- `siginfo_t`: des informations supplémentaires (e.g le pid du processus envoyant le signal);
- `void`*: un pointeur sur des données envoyées par le processus émetteur.
Example:
</div>
```c
void mon_handler(int signum, sig_info_t info, void* mydata) {};
struct sigaction sa;
sa.sa_sigaction = mon_handler;
```
--
## Associer une action à un signal
```c [2-3]
struct sigaction {
void *sa_handler (int);
void *sa_sigaction (int, siginfo_t *, void*);
sigset_t sa_mask;
int sa_flags;
}
```
**Attention: ne pas utiliser `sa_handler` et `sa_sigaction` en même temps: une union peut être impliquée !**
<!-- .element: style="text-align:center;" -->
--
## Associer une action à un signal
```c [4-5]
struct sigaction {
void *sa_handler (int);
void *sa_sigaction (int, siginfo_t *, void*);
sigset_t sa_mask;
int sa_flags;
}
```
- `sa_mask`: Un ensemble de signaux à masquer pendant l’exécution de l’action. **De plus le signal traité (i.e. [signum](#/3/2)) est systématiquement bloqué**.
- `sa_flags`: modifie le comportement du handler. Important: **SA_SIGINFO permet l'utilisation de sa_sigaction au lieu de sa_handler**.
--
## Règles de programmation des handlers
- un handler doit garantir qu’il n’est pas appelé à nouveau lors de son exécution (signaux associés au handler bloqués et pas de récursivité);
- il faut aussi garantir que le code soit ré-entrant:
- une fonction est ré-entrante si elle peut être interrompue sans causer d’effets de bords;
- toute fonction appelée dans le handler doit aussi être ré-entrante.
Exemple de fonctions NON ré-entrantes:
- `malloc`, `calloc`, `free`;
- `printf`, `scanf`, ...
--
## Exemple
```c
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
void manage_signals(int sig) {
switch (sig)
{
case SIGUSR1:
write(STDOUT_FILENO, "SIGUSR1\n", 8);
break;
case SIGTERM:
write(STDOUT_FILENO, "SIGTERM\n", 8);
break;
default:
break;
}
}
void usr2_exit(int sig) {
write(STDOUT_FILENO, "Received SIGUSR2, process exiting\n", 34);
exit(0);
}
int main() {
struct sigaction sa;
printf("Pid: %d\n", getpid());
sa.sa_handler = manage_signals;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGUSR1);
sigaddset(&sa.sa_mask, SIGTERM);
sa.sa_flags = 0;
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("setting up SIGUSR1");
return 1;
}
if (sigaction(SIGTERM, &sa, NULL) == -1) {
perror("setting up SIGTERM");
return 1;
}
sa.sa_handler = usr2_exit;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGUSR2, &sa, NULL) == -1) {
perror("setting up SIGUSR1");
return 1;
}
while(1)
pause(); // Sleep and wait for a signal
return 0;
}
```
<!-- .element: style="height:550px;font-size:0.35em;" -->
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>reveal.js</title>
<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 with a fallback on local monokai in case of connexion pb -->
<link rel="stylesheet" href="../../../plugin/highlight/monokai.css" id="highlight-theme">
<link rel="stylesheet" href="https://highlightjs.org/static/demo/styles/googlecode.css" id="highlight-theme">
<!-- Add my own theme on top of classical reveal.js theme -->
<link rel="stylesheet" href="../css/mytheme.css">
<!-- TODO: move in mytheme -->
<style>
.reveal p {
text-align: left;
}
</style>
<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('src/asm_readelf/simple.asm').then(function(response) {
add_text_and_highlight(response, 'ex-code-asm');
});
fetch('src/asm_readelf/Makefile').then(function(response) {
add_text_and_highlight(response, 'ex-code-asm-makefile');
});
fetch('src/asm_readelf/exec.c').then(function(response) {
add_text_and_highlight(response, 'ex-code-asm-c');
});
}
</script>
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h1>Mémoires partagées</h1>
<author>Guillaume Chanel</author>
</section>
<section>
<section>
<h1>Memory-mapped file (Fichier en mémoire partagée)</h1>
</section>
<section>
<h2>Rappel</h2>
<div class="text-block">
<p>Nous avons vu que certaines pages de la mémoire virtuelle:</p>
<ul>
<li>ne sont pas présentes en mémoire physique mais réside sur des systèmes de fichiers (swap / fichiers executables)</li>
<li>deviennent disponibles au fur et à mesure des fautes de pages</li>
<li>sont partagées entre plusieurs processus (e.g. librairies partagées)</li>
<li>peuvent être partagées uniquement jusqu'à leur modification ("copy-on-write")</li>
</ul>
<p>Nous allons voir un appel système qui permet d'<strong>associer un segment de mémoire virtuelle à un segment de fichier</strong>. Cet appel système est par exemple <strong>utilisé pour charger les librairies partagées</strong>.</p>
</div>
</section>
<section>
<h2>Principe du "file mapping"</h2>
<div class="text-block">
<p>Associer le segment (une partie) d'un fichier à un nouveau segments de mémoire partagé (file mapping). Cela permet de:</p>
<ul>
<li>partager des pages (données, instructions) entre plusieurs processus;</li>
<li>accéder aux données directement en mémoire (i.e. par pointeurs) plutôt que dans un fichier (i.e. par curseur)</li>
</ul>
<p><strong>Un tel fichier ne sera pas chargé intégralement en mémoire mais page par
page au fur et a mesure des fautes de page du processus.</strong></p>
</div>
</section>
<section>
<h2>Principe du "file mapping"</h2>
<p style="text-align:left; font-size: 0.8em;margin-bottom: 0px;">Deux processus peuvent partager un segment en y associant des espaces
d’adressage virtuel différents:</p>
<img src="img/shared_mem_mmap.svg" alt="Shared memory" height="500px">
</section>
<section>
<h2>Le "file mapping" en pratique</h2>
<div class="text-block" style="font-size: 0.8em">
<p>Pour associer un fichier à un espace de la mémoire virtuelle du processus on:</p>
<ul>
<li>ouvre le fichier en lecture et/ou écriture pour obtenir un descripteur de fichier <code>fd</code>:</li>
<pre><code data-noescape>int open(const char *pathname, int flags);</code></pre>
<li>associe le descripteur de fichier à une zone de la mémoire virtuelle par un appel à:</li>
<pre><code>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);</code></pre>
<li>pense ensuite à fermer / désassocier la mémoire partagée:</li>
<pre><code>int munmap(void *addr, size_t length);</code></pre>
<li>ferme le fichier:</li>
<pre><code>int close(int fd);</code></pre>
</ul>
</div>
</section>
<section>
<h2>open / close</h2>
<div style="font-size: 0.9em;">
<p>On peut ouvrir un fichier avec l'appel système suivant:</p>
<pre><code data-noescape data-trim>
int open(const char *pathname, int flags);
</code></pre>
<ul>
<li><code>pathname</code> est le nom du fichier;</li>
<li><code>flags</code> est un champ de bit indiquant le mode d'accès au fichier (<code>O_RDONLY, O_WRONLY, O_RDWR</code>);</li>
<li>retourne un entier représentant le fichier (descripteur de fichier), soit -1 en cas d'erreur (vérifier <code>errno</code>).</li>
</ul>
<p>On doit fermer un fichier avec l'appel système suivant:</p>
<pre><code class="c" data-noescape data-trim>
int close(int fd);
</code></pre>
<ul>
<li><code>fd</code> est l'entier représentant le fichier (descripteur de fichier);</li>
<li>0 en cas de succès, -1 en cas d'erreur (vérifier <code>errno</code>).</li>
</ul>
</div>
<br>
<small>Note: la description de la fonction open est incomplète. Une <a href="http://cui.unige.ch/~chanel/prez/presentations/sys-exploitation/7.io/7.io.html#/ouvrir-un-canal-open">description complète</a> sera présentée plus tard.</small>
</section>
<section>
<h2>mmap</h2>
<pre><code data-noescape data-trim>
#include &lt;sys/mman.h&gt;
void *mmap(void *addr, size_t length, <span style="opacity: 0.2;">int prot, int flags,</span> int fd, off_t offset);
</code></pre>
<ul style="font-size: 0.8em;">
<li><code>fd</code>: entier représentant le fichier (file descriptor);</li>
<li><code>addr</code>: adresse d‘un début de page (ajustée automatiquement), <strong>si NULL l'adresse est choisie automatiquement</strong>;</li>
<li><code>offset</code>: début du mapping dans le fichier, doit être multiple de la taille d'une page</li>
<li><code>length</code>: taille du mapping dans le fichier, complété par des zéros pour remplir une page en mémoire</li>
<li><strong>retourne l'adresse virtuelle correspondant au début du segment</strong></li>
</ul>
<img src="img/mmap.svg" alt="File mapping" style="border: none; box-shadow: none">
</section>
<section>
<h2>mmap</h2>
<pre><code data-noescape data-trim>
#include &lt;sys/mman.h&gt;
void *mmap(<span style="opacity: 0.2;">void *addr, size_t length,</span> int prot, int flags<span style="opacity: 0.2;">, int fd, off_t offset</span>);
</code></pre>
<div class="text-block" style="font-size: 0.8em;">
<p><code>prot</code>, bit field définissant la protection des pages partagées:</p>
<ul>
<li><code>PROT_READ / PROT_WRITE / PROT_EXEC</code> autorise respectivement la lecture, l’écriture et l’exécution;</li>
<li><code>PROT_NONE</code> aucun droit, utilisé pour réserver des pages;</li>
<li>les droits doivent correspondre au mode d’ouverture du fichier.</li>
</ul>
<p><code>flags</code>, bit field utilisé pour les options suivantes:</p>
<ul>
<li><strong><code>MAP_SHARED</code>: la zone est partagée entre les processus / fichiers toute modification sera reportée aux autres processus et dans le fichier;</strong></li>
<li><strong><code>MAP_PRIVATE</code>: copy-on-write, si un processus modifie le contenu il crée sa propre copie des pages et le fichier ne sera pas modifié;</strong></li>
<li><code>MAP_ANONYMOUS</code>: pas d’association avec un fichier, la mémoire est initialisée à 0 (fd et offset sont ignorés) et partageable uniquement avec ses enfants.</li>
</ul>
</div>
</section>
<section>
<h2>Exercice</h2>
<div class="text-block">
<p>L'objectif de cet exercice est de créer un processus qui:</p>
<ul>
<li>charge le segment contenant le code d'un autre executable dans sa mémoire virtuelle,</li>
<li>execute ce code à partir du point d'entrée mentionné dans l'exécutable.</li>
</ul>
<p>On simule ainsi l'exécution d'un code externe un peu comme cela est fait pour une librairie partagée</p>
</div>
</section>
<section>
<h2>Exercice</h2>
<p style="font-size: 0.8em;">Le programme à charger est fournit en assembleur. Il faut le compiler avec <code>nasm</code> (voir Makefile).</p>
<pre style="font-size: 12px;"><code class="x86asm" id="ex-code-asm"></code></pre>
<pre style="font-size: 12px;"><code class="makefile" id="ex-code-asm-makefile" data-line-numbers="1-7|8-9"></code></pre>
</section>
<section>
<h2>Exercice</h2>
<p style="text-align: left;">Il faut donc:</p>
<ul>
<li>Compiler le programme <code>simple</code>,</li>
<li>Executer ce programme et confirmer son status de retour (42),</li>
<li>utiliser la commande <code>readelf</code> pour identifier <strong>le segment à charger</strong> et le <strong>point d'entrée du programme</strong>,</li>
<li>implementer un programme en C qui va:
<ul>
<li>définir les adresses mesurées à l'étape précédente avec des <code>#define</code></li>
<li>charger le bon segment avec <code>mmap</code>,</li>
<li>définir une fonction pointant sur le point d'entrée dans le mémoire virtuelle,</li>
<li>exécuter cette fonction</li>
</ul>
<li>tester que votre programme C retourne bien 42</li>
</ul>
<p style="text-align: left; color: darkgray;">Pour aller plus loin: votre programme C devra rechercher les informations du segment et du point d'entrée directement dans le fichier executable.</p>
</section>
<section>
<h2>Exerice - correction</h2>
<pre style="font-size: 12px; height: 550px;"><code class="c" id="ex-code-asm-c"></code></pre>
</section>
</section>
<section data-markdown="shared_mem.md" data-separator-vertical="^\r?\n--\r?\n$"></section>
</div>
</div>
<!-- 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>
# Mémoires partagées sans fichier
--
## Vue généralle
Deux processus peuvent partager un segment en y associant des espaces d’adressage virtuel différents:
![](img/shared_mem.svg) <!-- .element: style="margin:0;" -->
--
## Segment de mémoire partagée
Un segment partagé:
- est alloué par le noyau sur requête d’un processus;
- peut être intégré dans les espaces d’adressage d’autres processus qui se l’attachent à des adresses potentiellement différentes;
- contient forcément un nombre entier de pages (ce qui est vrai pour tous les segments).
--
## Types de mémoires partagées
Historiquement il existe deux moyens UNIX / Linux pour créer des mémoires partagées entre deux processus sans relation parent / enfant.
- XSI Inter Process Communication (IPC)
- est héritée de System 5;
- est basée sur un système de clef et d’identificateurs;
- n’est pas limité au mémoires partagées mais est aussi utilisé pour créer des files de messages et des sémaphores.
- **POSIX shared memory objects**:
- **est basé sur la fonction mmap**;
- **utilise des descripteurs de fichier**;
- **est utilisé par Linux pour gérer les mémoires partagées**.
--
## Création de mémoire partagée
Pour créer un nouvel objet "mémoire partagé" ou pour ouvrir un objet existant on utilise:
```c
#include <sys/mman.h>
#include <sys/stat.h> // For mode constants
#include <fcntl.h> // For O_* constants
int shm_open(const char *name, int oflag, mode_t mode);
```
- retourne un descripteur de fichier représentant la mémoire partagée;
- `name` nest pas un nom de fichier standard mais doit commencer par "/", on retrouvera la mémoire partagée dans /dev/shm/name;
- `oflag` et `mode` fonctionnent comme pour open (notamment O_RDONLY, O_RDWR, O_CREAT, O_EXCL). **Attention à ne pas écraser une mémoire partagée crée par un autre utilisateur !**
--
## Taille de la mémoire
Le descripteur obtenu par `shm_open` crée un objet de mémoire partagée POSIX mais avant sont utilisation il faut lui donner une taille. Cela est effectué par l'appel à:
```c
#include <unistd.h>
int ftruncate(int fd, off_t length); // length est la taille en octets
```
`fd`: un descripteur de fichier (ici une mémoire partagée mais fonctionne aussi sur les fichiers);
`length`: la nouvelle taille de la mémoire.
--
## Mapping de la mémoire
On utilise les fonctions habituelles pour effectuer un mapping de la mémoire dans l'espace virtuel du processus:
```c
#include <sys/mman>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t len);
```
--
## Destruction de la mémoire
Pour supprimer la référence à la mémoire partagée on utilise:
```c
int shm_unlink(const char *name);
```
Comme pour l'appel à unlink, uniquement la référence est supprimée (i.e. `/dev/shm/name`). La mémoire ne sera effectivement détruite que si elle est désassociée par `munmap`.
--
## Exemple
Le programme ci-dessous montre un exemple de construction de mémoire partagée:
- [shm.h](src/shm.h)
- [creator.c](src/creator.c)
- [helper.c](src/helper.c)
- [makefile](src/makefile)
Toutefois il souffre d'un défaut. Lequel ?
--
## Conditions de course
Lorsque deux processus coopèrent (e.g. partagent des données) il faut faire extrêmement attention aux **conditions de course**:
- les deux processus peuvent être vu comme concurrent sur l’accès aux données;
- si il n’y a pas de contrôle d’accès sur ces données il peut y avoir conflit dans leur utilisation.
Cela est partiellement dû au fait qu’un processus peut être **suspendu par l’ordonnanceur au milieu d’une opération, laissant les données partagées dans un état intermédiaire**.
Seule les **opérations dites atomiques** garantissent d’être exécutée « en une fois »: le processeur n’est jamais alloué à un autre processus pendant leur exécution.
--
## Résolution des conditions de course
Afin de régler les problèmes de concurrence il faut utiliser des mécanismes de coordination tel que:
- des variables communes;
- des signaux;
- des mécanismes dédiés:
- la mémoire partagée étant représentée par un descripteur de fichier il est possible d'utiliser les `lock` comme pour les fichiers;
- il existe d'autre mécanismes comme les sémaphores ou les mutex:
```c
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
```
\ No newline at end of file
all: simple exec
simple: simple.o
gcc -Wall -s -nostdlib simple.o -o simple
simple.o: simple.asm
nasm -f elf64 simple.asm
exec: exec.c
gcc -std=gnu11 -Wall exec.c -o exec
clean:
rm simple.o simple exec
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
//Information gathered from readelf on the simple executable
//This information can be different on your system. This should be changed accordingly
#define SEG_OFFSET 0x1000
#define SEG_SIZE 0x0e
#define ENTRY_PT 0x1002
void onErr(const char *title) {
perror(title);
exit(EXIT_FAILURE);
}
int main() {
//Open executable file
int fd;
if((fd = open("./simple", O_RDONLY)) == -1)
onErr("open");
//Map file in memory
void *segment;
if((segment = mmap(NULL, SEG_SIZE, PROT_EXEC | PROT_READ, MAP_PRIVATE, fd, SEG_OFFSET)) == MAP_FAILED)
onErr("mmap");
getchar();
//Define a pointer to the _start function
void (*ptFunc)(void) = segment + (ENTRY_PT - SEG_OFFSET);
ptFunc();
munmap(segment, SEG_SIZE);
close(fd);
}
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include "shm.h"
int main(int argc, char* argv[])
{
int fd, i;
sharedMemory *shm;
//Vérifier les entrées
if(argc != 2)
{
printf("Usage: shm-create /sharedMemoryName\n\n");
exit(0);
}
//Créer une mémoire partagée en lecture / écriture
if( (fd = shm_open(argv[1], O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) == -1)
OnError("shm_open");
//Taille de la mémoire = indicator + number
if( ftruncate(fd, sizeof(sharedMemory)) == -1)
OnError("ftruncate");
//File mapping (Les parametres doivent correspondres au mode d'ouverture de l'objet POSIX)
shm = mmap(NULL, sizeof(sharedMemory), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(shm == MAP_FAILED)
OnError("mmap");
printf("Adr memoire partagée:%p\n", shm);
//Initialization memoire a zero
shm->number = 0;
//On attend l'autre processus pour travailler
shm->isReady = !READY;
printf("Ok j'attends un processus qui peut m'aider...\n");
while(!(shm->isReady == READY));
//On peut desassocier l'objet (retirer l'inode) car l'objet ne sera supprimé que lors du munmap
shm_unlink(argv[1]);
//Ok on travail
for(i=0;i<NUM_INCREMENTS;i++)
shm->number = shm->number + 1;
//Afficher le résultat
printf("Pour moi %d, le total est %ld\n", getpid(), shm->number);
//Unmap
if(munmap(shm, sizeof(sharedMemory)) == -1)
OnError("munmap");
return 0;
}
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include "shm.h"
int main(int argc, char* argv[])
{
int fd, i;
sharedMemory *shm;
//Vérifier les entrées
if(argc != 2)
{
printf("Usage: shm-help /sharedMemoryName\n\n");
exit(0);
}
//Ouvrir une mémoire partagée en lecture / écriture
if( (fd = shm_open(argv[1], O_RDWR, S_IRUSR | S_IWUSR)) == -1)
OnError("shm_open");
//File mapping (Les parametres doivent correspondres au mode d'ouverture de l'objet POSIX)
shm = mmap(NULL, sizeof(sharedMemory), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(shm == MAP_FAILED)
OnError("mmap");
printf("Adr memoire partagée:%p\n", shm);
//On signal que l'on est prêt
shm->isReady = READY;
//Ok on travail
for(i=0;i<NUM_INCREMENTS;i++)
shm->number = shm->number + 1;
//Afficher le résultat
printf("Pour moi %d, le total est %ld\n", getpid(), shm->number);
//Unmap
if(munmap(shm, sizeof(sharedMemory)) == -1)
OnError("munmap");
return 0;
}
#ifndef _SHM_H
#define _SHM_H
#include <stdlib.h>
#include <stdio.h>
//La mémoire partagée contiendra deux valeur:
//- un indicateur stipulant si un autre processus est près pour l'opération
//- un nombre qui sera incémenté par les deux processus conjointement
typedef struct {
char isReady;
long int number;
} sharedMemory;
#define READY 0
#define NUM_INCREMENTS 10000000
void OnError(const char *str)
{
perror(str);
exit(EXIT_FAILURE);
}
#endif
CC=gcc
CFLAGS=-Wall -g --pedantic
all: file_client file_server
file_client: file_client.o file_transmission.o
$(CC) $(CFLAGS) -o file_client file_client.o file_transmission.o
file_client.o: file_client.c file_transmission.h
$(CC) $(CFLAGS) -c -o file_client.o file_client.c
file_server: file_server.o file_transmission.o
$(CC) $(CFLAGS) -o file_server file_server.o file_transmission.o
file_server.o: file_server.c file_transmission.h
$(CC) $(CFLAGS) -c -o file_server.o file_server.c
file_transmission.o: file_transmission.c file_transmission.h
$(CC) $(CFLAGS) -c -o file_transmission.o file_transmission.c
clean:
rm -rf *o file_client file_server
\ No newline at end of file
#include "file_transmission.h"
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void prepare_address( struct sockaddr_in *address, const char *host, int port ) {
size_t addrSize = sizeof( address );
memset(address, 0, addrSize);
address->sin_family = AF_INET;
inet_pton( AF_INET, (char*) address, &(address->sin_addr) );
address->sin_port = htons(port);
}
int makeSocket( const char *host, int port ) {
struct sockaddr_in address;
int sock = socket(AF_INET, SOCK_STREAM, 0);
if( sock < 0 ) {
die("Failed to create socket");
}
prepare_address( &address, host, port );
if( connect(sock, (struct sockaddr *) &address, sizeof(address)) < 0) {
die("Failed to connect with server");
}
return sock;
}
int main(int argc, char *argv[]) {
int sock,file;
char *host;
int port;
char *filename;
int filenameLength;
if (argc != 4) {
fprintf(stderr, "USAGE: %s <host> <port> <filename>\n", argv[0]);
exit(EXIT_FAILURE);
}
host = argv[1];
port = atoi(argv[2]);
filename = argv[3];
filenameLength = strlen( filename );
/* Open the file */
if( (file = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0600)) < 0 ) {
die("Failed to create the local file");
}
sock = makeSocket( host, port );
/* Send the filename */
if( write(sock,filename,filenameLength) != filenameLength ) {
die( "Cannot send the filename to retrieve" );
}
/* Receive the file */
if( copy( sock, file ) < 0 ) {
die( "Cannot receive the file" );
}
close(sock);
close(file);
exit(EXIT_SUCCESS);
}
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "file_transmission.h"
#define MAX_FULLPATH 1024
#define MAX_NAME 255
#define MAX_PENDING 256
void prepare_address( struct sockaddr_in *address, int port ) {
size_t addrSize = sizeof( address );
memset(address, 0, addrSize);
address->sin_family = AF_INET;
address->sin_addr.s_addr = htonl(INADDR_ANY);
address->sin_port = htons(port);
}
int makeSocket( int port ) {
struct sockaddr_in address;
int sock = socket(AF_INET, SOCK_STREAM, 0);
if( sock < 0 ) {
die("Failed to create socket");
}
prepare_address( &address, port );
if( bind( sock,
(struct sockaddr *) &address,
sizeof(address)
) < 0 )
{
die("Failed to bind the server socket");
}
if (listen(sock, MAX_PENDING) < 0) {
die("Failed to listen on server socket");
}
return sock;
}
void handleClient( int clientSock, const char *path ) {
char fullPath[MAX_FULLPATH];
int pathLen = strlen( path );
int file;
int nRead;
printf( path );
strncpy( fullPath, path, MAX_FULLPATH ); /* TODO: Proteger le slash */
nRead = read( clientSock, (fullPath+pathLen), MAX_NAME );
if( nRead <= 0 ) {
die( "WTF? 1" );
}
printf( "Requested file: %s\n", fullPath );
file = open( fullPath, O_RDONLY, 0 );
if( copy( file, clientSock ) < 0 ) {
perror( "Failed to send the file" );
}
close( file );
close( clientSock );
}
void run( int serverSock, const char *path ) {
while( 1 ) {
struct sockaddr_in clientAddress;
unsigned int clientLength = sizeof(clientAddress);
int clientSock;
printf( "Waiting for incoming connections\n");
clientSock =
accept(serverSock, (struct sockaddr *) &clientAddress, &clientLength );
if( clientSock < 0 ) {
die("Failed to accept client connection");
}
printf( "Client connected: %s\n", inet_ntoa(clientAddress.sin_addr));
handleClient(clientSock,path);
}
}
int main( int argc, char **argv ) {
int servSock;
char *path;
int port;
if (argc != 3) {
fprintf(stderr, "USAGE: %s <port> <path>\n", argv[0]);
exit(EXIT_FAILURE);
}
port = atoi(argv[1]);
path = argv[2];
servSock = makeSocket( port );
printf( "Server running on port %d at dir '%s'\n", port, path );
run( servSock, path );
close(servSock);
return EXIT_SUCCESS;
}
#include "file_transmission.h"
#include <errno.h>
#include <unistd.h>
int copy(int from, int to) {
char buf[BUFF_SIZE];
ssize_t nread;
while( nread = read(from, buf, sizeof buf), nread > 0 ) {
char *out_ptr = buf;
ssize_t nwritten;
do {
nwritten = write(to, out_ptr, nread);
if (nwritten >= 0) {
nread -= nwritten;
out_ptr += nwritten;
} else if (errno != EINTR) {
return -1;
}
} while (nread > 0);
}
return nread;
}
#ifndef FILE_TRANSMISSION_H
#define FILE_TRANSMISSION_H
#include <stdlib.h>
#include <stdio.h>
#define BUFF_SIZE 32
/* Affiche le message d'erreur et termine l'exécution */
static inline void die(char *issue) {
perror(issue);
exit(EXIT_FAILURE);
}
/* Copie les données d'un descripteur de fichier vers un autre.
Retourne un nombre négatif en cas d'erreur */
int copy( int from, int to);
#endif /* FILE_TRANSMISSION_H */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment