• Aucun résultat trouvé

Atelier 4 : Programmation système en C

N/A
N/A
Protected

Academic year: 2022

Partager "Atelier 4 : Programmation système en C"

Copied!
14
0
0

Texte intégral

(1)

Atelier 4 : Programmation syst` eme en C

Syst`emes embarqu´es temps r´eel – GIF-3004 Professeur : Christian Gagn´e

Semaine 4 : 7 f´evrier 2020

(2)

Laboratoire 2

Vous devriez avoir termin´e la phase exploratoire et comprendre ce que vous devez faire de mani`ere g´en´erale

I C’est le temps de plonger dans le code !

Nous allons voir ensemble certaines constructions utiles, mais pas forc´ement ´evidentes, `a la r´ealisation du laboratoire 2

SETR – GIF-3004 (U. Laval) Programmation syst`eme C. Gagn´e 2 / 14

(3)

Lectures et ´ ecritures

Extrait du manuel de read() (mˆeme chose pour write())

read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf. [...] On success, the number of bytes read is returned [...] It is not an error if this number is smaller than the number of bytes requested.

Qu’est-ce que cela implique ?

I Ce n’est pas parce que vous vous attendez `a recevoir (ou envoyer)noctets que vous allez tous les recevoir (ou envoyer) d’un seul coup !

Cela s’applique `a la majorit´e des lectures et ´ecritures : fichiers, sockets, pipes, etc.

(4)

Lectures et ´ ecritures

Quelle est l’erreur dans le code suivant ?

void lireFichier(char* buffer, size_t* size) {

*size = 43894;

char* buffer = malloc(*size);

read(descripteur, buffer, *size);

}

Comment le corriger ? En ajoutant une boucle de lecture

void lireFichier(char* buffer, size_t* size) {

*size = 43894;

char* buffer = malloc(*size);

size_t octetsLus = 0;

while(octetsLus < *size) {

octetsLus += read(descripteur, buffer + octetsLus, *size - octetsLus);

} }

En bonus : pourquoi size_t?

SETR – GIF-3004 (U. Laval) Programmation syst`eme C. Gagn´e 4 / 14

(5)

Allocation m´ emoire

C’est bien beau de lire, mais encore faut-il pouvoir allouer !

I Vous connaissez probablement d´ej`avoid *malloc(size_t size);

I Consid´erez utilisercalloc, tr`es similaire, mais qui initialise la m´emoire `a 0 La taille doit ˆetre connue lors de l’appel `amalloc()

Petit probl`eme dans un contexte de communication

I Le message est compos´e democtets d’en-tˆete et denoctets de contenu

I mest connu, mais pasn: il faut d’abord lire l’en-tˆete pour le savoir !

I Mais pour lire l’en-tˆete, il faut d’abord allouer un buffer !

I Tout cela n’est pas tr`es productif

(6)

Allocation m´ emoire

Premi`ere solution : allouer 1 Go de m´emoire,«au cas o`u» Pas tr`es efficace...

I Et le jour o`u le message d´epasse 1 Go ? Meilleure solution : utiliser realloc()

struct msgReq req;

char* buffer = malloc(sizeof(req));

/* Lire seulement l'en-tete */

octetsTraites = read(reqList[i].fdSocket, buffer, sizeof(req));

memcpy(&req, buffer, sizeof(req));

/* Reallouer selon taille specifiee dans en-tete */

buffer = realloc(buffer, sizeof(req) + req.sizePayload);

/* Lire les donnees */

octetsTraites = read(reqList[i].fdSocket, buffer + sizeof(req), req.sizePayload);

Question pi`ege : combien d’octets doit-on allouer pour une chaˆıne de 8 caract`eres ?

SETR – GIF-3004 (U. Laval) Programmation syst`eme C. Gagn´e 6 / 14

(7)

Utilisation de select

Supposons plusieurs sockets ouverts et qui ´ecoutent les requˆetes, avec certains pouvant ˆ

etre prˆets `a ˆetre lus

I Autrement dit, les clients attach´es `a certains sockets ont envoy´e une requˆete Mais vous ne savez pas lesquels, comment proc´eder ?

I Solution simple : faire une boucle

struct msgReq req;

char* buffer = malloc(sizeof(req));

for(int i=0; i<NOMBRE_SOCKETS; i++) {

int octetsLus = read(tableauSockets[i], buffer, sizeof(req));

if(octetsLus > 0) {

/* On a recu un message, on peut le traiter */

} }

I Petit«etail»:read()(etopen()etwrite()) sont bloquants

I Donc tant que le premier client n’aura pas envoy´e une requˆete, aucun client ne sera trait´e

(8)

Utilisation de select (2

e

option)

Pour les petits malins qui vont lire le manuel de open()

O NONBLOCK or O NDELAY When possible, the file is opened in nonblocking mode.

Neither the open() nor any subsequent operations on the file descriptor which is re- turned will cause the calling process to wait.

Pas fou, mais il faut prendre en consid´eration queread() est un appel syst`eme.

I Chaqueread()coˆute en moyenne 12 000 cycles processeur (≈3800 instructions), sans compter les autres pertes (´evictions du cache, du TLB, etc.)

SETR – GIF-3004 (U. Laval) Programmation syst`eme C. Gagn´e 8 / 14

(9)

Utilisation de select (3

e

option)

La«bonne» solution : demander au syst`eme de faire le travail pour nous.

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

I readfds,writefdsetexceptfds sont des structures similaires qui surveillent diff´erents

´ev´enements apparaissant sur un ensemble de descripteurs

