0% ont trouvé ce document utile (0 vote)
4 vues63 pages

Introduction à RT-POSIX et systèmes temps-réels

Transféré par

abyoussefi
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
4 vues63 pages

Introduction à RT-POSIX et systèmes temps-réels

Transféré par

abyoussefi
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Cours

 RT-­‐POSIX  

E0enne  Borde,  
Bertrand  Dupouy  
Pourquoi  POSIX?  
• Qu’est-­‐ce  que  POSIX?  
– Un  standard  IEEE  
– Une  API,  et  donc  une  librairie  
– Fournis  par  le  Système  d’Exploita0on  (SE)  
– Ges0on  des  processus  et  threads  (processus  légers)  
• Alors,  pourquoi  POSIX?  
– Portabilité:  Portable  Opera0ng  System  Interface  
– Une  applica0on  u0lisant  l’API  POSIX  pourra  être  
exécutée  sur  n’importe  quel  SE  qui  implémente  POSIX  
(Linux,  …  mais  aussi  …  INTEGRITY,  LynxOS,  RTEMS,  
VxWorks,  …)  
 
Pourquoi  RT-­‐POSIX?  
• De  plus  en  plus  d’applica0ons  temps-­‐réels  
• SE  classique  non-­‐adaptés  à  un  ges0on  
deterministe  du  temps  
• Une  des  raison  de  sa  popularité  est  d’être  
u0lisable  sur  une  machine  Linux,  puis  portable  
sur  des  OS  propriétaires  

En  bref,  POSIX=API  système…  Va-­‐t-­‐on  parler  


1h30  d’une  API?  …  
Plan  du  cours  
• Introduc)on  aux  systèmes  temps-­‐réels  d’un  
point  de  vue  SE.  
• Facteurs  intervenants  dans  la  ges0on  du  
temps,  toujours  d’un  point  de  vu  SE  
• Proposi0ons  RT-­‐POSIX  (l’API)  
• Patron  d’implémenta0on  pour  modèle  
d’ordonancement  théorique  
Les  composants  d’un  SE  
• Ges0onnaire  de  fichiers  
• Ges0on  de  la  mémoire  
• Ges0on  de  l’accès  aux  resources  
– de  calcul  (processeur)  
– entrées/sor0es  (périphériques)  

Ges0on  du  temps  dans  un  SE?  …  


Ordonnaceur!  ...  Mais  pas  seulement  
Le  temps  dans  un  SE  classique  
• Un  système  d’exploita0on  classique  doit  
organiser  et  op0miser  l'u0lisa0on  des  
ressources  de  façon  à  ce  que  l'accès  à  ces  
ressources  soit  équitable.    
• Un  SE  n'a  donc  pour  seule  contrainte  de  
temps  que  celle  d'un  temps  de  réponse  
sa2sfaisant  pour  les  applica0ons  

è  Critère  de  qualité:  temps  de  réponse.  


Le  temps  dans  un  SE  classique  
• Le  SE  ne  prend  pas  en  compte  les  contraintes  
d’échéances  dans  l’ordonnacement  
• L’ordonnacement,  basé  sur  des  priorités  
dynamiquement  agribuées,  dépend  du  type  
d'événement  agendu/reçu  et  de  la  
consomma2on  de  temps  cpu  des  processus  
(vieillissement)  
• L’ordonnancement  ne  prend  pas  en  compte  
l’échéance  de  réalisa0on  de  la  tâche.    
Rappels  sur  les  Systèmes  Temps-­‐Réels  
(STR)  
• Objec0f  des  STR  :    
– déterminisme  temporel  (en  plus  du  déterminisme  logique  où  les  
mêmes  données  en  entrées  donnent  les  mêmes  résultats):    
• Respect  des  échéances,  prédic0bilité  :  répondre  à  des  contraintes  temporelles  
(sur  le  début  et/ou  la  fin  des  ac0vités),    
• Résultat  correct  =  résultat  exact  ...  et  fourni  à  la  date  voulue  
• Exemples  :    
– Logiciel  de  lecture  vidéo:  synchronisa0on  des  flux  audio/vidéo,  
– Logiciel  de  déclenchement  d’un  airbag  de  voiture  

• Note1  :  ces  exemples  introduisent  une  no0on  de  cri2cité  et  de  
précision  des  contraintes  temporelles  
• Note2  :  POSIX=interface  de  programma0on  seulement,  donc  sa  
capacité  à  être  u0lisé  dans  un  domaine  d’applica0on  dépend  de  
l’implémenta)on  de  cege  API  et  de  la  façon  dont  elle  est  u)lisée.  
Synthèse  des  différences  SE/STR  
SE  
– Les  performances  sont  jugées  suivant  le  rendement  :  exécuter  le  plus  de  
tâches  possibles,  le  plus  rapidement  possible,    
 
C'est  le  SE  qui  décide  de  la  dynamique  d'exécu2on  
(CONTRAINTES  LOGICIELLES)    
 
STR    
– Le  critère  de  performance  est  le  suivant  :  respect  de  toutes  ou  d'une  par2e  
(en  cas  de  surcharge)  des  échéances,  qu'elles  soient  périodiques  ou  non.  Si  on  
exige  le  respect  de  toutes  les  échéances,  on  parle  de  TR  dur,  sinon  TR  souple  
(mou,  son).    
 
C'est  l'environnement  extérieur  qui  impose  la  dynamique  d’exécu2on  
(CONTRAINTES  PHYSIQUES)    
Le  temps  dans  les  STR  
• Real-­‐0me  compu0ng  is  not  fast  compu0ng:  
FAUX   VRAI  

