Vous êtes sur la page 1sur 9

3ᵉ année IS – 14 janvier 2021 – 2 h

Contrôle de synthèse 2020-2021


Systèmes d’exploitation & Réseaux
Calculatrice et éléments communicants interdits.
Tous documents autorisés

Le sujet est composé de 5 parties indépendantes à rédiger directement sur le


sujet. Ce n’est pas la peine de mentionner les include dans vos programmes–réponses.
Exercice 1 : Fichiers

Écrire un programme ccaatt qui prend 3 noms de fichier en argument :


Exemple d’appel : $ ./ccaatt file_1 file2 file_3
Ce programme doit prendre la première moitié de file_1, la deuxième moitié de file_2 et les
concaténer dans un nouveau fichier file_3 (AA, BBBB => ABB).
On demande une implémentation exclusivement avec mmap. Vous pourrez utiliser la fonction int
ftruncate(int fd, off_t length) pour fixer la taille du nouveau fichier (comme son nom de l’indique pas, cette
fonction peut aussi rallonger un fichier).

int main(int argc, char **argv)


{
if (argc < 4)
perror('Not enough args');

int fd_a = open(argv[1], O_RDONLY, S_IRUSR | S_IWUSR);


int fd_b = open(argv[2], O_RDONLY, S_IRUSR | S_IWUSR);
int fd_c = open(argv[3], O_RDWR, S_IRUSR | S_IWUSR);
struct stat sb_a, sb_b, sb_c;

if (fstat(fd_a, &sb_a) == -1)


perror("Error on f1 opening");

if (fstat(fd_b, &sb_b) == -1)


perror("Error on f2 opening");

if (ftruncate(fd_c, sb_a.st_size/2 + sb_b.st_size/2) == -1)


perror("Error on opening f3");

if (fstat(fd_c, &sb_c) == -1)


perror("Error on f3 opening");

char *f1 = mmap(NULL, sb_a.st_size, PROT_READ, MAP_SHARED, fd_a, 0);


char *f2 = mmap(NULL, sb_b.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
fd_b, 0);
char *f3 = mmap(NULL, sb_c.st_size, PROT_WRITE, MAP_SHARED, fd_c, 0);
int i_3 = 0;
for (int i = 0; i < sb_a.st_size / 2; i++)
{
f3[i_3] = f1[i];
i_3++;
}

for (int i = 0; i < sb_b.st_size / 2; i++)


{
f3[i_3] = f2[i];
i_3++;
}
return 0;
}

Exercice 2 : Utilisation de Fork On

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
int i = fork() && (fork() || fork());
printf("fork! (%d, %d, %d)\n", getpid(), getppid(), i);
exit(EXIT_SUCCESS);
}

1. Que fait ce programme ? Dessiner un arbre des processus avec la valeur de i à chaque étape.
Indice : Il n’y aura pas 8 processus.
2. Écrire un programme qui, pour un n ≥ 0 donné, engendre un arbre généalogique linéaire de n
générations de fils (sans aucun frère). Chaque fils crée tire un int au hasard. Le programme
initial (le père) affiche la plus grande des valeurs aléatoires tirées par les fils.
Exercice 3 : Mémoire

Vous allez écrire un allocateur mémoire qui permet d’allouer efficacement des espaces mémoires
de tailles identiques. Pour accélérer les allocations, chaque appel à sbrk() demandera 10 fois plus
d’espace que nécessaire, cela permettra ainsi d’avoir déjà 9 blocs prêt à être alloué. Le cahier des
charges de l’API est le suivant :

• void *block_init(size_t x, int n) initialise l’allocateur pour qu’il puissent fournir à la demande n blocs
de x octets.
• void *block_alloc() retourne l’adresse d’un bloc de x octets vide. Le numéro du bloc sera stocké
juste avant cette adresse.
• void block_free(void* ptr) marque un bloc comme libre un bloc libérer, • void block_free_all()
libère tous les blocs alloués depuis l’appel à bloc_init.