F Ces structures peuvent ˆetre initialis´ees et utilis´ees en utilisant les macrosFD_ZERO,FD_SETet FD_ISSET

I timeoutest une structure permettant d’informerselectdu temps que l’on est prˆet `a attendre avant qu’un ´ev´enement se produise

I nfdsest particulier : valeur du plus haut descripteur de fichier donn´e dans les ensembles readfds,writefdsetexceptfds, plus 1

(10)

Exemple d’utilisation de select

fd_set setSockets;

/* Indiquer le temps pret a attendre */

structtimeval tvS;

tvS.tv_set= 0;

tvS.tv_usec=SLEEP_TIME;

intnfdsSockets= 0;

FD_ZERO(&setSockets);

for(inti=0; i<maxlen; i++) {

/* Verifie si le socket peut recevoir des requetes */

if(reqList[i].status==REQ_STATUS_LISTEN) { /* Si oui, ajout a l'ensemble */

FD_SET(reqList[i].fdSocket,&setSockets);

/* Verifie si le descripteur est plus haut que le maximum actuel */

nfdsSockets=(nfdsSockets<=reqList[i].fdSocket)?reqList[i].fdSocket+1 :nfdsSockets;

} }

if(nfdsSockets> 0) {

/* Au moins un socket en attente d'une requete, on fait select en lecture */

ints=select(nfdsSockets,&setSockets,NULL,NULL,&tvS);

if(s> 0) {

for(inti=0; i<maxlen;++i) {

/* Trouver sockets prets a etre lus */

if(reqList[i].status==REQ_STATUS_LISTEN&&FD_ISSET(reqList[i].fdSocket,&setSockets)) { /* On traite la lecture ici */

} } } }

SETR – GIF-3004 (U. Laval) Programmation syst`eme C. Gagn´e 10 / 14

(11)

Utilisation de select

select peut ˆetre utilis´e sur autre chose que des sockets (par exemple les pipes !) Des alternatives plus modernes comme poll ouepollexistent, mais le principe est similaire, etselect est la solution la plus portable

I Si vous n’ˆetes pas Google ou Facebook et que vous n’op´erez pas de millions de serveurs, l’´economie d’´energie est n´egligeable et selectest un bon choix

(12)

Copier des donn´ ees

En C, pas de constructeurs de copie ou d’autres fonctions de«haut niveau», tout se passe via des fonctions de copie

I Fonction g´en´erale

void *memcpy(void *dest, const void *src, size_t n);

F Copie litt´eralement n’importe quoi : n’a aucune notion de ce qu’il copie

F Pratique, mais dangereux...

I Pour une chaˆıne de caract`eres

char *strcpy(char *dest, const char *src);

F S’arrˆete lorsqu’elle rencontre un caract`ere nul (0x00)

I Mieux

char *strncpy(char *dest, const char *src, size_t n);

SETR – GIF-3004 (U. Laval) Programmation syst`eme C. Gagn´e 12 / 14

(13)

Copier des donn´ ees

En C, pas de notion de type pour la copie, tout est une suite d’octets A quelques d´` etails pr`es, les codes suivants font la mˆeme chose

I Approche la plus«ure», mais ne pas oublier de d´esallouer le buffer

struct msgReq req;

char* buffer = malloc(sizeof(req));

octetsTraites = read(reqList[i].fdSocket, buffer, sizeof(req));

memcpy(&req, buffer, sizeof(req));

I Assignation d’un buffer comme contenu : valide mais plus propice `a bogues (copier pointeur au lieu du contenu)

struct msgReq req;

char* buffer = malloc(sizeof(req));

octetsTraites = read(reqList[i].fdSocket, buffer, sizeof(req));

req = *((struct msgReq*)buffer);

I Utilisation directe, valable si valeur lue non utilis´ee apr`es fin de la fonction

struct msgReq req;

octetsTraites = read(reqList[i].fdSocket, (char*)&req, sizeof(req));

(14)

Objectifs de la s´ eance

C’est le moment de poser vos questions reli´ees au laboratoire 2

I Vous devriez avoir un environnement op´erationnel

I Et une certaine id´ee de ce qu’il faut faire

Nous sommes disponibles pour r´epondre `a vos questions techniques

I Mais pas pour faire le travail `a votre place !

SETR – GIF-3004 (U. Laval) Programmation syst`eme C. Gagn´e 14 / 14

Références

Documents relatifs

◊ le courant qui sort du grillage à l'infini est I c dans le schéma de gauche et -I c dans le schéma de droite, cela donne bien un total nul comme dans le réseau réel.. • Le

On peut utiliser sans justification les faits que le groupe S n est engendré par les transpositions et que les transpositions sont toutes conjuguées (en d’autres termes, pour

L’archive ouverte pluridisciplinaire HAL, est destinée au dépôt et à la diffusion de documents scientifiques de niveau recherche, publiés ou non, émanant des

 Une expression expression peut être une valeur, une variable ou une opération constituée par des valeurs, des constantes et des variables reliées entre elles par des

Les instructions (corps de la boucle) sont exécutées tant que la condition est vraie, on sort de la boucle dès que la condition devient fausse.  dans la boucle while le test

 Les variables déclarées au début de la fonction principale main ne sont pas des variables globales, mais elles sont locales à main.  Une variable locale cache la variable

 La déclaration d’un tableau provoque la réservation automatique par le La déclaration d’un tableau provoque la réservation automatique par le compilateur d’une zone

For functions meromorphic in an angle the results are much less precise but with the aid of modified Nevanlinna theory we can say a good deal in this case also..