Informa0on  soumise  à  des  conraintes   Informa0on  soumise  à  des  contraintes  


temps-­‐réel  =  informa0on  à  obtenir   temps  réel  =  informa0on  à  obtenir  avant  
rapidement   une  certaine  date  
   
Traitement  temps  réel  =  traitement  à   Traitement  temps-­‐réel  =  traitement  à  
effectuer  rapidement   effectuer  avant  une  certaine  date  

• Ex:  Airbag…  
Tâche  urgente  et  tâche  cri0que  
• Chaque  tâche  a  un  degré  :  
– d'urgence,  lié  à  la  date  de  son  échéance;  
– de  cri0cité,  lié  à  son  importance  rela0ve.    
 
• Mais  :  une  tâche  très  cri0que  peu  avoir  de  faibles  contraintes  de  
temps  et  une  tâche  peu  cri0que  de  fortes  contraintes  de  temps  …  
è  Pb.  des  ordonnancements  du  type  RMS  où  le  seul  critère  est  la  
période  des  tâches  ou  EDF  où  le  seul  critère  est  la  deadline  des  
tâches.    

• Comment  conjuguer  ces  deux  critères  ?  c'est  à  dire  comment  


refléter  l'urgence  ET  l'importance  des  tâches  ?  
– MUF  (Maximum  Urgency  First),    
– Systèmes  pari0onés  (ARINC653)  
– Mixed  cri0cality  
Types  d’ordonnancement  TR  
• Hors  ligne  
– l’ordonnancement  est  calculé  a  priori,  c’est  à  dire  avant  
l’exécu0on  (0me  driven  scheduling),  l’ordonnanceur  se  réduit  à  
un  séquenceur.  
• En–ligne  :    
– l’ordonnancement  est  décidé  à  l’exécu0on,  la  détec0on  des  
surcharges  est  plus  difficile…  Quelles  surcharges  d’ailleurs?  
• Ne  pas  confondre  :    
– Hors  ligne  /  en  ligne.  
– Priorité  fixe  /  priorité  dynamique.  
– Préemp0f  /  non  préemp0f.  
– Priorité  /  cri0cité.  
Réalisa0ons  d’applica0ons  TR  
• Déterminisme  temporel,  il  faut  donc  :    
– maitriser  les  temps  d’exécu0on  (début/fin),    
– garan0r  l’ordre  d’exécu0on  des  fonc0ons  (contraintes  de  précédence),    
– prouver  l’ordonnnançabilité,  donc  u0liser  des  techniques  d’ordonnancement  et  de  
ges0on  de  la  concurrence  éprouvées,    
– ne  pas  se  contenter  des  tests  qui  ne  sont  pas  toujours  assez  près  des  condi0ons  réelles  
(cf.  accumula0on  des  dérives  d’horloge  dans  une  applica0on  répar0e  très  longue)    

• Sûreté  de  fonc0onnement,  il  faut  pouvoir  :    


– Détecter  les  erreurs  (y  compris  temporelles,  dépassement  d’échéances  par  exemple)  
– Confiner  les  erreurs,  càd  éviter  leur  propagag0on  (par00onnement  spa0o-­‐temporelle,  
voir  cours  sur  ARINC)  
– Corriger  les  erreurs  

Les  Systèmes  d’Exploita0on  Temps  Réel  fournissent  les    


«  briques  »  de  base  (API);    
ce  n’est  que  par  un  usage  rigoureux  de  ces  briques  de  base    
qu’on  ob0ent  les  garan2es  désirées  
Plan  du  cours  
• Introduc0on  aux  systèmes  temps-­‐réels  d’un  
point  de  vue  SE.  
• Facteurs  intervenants  dans  la  ges)on  du  
temps,  toujours  d’un  point  de  vu  SE  
• Proposi0ons  RT-­‐POSIX  (l’API)  
• Patron  d’implémenta0on  pour  modèle  
d’ordonancement  théorique  
Facteurs  intervenants  dans  la  ges0on  
du  temps  
• Facteurs  logiciels  :  
– synchronisa0on  (partage  de  ressources),    
– Algorithmes  de  ges0on  de  la  mémoire  :  (GC,  pagina0on,  caches,  mul0-­‐coeurs),  
– entrées-­‐sor0es  (pas  de  priorité),  
– ges0on  des  disques  (algorithme  de  parcours,  alloca0on),  
• Facteurs  matériels  :  
– ges0on  des  interrup0ons,  
– mémoires  caches,    
– pipe-­‐line  

• et  encore  :    
– format  de  l’exécutable  (édi0on  de  liens  sta0que/dynamique)  
– protocole  réseau  pour  les  SE  répar0s    
– la  mesure  doit-­‐elle  se  faire  dans  la  configura0on  la  plus  défavorable  (worst  case  
execu0on  0me,  WCET)  ?  
Temps-­‐réel,  ges0on  de  la  mémoire,  
 et  codage  
• Ges0on  sta0que  :    
– le  nombre,  la  taille  et  l’emplacement  des  objets  sont  connus,  ou  
bornés,  lorsque  l’applica0on  est  lancée,    
– avantage  :  accès  aux  objets  en  temps  constant  et  faible  (objets  
implantés  sous  forme  de  tableaux)    
– inconvénient  :  pas  flexible    
• Ges0on  dynamique  :    
– avantage  :  souplesse    
– inconvénients  :    
• temps  d’accès  et  d’alloca0on  difficile  à  prédire  ou  à  borner  
• déterminisme  de  la  désalloca0on    
• Pra$que  des  systèmes  fortements  cri$ques:  pas  d’alloca$on  
dynamique  de  mémoire.  
 