Cet allocateur devra gérer les blocs alloués et désallouer, un bloc alloué sera marqué avec son
numéro de bloc (int placé juste avant le bloc), un bloc non alloué portera un numéro de bloc nul. Si
aucun bloc n’est disponible lors de l’appel à void *block_alloc(), alors un nouveau pool de n blocs sera
créé et ajouté à la liste chaînée.
Le header de la liste chaînée sera le suivant :
typedef struct BLOCK_TAG{
struct BLOCK_TAG *ptr_next; // pointe sur le prochain chainon
int n; // nombres de blocs
size_t x; // taille d'un bloc
} BLOCK_PTR;
BLOCK_PTR *head = NULL;

à utiliser de la manière suivante :


1. Quelle taille devrait réellement faire la zone mémoire réservée par void *block_init( size_t x, int n)?
La taille total fait :
HEADER_SIZE = sizeof(BLOCK_PTR) + 10 * n *( x + sizeof(int)) + ;

3/7
2. Proposer une implémentation de l’allocateur void *block_init(size_t x, int n) telle qu’il puisse aussi
être utilisé pour ajouter un nouveau pool.
void *block_init(size_t size, int n)
{
HEADER_TAG *head = sbrk(HEADER_SIZE + size);
head->ptr_next = NULL;
head->bloc_size = size;

// put watchdog at the end


long *end = (void *)(head + 1) + size + sizeof(MAGIC);
*(end - 1) = MAGIC;

// First address right after HEADER


return head + 1;
}

3. Proposer une implémentation de void *block_alloc().


void *bloc_alloc()
{
void *block;
block = sbrk(x);
if (block == (void*) -1)
return NULL;
return block;
}

4. Proposer une implémentation de void block_free(void *addr).

4/7
5. Proposer une implémentation de void block_free_all().

Exercice 4 : Filesystem

On considère la structure d’Inodes vue en TP pour TosFS :


/* on disk inode */
struct tosfs_inode {
__u32 inode; /* inode number */
__u32 block_no; /* block number for data. 1 block per file. */
__u16 uid; /* user id */
__u16 gid; /* group id */
__u16 mode; /* mode (fil, dir, etc) */
__u16 perm; /* permissions */
__u16 size; /* size in byte */
__u16 nlink; /* link (number of hardlink) */
};

La taille d’un bloc du FS est fixé à 2048 octets.

1. Dessinez la structure de ce Filesystem sur le disque dur, en particulier le contenu des pre miers
blocs.
2. Combien d'inodes peut-on avoir au maximum sur un filesystem si on considère que la table
d’inodes tient forcément dans 1 seul bloc ?

2048 / 128 = 16 inodes dans 1 bloc

3. Pour augmenter la taille des fichiers possibles, on considère Tos2FS où le champ d'indexation
direct block_no est remplacé par une indexation indirecte simple. Quelle est alors la taille
maximale d’un fichier en octets et en blocs ?

4. Quel est alors le nombre maximal de bloc possible pour l’ensemble du système de fichier
Tos2FS ?
Exercice 5 : Réseau

Un service de streaming en ligne de musique propose une API REST. Cette API permet de manipuler notamment les playlists des
utilisateurs à l’aide des ”end-points” suivants :

• POST /api/playlists/{playlist_id}/tracks
• POST /api/users/{user_id}/playlists
• GET /api/users/{user_id}/playlists
• GET /api/playlists/{playlist_id}/images
• GET /api/playlists/{playlist_id}
• GET /api/users/{user_id}/playlists

• DELETE /api/playlists/{playlist_id}/tracks
• PUT /api/playlists/{playlist_id}/tracks
• PUT /api/playlists/{playlist_id}/images

On suppose que chaque objet échangé est sous la forme json suivante :
{
'id': 12345,
'type': 'type de la ressource',
'name': 'nom de la ressource',
'data': données de la ressource
}

avec le type pouvant être égale à : ’playlist’, ’user’, ’artist’, ’tracks’, ’image’…

1. Quel end-point doit-on utiliser pour créer une nouvelle playlist ?


POST /api/users/{user_id}/playlists ?

2. Quel end-point doit-on utiliser pour lister les pistes d’une playlist ?
GET /api/users/{user_id}/playlists

3. Quel end-point doit-on utiliser pour changer une piste d’une playlist ?
PUT /api/playlists/{playlist_id}/tracks

4. Quel end-point faudrait-il ajouter à cette liste pour effacer une playlist ?

DELETE /api/playlists/{playlist_id}

Vous aimerez peut-être aussi