Temps-­‐réel,  ges0on  de  la  mémoire,  
 et  pagina0on  
• Temps  d’accès  aux  informa0ons  difficile  à  
prédire  
• Ce  temps  d’accès  peut  être  très  long  :  accès  
disques  possibles  
• la  pagina0on  est  donc  peu  u0lisée  en  TR,  ou  
bien  avec  des  mécanismes  de  verrouillage  des  
pages  en  mémoire  pour  les  applica0ons  
cri0ques  
Temps  réels  et  mémoires  caches  
• Avantage  de  l’u0lisa0on  de  caches  :    
– diminue  le  temps  d’exécu0on  des  tâches  de  manière  
probabiliste  (cf.  hit  ra0o)  

• Inconvénients  :    
– augmenta0on  du  temps  de  changement  de  contexte  
(réini0alisa0on  des  caches)  si  les  espaces  d’adressage  sont  
séparés,  d’où  u0lisa0on  de  threads  dans  les  STR  
– moins  de  prédic0bilité,  il  existe  diverses  méthodes  d’es0ma0on  
du  comportement  des  caches  

• Techniques  mises  en  œuvre  :  verrouillage,  partage  des  


caches  
Autres  éléments  à  prendre  en  compte  
• Intérrup0ons  
• Ges0on  des  entrée/sor0es  
• Bus  d’interconnec0on  (mul0-­‐coeur)  

En  résumé,  les  techniques  d’accéléra$on  matériel:  


 Diminuent  le  temps  moyen  d’exécu$on  d’un  
 programme  
 Augmentent  la  disper$on  des  temps  
 d’exécu$on  (soit  la  différence  entre  
 BCET  et  WCET)  
Plan  du  cours  
• Introduc0on  aux  systèmes  temps-­‐réels  d’un  
point  de  vue  SE.  
• Facteurs  intervenants  dans  la  ges0on  du  
temps,  toujours  d’un  point  de  vu  SE  
• Proposi)ons  RT-­‐POSIX  (l’API)  
• Patron  d’implémenta0on  pour  modèle  
d’ordonancement  théorique  
RT-­‐POSIX,  l’API  
• L’API  POSIX  permet  de  gérer  des  threads,  c’est-­‐à-­‐dire  
des  processus  léger  
– Partagent  le  même  espace  d’adressage  (celui  du  
processus  qui  les  crée)  
– Changement  de  contextes  plus  court  
• Norme  POSIX  1003.4  pour  la  portabilité  des  
applica0ons  TR  :    
– définit  une  interface  standard  entre  l'applica0on  et  le  
système  
– ne  spécifie  PAS  l'implanta0on,  mais  propose  des  ou0ls  
de  mesure  des  performances  
– peu  de  fonc0onnalités  obligatoires  
RT-­‐POSIX,  l’API  
• POSIX  4  définit  la  panoplie  TR  minimale,  4a  les  threads  et  4b  les  
extensions.  POSIX  4b  propose  des  ou0ls  tels  que  :    
– l'accès  direct  aux  interrup0ons  depuis  les  applica0ons,  
– l'ordonnancement  "serveur  sporadique”,  
– les  ordonnancements:  SCHED_FIFO,  SCHED_RR,  SCHED_OTHER  
– une  fonc0on  qui  permet  à  un  thread  de  suivre  la  consomma0on  cpu  d'un  
autre  thread,  
– les  files  de  message  (mq_open,  mq_receive,  ...)  
– Les  signaux  temps  réel    
• Pour  vérifier  si  la  par0e  de  la  norme  que  l'on  veut  u0liser  est  bien  
implantée,  u0liser  ifdef  et  error  pour  être  aver0  par  le  préprocesseur,  ou  
sysconf  pour  un  message  à  l'exécu0on:    
#include <unistd.h>!
#ifndef _POSIX_PRIORITY_SCHEDULING !
#error POSIX : pas d'ordonnancement TR !
#endif !
POSIX,  créa0on  de  threads  
• Les  threads  peuvent  être  créés  à  par0r  de  la  librairie  pthread:  
– Header:  pthread.h  
– Link  op0on  for  gcc:  gcc  –o  app  app.c  –lpthread  
• La  fonc0on  pour  créer  un  thread  est  pthread_create:    
int pthread_create(pthread_t * thread,
pthread_attr_t * attr, void *(*start_routine)(void
*), void * arg); !
– thread  est  une  structure  passé  par  adresse,  et  ini0alisée  par  pthread-­‐create;  
on  pourra  la  réu0liser  avec  d’autres  fonc0ons  de  l’API  POSIX  
– agr  est  un  pointeur  (peut  être  NULL)  vers  une  structure  pthread_agr_t.  Cege  
structure  peut  être  ini0alisé  en  u0lisant  les  fonc0ons  du  type  
pthread_agr_*().  Elle  peut  être  u0lisé  pour  megre  à  jour  un  ensemble  varié  
de  propriétés  d’un  thread  (detach  policy,  scheduling  policy,  etc.)    
– start_rou0ne  est  un  pointeur  de  fonc0on  qui  désigne  la  fonc0on  qui  sera  
exécuté  par  le  thread.  Le  thread  est  détruit  à  la  fin  de  l’exécu0on  de  cege  
fonc0on.    
– arg  est  un  argument  passé  à  la  fonc0on  start_rou0ne.  

 
Partage  de  l’espace  d’adressage  

Espace  d’adressage  du  processus   Espace  d’adressage  du  processus  

Thread   Thread   Thread  


principale   principale    
     
Execute  la   Execute  la   Execute  la  
fonc0on  main   fonc0on  main   fonc0on  fonc  

Créa0on  d’un  nouveau  thread  


Lancement  fu  programme,  
ou  fork   Seule  la  pile  (stack)  est  spécifique  à  chaque  thread  
Exemple  de  code  
#include <pthread.h>!
void *thread(void *data) { !
int i; !
for (i = 0; i < 100; i++) {!
printf(« Hello world from thread »);!
}!
} !
int main(void) { !
pthread_t th;!
pthread_create(& th, NULL, thread, NULL); !
} !
!
Affichage:  rien  le  plus  souvent…  
Agendre  la  fin  d’un  thread  
• Quand  la  fonc0on  main  se  termine,  tous  les  threads  créés  par  le  processus  
sont  détruits  
!
• pthread_join(pthread_t thread, void **value_ptr)  est  la  fonc0on  à  
u0liser  pour  suspendre  un  thread  (passe  dans  l’état  bloqué)  jusqu’à  ce  
qu’un  autre  thread  termine  son  exécu0on  
• int pthread_t thread, void **value_ptr!
– thread:  l’iden$fiant  du  thread  don’t  on  aIend  la  terminaison  
– value_ptr,  recoit  la  valeur  passé  en  argument  de  l’appel  à  void  
pthread_exit(void  *value_ptr);  dans  le  thread  agendu.  

• Les  threads  peuvent  aussi  être  détachés  (et  ne  plus  être  “joinable”):  
– int pthread_detach(pthread_t thread);!
– Les  threads  détachés  con0nuent  leur  exécu0on  après  la  fin  du  main  si  
le  main  termine  en  appelant  pthread_exit()  plutôt  que  exit  ou  return.  
Exemple  de  code  
#include <pthread.h>!
void *thread(void *data) { !
int i; !
for (i = 0; i < 100; i++) {!
printf(« Hello world from thread »);!
}!
} !
int main(void) { !
pthread_t th;!
pthread_create(& th, NULL, thread, NULL); !
pthread_join(th, NULL);!
} !
 
Affichage:  Hello  world  from  thread  Hello  world  from  thread  Hello  
world  from  thread  Hello  world  from  thread  Hello  world  from  
thread  Hello  world  from  thread  Hello  world  from  thread  etc….  
Annula0on  d’un  thread  (par  l’exemple)  
#include <pthread.h> !
void *thread(void *data) { !
while(1) { !
printf(« Hello world from thread »); !
} !
} !
int main(void) { !
pthread_t th;!
pthread_create(& th, NULL, thread, NULL); !
sleep(1); !
pthread_cancel(th); !
pthread_join(th, NULL); !
return 0; !
} !
 
Peu  u0lisé  dans  un  système  temps-­‐réel,  sauf  en  cas  de  “reconfigura0on”  
(e.g.  Traitement  d’erreurs,  changement  de  mode).  
L’ordonnancement  en  POSIX  
• Ordonnancement  préemp0f  à  priorités  fixes  
– 32  niveaux  de  priorité  doivent  être  proposés  
• les  poli0ques  de  ges0on  des  files  d'agentes  associées  à  ces  
priorités  sont  :  FIFO,  RR,  OTHERS…  “Within  priori0es”  
• seul  l'u0lisateur  privilégié  (root)  peut  accéder  à  ce  service  
d'ordonnancement  pour  choisir  FIFO  ou  OTHERS  
• On  peut  aussi  megre  à  jour  la  priorité  d’un  thread  via  ses  
agributs  (voir  exemple  ci-­‐après)  

Note:  toutes  ces  fonc0ons  sont  applicables  qu’aux  threads;  


quand  on  peut  appliquer  des  fonc0ons  POSIX  aux  processus,  
le  nom  de  ces  fonc0on  ne  con0ent  pas  le  mnémonique  
pthread  
Exemple:  mise  à  jour  de  la  priorité  
d’un  thread  
#include <sched.h>!
pthread_t tid;!
pthread_attr_t attr;!
struct sched_param param;!
...!
pthread_attr_init (&attr) !
!
/******** politique d'ordonnancement ********/ !
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);!
!
/******** priorité du thread ********/!
param.sched_priority = 5;!
pthread_attr_setschedparam (&attr, &param);!
/******** création du thread ********/!
pthread_create (&tid, &attr, fonc, NULL);!
 
Faire  agen0on  aux  détails  de  la  
norme…  
int pthread_attr_setinheritsched(pthread_attr_t *attr,!
! ! ! ! !int inheritsched);!
!
• When  the  agribute  objects  are  used  by  pthread_create(),  the  inheritsched  
agribute  determines  how  the  other  scheduling  agributes  of  the  created  
thread  are  to  be  set:  

– PTHREAD_INHERIT_SCHED,  by  default  


• Specifies  that  the  scheduling  policy  and  associated  agributes  are  to  be  inherited  
from  the  crea0ng  thread,  and  the  scheduling  agributes  in  this  aIr  argument  are  to  
be  ignored.  
– PTHREAD_EXPLICIT_SCHED  
• Specifies  that  the  scheduling  policy  and  associated  agributes  are  to  be  set  to  the  
corresponding  values  from  this  agribute  object.!
POSIX  et  ordonnancement  
(Divers)  
•  Connaitre  les  niveaux  de  priorité  authorisés  (dépend  
de  l’implémenta0on;  32  niveaux  min.)  
– prio_max  =  sched_get_priority_max(policy);    
– prio_min  =  sched_get_priority_min(policy);    

• Parameter  policy:  SCHED_FIFO,  SCHED_RR,  


SCHED_SPORADIC,  SCHED_OTHER  

• Connaitre  la  valeur  du  quantum  pour  la  poli0que  RR  


(pour  le  processus  courant)  
– struct timespec qtm; !
– sched_rr_get_interval(0,&qtm); !
Mutexes  
• Les  mutex  sont  des0nés  à  la  ges0on  des  accès  
aux  sec0ons  cri0ques  (exclusion  mutuelle)  
– la  file  d'agente  qui  leur  est  associée  est  gérée  par  
ordre  de  priorités  décroissantes;  ils  peuvent  être  
u0lisés  entre  threads  ou  processus,  suivant  les  
op0ons  :    
– pthread_mutex_init(),  
pthread_mutex_lock(),  pthread_mutex_trylock(),  
pthread_mutex_unlock(),  
pthread_mutexattr_*,  …  
Exemple  de  code  
//variable globale!
pthread_mutex_t lock; !
...!
// initialisation du lot de tâches (i.e. avant la création
des threads)!
pthread_mutex_init(& lock, NULL); !
... // création des threads!
!
! // dans les threads concurrents! // dans les threads concurrents!
! ret = pthread_mutex_lock(& lock); ! ret = pthread_mutex_lock(& lock); !
! … // section critique! … // section critique!
ret = pthread_mutex_unlock(& lock);   ret = pthread_mutex_unlock(& lock);  
!
!
// à la fin du processus!
pthread_mutex_destroy(& lock); !
Agributs  des  mutex  
• Le  second  argument  de  pthread_mutex_init()  est  un  
ensemble  d’agributs  spécifiques  aux  mutexes,  et  regroupés  dans  
une  structure  pthread_mutexattr_t.    
• Une  telle  structure  peut-­‐être  ini0alisée  et  manipulé  avec  des  
fonc0ons  du  type  pthread_mutexattr_*()!

• Exemple:    
 
int pthread_mutexattr_settype
(pthread_mutexattr_t *attr, int type); !
 
où  type  peut  prendre  comme  valeur:  PTHREAD_MUTEX_NORMAL,  
PTHREAD_MUTEX_ERRORCHECK,  PTHREAD_MUTEX_RECURSIVE,  
PTHREAD_MUTEX_DEFAULT  
 
Mutexes  pour  le  TR  
• Un  mutex  peut-­‐être  configuré  avec  une  poli0que  
– Poli0que  «  PCP-­‐like  »:  si  un  thread  t1  a  pris  le  verrou  et  bloque  un  thread  t2  
plus  prioritaire,  alors  t1  hérite  de  la  priorité  max  associée  au  verrou.  
pthread_mutex_t lock1;!
pthread_mutexattr_t mutex_attr;!
pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_PROTECT);!
pthread_mutexattr_setprioceiling(&mutex_attr, max_prio-1);!
pthread_mutex_init(&lock1, &mutex_attr);!
 
– Poli0que  «  PIP-­‐like  »:  si  un  thread  t1  a  pris  le  verrou  et  bloque  un  thread  t2  
plus  prioritaire,  alors  t1  hérite  de  la  priorité  de  t1.  
pthread_mutex_t lock1;!
pthread_mutexattr_t mutex_attr;!
pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT);!
pthread_mutex_init(&lock1, &mutex_attr);!
Variables  condi0onnelles  
• Les  variables  condi0onnelles  permegent  de  suspendre  l’exécu0on  d’un  thread  
jusqu’à  ce  qu’une  condi0on  devienne  vrai;  cege  condi0on  est  signalée  par  un  
autre  thread.  

• Ini0alisa0on  
– pthread_cond_t  cond;  
– pthread_cond_init(&  cond,  NULL);    
• Agente:  
– pthread_cond_wait(&  cond,  &  mutex)    
– Toujours  bloquant  
– Le  mutex  passé  en  paramètre  est  sera  libéré  avant  la  mise  en  agente  (de  façon  atomique),  
puis  repris  immédiatement  au  réveil  (trylock)  
• Signalisa0on:  
– A  un  thread  en  agente  (pas  forcément  FIFO):  
pthread_cond_signal(&  cond);    
– A  tous  les  threads  en  agente:    
pthread_cond_broadcast(&  cond);  
– Non  mémorisé  (perdu  si  aucun  thread  en  agente)  
Example  de  code  
/****** variables partagees ******/ !
pthread_mutex_init(&Verrou, NULL); !
pthread_cond_init(&VarCond, NULL); !
// création de threads  

...!
...!
while (...){!
pthread_mutex_lock (&Verrou); !
...!
while (N < Compteur) { !
pthread_mutex_lock(&Verrou);!
pthread_cond_wait(&VarCond,!
Compteur ++;!
&Verrou); !
if (Compteur > N)!
}!
pthread_cond_broadcast(&VarCond);!
printf ("Seuil atteint! "\n); !
pthread_mutex_unlock (&Verrou);!
...!
... !
pthread_mutex_unlock (&Verrou);!
}!
!
 

Vérfifie  qu’un  seuil  est  ageint   Vérfifient  que  le  seuil  soit  ageint  
Signaux  
• Dans  l’implémenta0on  TR  les  différentes  occurrences  d’un  même  
signal  sont  conservées,  le  nombre  de  signaux  reçus  correspond  
toujours  au  nombre  de  signaux  émis.    
– Pas  de  perte  :  ges0on  d'une  liste  de  signaux  en  agente    
– La  priorité  liée  au  signal  est  respectée  dans  la  ges0on  de  la  file  
d'agente    
•  Emission  par  sigqueue,  par  un  0mer,  ou  par  une  fin  d'e/s  
•  Nouveaux  signaux  dans  RT-­‐POSIX:  RTSIG_MAX  signaux,  numérotés  
de  SIGRTMIN  à  SIGRTMAX  (les  signaux  avec  un  plus  pe0t  numéro  sont  
considérés  comme  plus  prioritaires).  La  priorité  rela0ves  des  signaux  RT  et  
standards  est  non-­‐spécifiée  dans  le  standard.  
 
² Sigaction: modifier  le  traitement  associé  à  un  signal  
² sigwaitinfo:  Agendre  un  signal  et  une  info    
² sigtimedwait:  Agendre  d’un  signal  avec  temporisa0on  
Signaux  (émission)  
 
² kill!
² int pthread_kill(pthread_t thread, int sig);!

² sigqueue:  Megre  un  signal  dans  la  file  d’agente  associée  au  processus  
des0nataire    
² int sigqueue(pid_t pid, int sig, const union sigval
value);!
² Paramètre  value:  spécifier  une  donnée  associée  au  signal  
² Défini2on  de  sigval:  
 union sigval {!
int sival_int;!
" " void *sival_ptr;!
};!
² int  pthread_sigqueue(pthread_t  thread,  int  sig,  const  union  sigval  value);!
Signaux  (traitement)  
² sigaction:!
" int sigaction(int sig, const struct sigaction * act, " "
" "struct sigaction * oldact);!
² Paramètre  act:  spécifie  la  nouvelle  ac0on  associé  au  signal  sig;  paramètre  oldact:  si  
non  null,  sert  à  sauvegarder  l’ancienne  ac0on  associé  au  signal  sig.  
!
struct sigaction {!
"

" void (*sa_handler)(int);!


" void (*sa_sigaction)(int, siginfo_t *, void *);!
" sigset_t sa_mask;!
" int sa_flags;!
" void (*sa_restorer)(void);!
"};!
² sa_handler  :  ac0on  associée  au  signal  (éventuellement  SIG_IGN,  SIG_DFL)  
² sa_mask:  ensemble  de  signaux  ignorés  pendant  le  traitement  du  signal  sig  
² Autres  champs:  voir  la  documenta0on  
Différence  signal/sigac0on  
[source:  stackoverflow]  
• Signal  has  a  number  of  undesirable  characteris0cs  that  sigac0on()  avoids  
– The  signal()  func0on  does  not  (necessarily)  block  other  signals  from  arriving  
while  the  current  handler  is  execu0ng;  sigac0on()  can  block  other  signals  un0l  
the  current  handler  returns.  
– The  signal()  func0on  (usually)  resets  the  signal  ac0on  back  to  SIG_DFL  
(default)  for  almost  all  signals.  This  means  that  the  signal()  handler  must  
reinstall  itself  as  its  first  ac0on.  It  also  opens  up  a  window  of  vulnerability  
between  the  0me  when  the  signal  is  detected  and  the  handler  is  reinstalled  
during  which  if  a  second  instance  of  the  signal  arrives,  the  default  behaviour  
(usually  terminate,  some0mes  with  prejudice  -­‐  aka  core  dump)  occurs.  
– The  exact  behaviour  of  signal()  varies  between  systems  —  and  the  standards  
permit  those  varia0ons.  
• The  Linux  man  page  for  signal()  says:  
– The  effects  of  signal()  in  a  mul$-­‐threaded  process  are  unspecified.  
 
CCL:  prefer  sigac$on  to  signal…  
Signaux  (agente)  
²int  sigwait(const  sigset_t  *set,  int  *sig);  
Agendre  un  signal  parmi  set  et  renseigner  le  
signal  reçu  en  ini0alisant  sig.  
²sigwaitinfo:  Agendre  un  signal  parmi  un  
ensemble  set  et  récupèrer  certaines  info  
associées  au  signal  (ex:  pid  du  processus  
émegeur,  numéro  de  signam)  
² sigtimedwait:  Agendre  d’un  signal  avec  
temporisa0on  
Clocks  
² clock_settime:  Ini0aliser  l’horloge  
(CLOCK_MONOTONIC)  
² int  clock_se^me(clockid_t  clk_id,  const  struct  2mespec  
*tp);  
² clock_gettime:  Lire  la  valeur  de  l’horloge    
² int  clock_ge^me(clockid_t  clk_id,  struct  2mespec  *tp);  
² clock_getres:  Lire  la  résolu0on  de  l’horloge    
² int  clock_getres(clockid_t  clk_id,  struct  2mespec  *res);  
² nanosleep:  Sleep  haute  résolu0on    

clk_id  =  CLOCK_REALTIME/CLOCK_MONOTONIC    
 
Timers  
 
² timer_create:  Créa0on  d’un  0mer    
² int  0mer_create(clockid_t  clockid,  struct  sigevent  *sevp,  0mer_t  *  
0merid);  
² Structure  qui  spécifie  comment  l’appelant  sera  no0fié  de  l’échéance  
du  0mer:  le  champ  sigev_no$fy  de  sevp  sert  à  préciser  cela:  
² SIGEV_NONE  à  pas  de  no2f  
² SIGEV_SIGNAL  à  génère  un  signal  vers  le  processus  appelant;  le    champs  
sigev_signo  de  sevp  précise  le  numéro  de  signal;  
² timer_delete:  Destruc0on  d’un  0mer    
² timer_settime:  Armement/désarmement  d’un  0mer    
² timer_gettime:  Lire  le  délai  restant  sur  un  0mer    
² timer_getoverrun:  Lire  le  délai  dépassé  sur  un  0mer    
Sémaphores  (vues  en  cours  de  UNIX)  
Les  sémaphores  sont  l'implanta0on  classique  de  l'ou0l  
défini  par  Dijkstra.  La  file  d'agente  est  gérée  par  ordre  de  
priorités  décroissantes,  les  sémaphores  peuvent  être  
u0lisés  entre  threads  ou  processus,  suivant  les  op0ons.  
² sem_open:  open  and  /  or   ² sem_getvalue:  get  current  
create  a  named  semaphore.     semaphore  count    
² sem_close:  close  a  named   ² sem_wait:  Try  to  lock  the  
semaphore     semaphore.  Wait  otherwise.    
² sem_unlink:  destroy  a  named   ² sem_trywait:  Just  tries  to  
semaphore     lock  the  semaphore,  but  gives  up  
² sem_init:  ini0alize  an   if  the  semaphore  is  already  
unnamed  semaphore     locked.    
² sem_destroy:  destroy  an   ² sem_post:  Release  the  
unnamed  semaphore     semaphore.    
Files  de  messages  
Elles  sont  similaires  à  ceux  proposés  par  les  IPC  System  V,  
mais  à  chaque  message  est  associée  une  priorité.  Le  problème  
de  l'inversion  de  priorité  n'est  pas  géré  :    

² mq_close:  fermer  une  file  de  messages    


² mq_getattr:  récupérér  les  caractéris0ques  d’une  file  de  
messages    
² mq_open:  ouvrir  une  file  de  message    
² mq_receive:  extraire  un  message  d’une  file    
² mq_send:  déposer  un  message  dans  une  file    
² mq_setattr:  changer  les  agributs  d’une  file    
² mq_unlink:  détruire  une  file  de  messages    
Plan  du  cours  
• Introduc0on  aux  systèmes  temps-­‐réels  d’un  
point  de  vue  SE.  
• Facteurs  intervenants  dans  la  ges0on  du  
temps,  toujours  d’un  point  de  vu  SE  
• Proposi0ons  RT-­‐POSIX  (l’API)  
• Patron  d’implémenta)on  pour  modèle  
d’ordonancement  théorique  
Ordonnancement  RMS  
/*!
* scenario: !
* "- 2 periodic threads T1 (period=1000 ms) and T2 (period=2000 ms);!
* "- 1 sporadic thread T3 (period=3000 ms);!
* "- 1 global variable gv (integer); protected with PCP;!
* "- T1 increments gv; T2 and T3 displays gv;!
* "- Scheduling policy is RMS.!
*/!
!
 
Ordonnancement  RMS  
int main()!
{!
...!
pthread_create(&tid1, &attr1, (void* (*)(void*))body_of_T1, NULL);!
pthread_create(&tid2, &attr2, (void* (*)(void*))body_of_T2, NULL);!
pthread_create(&tid3, &attr3, (void* (*)(void*))body_of_T3, NULL);!
}!
!
void body_of_T1()!
{…}!
void body_of_T2()!
{…}!
void body_of_T3()!
{…}!
!

Que  manque-­‐t-­‐il?  
Ordonnancement  RMS  
int main()!
{!
...!
pthread_create(&tid1, &attr1, (void* (*)(void*))body_of_T1, NULL);!
pthread_create(&tid2, &attr2, (void* (*)(void*))body_of_T2, NULL);!
pthread_create(&tid3, &attr3, (void* (*)(void*))body_of_T3, NULL);!
!
// wait for threads to finish (otherwise the process terminates!
// immediately)!
pthread_join(tid1, NULL);!
pthread_join(tid2, NULL);!
pthread_join(tid3, NULL);!
}!
!
void body_of_T1()!
{…}!
void body_of_T2()!
{…}!
void body_of_T3()!
{…}!
Ordonnancement  RMS  
void body_of_T1()!
{!
unsigned int iter=0;!
while(1)!
{!
iter++;!
printf("Executing T1 iter %d\n", iter);!
// Compute next dispatch time!
clock_gettime(CLOCK_REALTIME, &T1_timer); !
T1_timer.tv_sec = T1_timer.tv_sec+PERIODT1_s; !
T1_timer.tv_nsec = T1_timer.tv_nsec;!
!
// T1‘s code executed here!
!
// Wait for next dispatch time!
pthread_mutex_lock (&T1_mutex);!
pthread_cond_timedwait (&T1_cond, &T1_mutex, &T1_timer);!
pthread_mutex_unlock (&T1_mutex);!
!
!
Problème?  …  
Départ  synchronisé  des  threads  

• Agendre  dans  le  main  que  tous  les  threads  


soient  ini0alisés  
• Agendre  dans  les  threads  que  le  main  donne  
le  signal  (pas  forcément  signal  au  sens  SE)  de  
départ  
• U0liser  des  dates  de  réveil  absolues  
Agendre  dans  le  main  que  tous  les  
threads  soient  synchro  
int main()!
{!
...!
pthread_create(&tid3, &attr3, (void* (*)(void*))body_of_T3, NULL);!
// wait a bit for the end of the threads creation before to release them;!
sleep(2); "!
clock_gettime(CLOCK_REALTIME, &init_time);!
pthread_cond_broadcast(&threads_init_cond);!
…!
}!
!
void body_of_T1()!
{!
// wait all threads have been created and initialized!
pthread_mutex_init(&T1_mutex, NULL);!
pthread_cond_init (&T1_cond, NULL);!
pthread_mutex_lock(&threads_init_mutex);!
pthread_cond_wait(&threads_init_cond, &threads_init_mutex);!
pthread_mutex_unlock(&threads_init_mutex);  
           …  
}  
U0liser  des  dates  de  réveil  absolues  
!
void body_of_T1()!
{!
...!
unsigned int iter=0;!
while(1)!
{!
iter++;!
T1_timer.tv_sec = init_time.tv_sec+iter*PERIODT1_s; !
T1_timer.tv_nsec = init_time.tv_nsec;!
!
...!
!
pthread_mutex_lock (&T1_mutex);!
pthread_cond_timedwait (&T1_cond, &T1_mutex, &T1_timer);!
pthread_mutex_unlock (&T1_mutex);!
}  
}  
Notes  à  propos  de  la  solu0ons  
• Devrait  être  généralisée  pour  un  ensemble  de  
N  threads,  en  fournissant  une  API  de  plus  haut  
niveau:  Middleware.  

• Ce  n’est  qu’une  solu0on  possible,  il  en  existe  


d’autres,  peut-­‐être  mieux…  
– Notamment  basées  sur  les  signaux/0mers!  
Synchro  PCP  
• Directement  fourni  par  POSIX  
!
#define PRIOT1 5;!
!
pthread_mutex_t mutex;!
pthread_mutexattr_t mutex_attr;!
!
int main()!
{!
pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_PROTECT);!
pthread_mutexattr_setprioceiling(&mutex_attr, PRIOT1);!
pthread_mutex_init(&mutex, &mutex_attr);!
...!
}!
Sporadic  server  
• Directement  fourni  par  POSIX  (SCHED_SPORADIC):  
– Init  
!
int main()!
{!
/****** Initialisation des priorites ******/!
/****** Initialisations de la periode et du budget ******/!
/****** a 1/2 seconde et 1/4 seconde ******/!
#define HIGH_PRIORITY 150!
#define LOW_PRIORITY 100!
schedparam.ss_replenish_period.tv_nsec = 500000000;!
schedparam.ss_initial_budget.tv_nsec = 250000000;!
schedparam.sched_priority = HIGH_PRIORITY;!
schedparam.ss_low_priority = LOW_PRIORITY;!
…!
}!
!
 
Sporadic  server  
Dans  le  thread  serveur  sporadic,  on  simule  le  comportement  (1/3):    
"!
"/********!
"Boucle pour voir diminuer la priorite !
"**********/!
"for ( ; ; ) {!
" "if ( schedparam.sched_priority != LOW_PRIORITY )!
" " "continue;!
" "priority = schedparam.sched_priority;!
" "sprintf( buffer, "-nouvelle priorite = %d", priority );!
" "print_current_time( buffer );!
" "/********** !
" "L'appel a lock va augmenter la priorite!
" "**********/!
" "puts( "Verrou va etre pris" );!
" "pthread_mutex_lock( &mutex );!
" "priority = schedparam.sched_priority;!
" "sprintf( buffer, "-nouvelle priorite = %d", priority );!
" "print_current_time( buffer );!
" "break;!
"}  
Sporadic  server  
Dans  le  thread  serveur  sporadic,  on  simule  le  comportement  (2/3):    
"!
"/*****!
"Attendre pour voir le budget etre re-alimente!
"**********/!
"for ( ; ; ) {!
" "if (schedparam.sched_priority == HIGH_PRIORITY )!
" " "break!
"}!
!
"priority = schedparam.sched_priority;!
"sprintf( buffer, "-nouvelle priorite = %d", priority );!
"print_current_time( buffer );  
Sporadic  server  
Dans  le  thread  serveur  sporadic,  on  simule  le  comportement  (3/3):    
"!
"/***** !
"Le unlock doit faire descendre la priorite !
"**********/!
!
"puts( " On va rendre le verrou" );!
"pthread_mutex_unlock( &mutex );!
"priority = schedparam.sched_priority;!
"sprintf( buffer, "-nouvelle priorite = %d", priority );!
"print_current_time( buffer );!
"for ( ; ; ) {!
" "if ( schedparam.sched_priority == LOW_PRIORITY )!
" " "break;!
"}!
"priority = schedparam.sched_priority;!
"sprintf( buffer, "-nouvelle priorite = %d", priority );!
"print_current_time( buffer );  
/******************** RESULTATS **************************/

Fri May 24 11:05:01 -


Fri May 24 11:05:01 -
Résultat  
nouvelle priorite = 150
nouvelle priorite = 100
Verrou va etre pris
Fri May 24 11:05:01 - nouvelle priorite = 131
Fri May 24 11:05:01 - nouvelle priorite = 150!
Fri May 24 11:05:01 - nouvelle priorite = 150
Fri May 24 11:05:01 - nouvelle priorite = 100 Verrou va etre pris!
On va rendre le verrou
Fri May
Fri24May
11:05:01 - nouvelle
24 11:05:01 - priorite
nouvelle= 131!
priorite = 150
Fri24May
Fri May 24 11:05:01
11:05:01 -
- nouvelle nouvelle
priorite = priorite
150 = 100 le verrou!
On va rendre
Fri May 24 11:05:01 - nouvelle priorite = 150 !
Fri May 24 11:05:01 - nouvelle priorite = 100!
!
Conclusion  
• API  RT-­‐POSIX  riche,  très  u0lisée  en  pra0que  dans  
les  STR,  pour  sa  portabilité  
• API  bas  niveau,  don’t  l’usage  mérite  être  factorisé  
via  un  middleware  
– Pas  de  modèle  de  tâche  temps  réel  en  RT-­‐POSIX  (à  la  
charge  du  middleware)  
• Agen0on  à  la  correspondance  entre  modèle  
d’ordo  théorique  et  implémenta0ons  
• Autres  standard  d’OS  temps-­‐réel  existent:  OSEK,  
ARINC653…  

Vous aimerez peut-être aussi