Le code C/C++ ci-dessous effectue 3 milliards de tours de boucle, et à chaque 100 ms (c’est-à-dire 100000 μs) le processus est interrompu pour afficher le nombre de tours de boucle effectué à date. La temporisation s’effectue à l’aide de l’appel système setitimer(...)
qui envoie un signal SIGPROF
au processus à chaque 100 ms. Le prototype de setitimer()
est :
int setitimer(ITIMER_PROF, const struct itimerval *value, NULL);
sighandler()
soit exécutée à la réception du signal SIGPROF
. Le signal 29 SIGPROF
sert au déclenchement d’un temporisateur profilé (setitimer
).
sighandler()
doit afficher le nombre de tours de boucle effectué et doit aussi compter le nombre d’interruptions du temporisateur. Avant de terminer son exécution, le processus affiche alors le nombre total des interruptions durant les 3 milliards de tours de boucle.
// Définition du gestionnaire du signal de temporisation
static void sighandler(int sigtype);
long i = 0; // Compteur
...
// Valeurs du temporisateur par intervalle
struct itimerval v;
int main() {
...
// Initialisation du temporisateur
//Valeur des répétitions en secondes ou microsecondes
v.it_interval.tv_sec = 0;
v.it_interval.tv_usec = 100000;
// Valeur initiale en secondes ou microsecondes
v.it_value.tv_sec = 0;
v.it_value.tv_usec = 100000;
printf("\ndébut de la temporisation\n");
setitimer(ITIMER_PROF, &v, NULL);
for (i=0; i < 3000000000; i++);
exit(0);
}
...
#include <sys/time.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
// Définition du gestionnaire du signal de temporisation
static void sighandler(int sigtype);
long i = 0; // Compteur
long count=0;
// Valeurs du temporisateur par intervalle
struct itimerval v;
int main() {
signal(SIGPROF,sighandler);
// Initialisation du temporisateur
//Valeur des répétitions en secondes ou microsecondes
v.it_interval.tv_sec = 0;
v.it_interval.tv_usec = 100000;
// Valeur initiale en secondes ou microsecondes
v.it_value.tv_sec = 0;
v.it_value.tv_usec = 100000;
printf("\ndébut de la temporisation\n");
setitimer(ITIMER_PROF, &v, NULL);
for (i=0; i < 3000000000; i++);
printf("Nombre d’interruptions traitées %ld \n",count);
exit(0);
}
void sighandler(int sigtype)
{
count++;
printf("interruption à l’itération %ld\n",i);
}
On veut établir, en utilisant les tubes anonymes, une communication de type anneau unidirectionnel entre trois processus fils. Pour ce faire, la sortie standard de l’un doit être redirigée vers l’entrée standard d’un autre de manière à établir, entre eux, une communication de type anneau. Complétez le code suivant qui crée déjà trois processus fils pour créer une telle communication :
/*0*/
int main () {
/*1*/
if (fork()) // création du premier processus
{ if(fork()) { /*2*/
if(fork()) { /*3*/
while (wait(NULL)>0);
/*4*/
} else { // processus P3
/*5*/
execlp("program3", "program3",NULL);
/*6*/
}
} else { // processus P2
/*7*/
execlp("program2", "program2",NULL);
/*8*/
}
} else { //processus P1
/*9*/
execlp("program1","program1", NULL);
/*10*/
}
/*11*/
}
/*0*/ int p12[2], p23[2], p31[2];
int main () {
// pour des raisons de clarté,
// les cas d’erreur ne sont pas traités
/*1*/ pipe(p12); pipe(p23); pipe(p31);
if (fork()) // création du premier processus
{ if(fork()) { /*2*/
if(fork()) { /*3*/ close(p12[0]); close(p12[1]);
close(p23[0]); close(p23[0]);
close(p31[0]); close(p31[1]);
while (wait(NULL)>0);
/*4*/ exit(0);
} else { // processus P3
/*5*/ dup2(p31[1],1); dup2(p23[0]);
close(p12[0]); close(p12[1]);
close(p23[0]); close(p23[0]);
close(p31[0]); close(p31[1]);
execlp("program3", "program3",NULL);
/*6*/ exit(1); }
} else { // processus P2
/*7*/ dup2(p23[1],1); dup2(p12[0]);
close(p12[0]); close(p12[1]);
close(p23[0]); close(p23[0]);
close(p31[0]); close(p31[1]);
execlp("program2", "program2",NULL);
/*8*/ exit(1); }
} else { //processus P1
/*9*/ dup2(p12[1],1); dup2(p31[0]);
close(p12[0]); close(p12[1]);
close(p23[0]); close(p23[0]);
close(p31[0]); close(p31[1]);
execlp("program1","program1", NULL);
/*10*/ exit(1);
}
}
Écrivez un programme mypipe.c
qui permet de simuler un pipe (’|’). Ce code doit utiliser un tube nommé et doit avoir exactement le même comportement qu’un pipe (’|’) dans un shell.
Exemples :
bash$> mypipe who wc -l (équivaut à bash$> who | wc -l)
bash$> mypipe ls grep "xxx" (équivaut à bash$> ls | grep "xxx")
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{ if (argc <4) exit(1);
int fd[2];
pipe(fd);
if (fork()==0)
{ dup2(fd[1],1); close(fd[0]); close(fd[1]);
execlp(argv[1], argv[1], NULL);
exit(1);
}
if (fork()==0)
{ dup2(fd[0],0); close(fd[0]); close(fd[1]);
execvp(argv[2], &argv[2]);
exit(1);
}
close(fd[0]); close(fd[1]);
while(wait(NULL)>0);
exit(0);
}
Considérez le programme suivant :
void sigintP() {/*1*/}
void sigalrm() {/*2*/}
void sigintF() {/*3*/}
void sigchld() {
wait(&NULL);
exit(0);
}
int main( ) {
signal(SIGCHLD, sigchld);
if (fork() == 0)
{ signal(SIGINT, sigintF);
while(1)
{ printf ("ici fils \n");
sleep(1);
}
}
while(1)
{ signal(SIGINT, sigintP);
printf("ici pere \n");
sleep(1);
}
return 0;
}
Complétez le code précédent de manière à réaliser les traitements suivants :
Ctrl-C
durant l’exécution du programme, les processus père et fils ne se terminent pas immédiatement, mais après un délai de 5 secondes. Ctrl-C
, le père affiche son identificateur (sans se terminer).Indication : La double-touche Ctrl-C
doit déclencher un appel système alarm(5)
, qui envoie automatiquement le signal SIGALRM
après 5 secondes.
Dans quel(s) ordre(s) les processus père et fils peuvent-ils se terminer ?Pourquoi ?
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
void sigintP() {/*1*/
alarm(5);
printf("capture SIGINT par le pere %d\n",getpid());
}
void sigalrm() {/*2*/
printf("capture SIGALRM par %d\n",getpid());
}
void sigintF() {/*3*/ alarm(5);}
void sigchld() { wait(NULL);
exit(0);
}
int main( ) {
signal(SIGCHLD, sigchld);
if (fork() == 0)
{ signal(SIGINT, sigintF);
while(1)
{ printf ("ici fils \n");
sleep(1);
}
}
while(1)
{ signal(SIGINT, sigintP);
signal(SIGALRM,sigalrm);
printf("ici pere \n");
sleep(1);
}
return 0;
}
Si la terminaison n’est pas forcée par la commande kill
, après la double touche Ctrl+C, le fils va toujours se terminer avant son père. La raison est que si le père reçoit en premier le signal SIGALRM, il va attendre la fin de son fils avant de se terminer.
Le programme fils-tube.c
suivant crée 2 processus fils qui communiquent via un tube. Le premier processus (producteur) envoie, via un tube de communication, des données au second processus (consommateur). Tracez manuellement le programme et affichez les résultats possibles. Ensuite, exécutez le code en commentant l’appel système sleep
et observez les résultats obtenus. Si vous n’observez pas une alternance d’exécution entre les processus producteur et consommateur, expliquez la raison. Finalement, utilisez l’appel système gettimeofday pour mesurer le temps d’exécution (système et utilisateur en microsecondes) de chacun des processus et expliquez pourquoi ce temps n’est pas exactement le même pour chacun des processus. Si votre temps d’exécution est 0, alors incrémentez la valeur du paramètre de sleep()
.
Listing 3.13 - fils-tube.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void) {
int
pid1, pid2, // identificateurs des processus fils
p1, p2, // identificateurs des processus termines
code_retour1,
code_retour2, // Codes de terminaison des processus
x = 0, // donnee non partagee entre les processus
y = 0,
i, // indice
tube[2]; // tube entre les processus
pipe(tube); // creation du tube
// Bloc du processus producteur
pid1=fork();
if (pid1==0) {
for (i = 0; i < 10; ++i) {
// afficher les donnees
printf("Producteur : x=%d y=%d\n", ++x, --y);
// Envoyer y au consommateur via le tube
write(tube[1], &y, 1);
// Bloquer le producteur pour 2 secondes
sleep(2);
}
exit(0); // Terminer l'execution du producteur
}
// Bloc du processus consommateur
pid2=fork();
// Si pid = 0, executer code du processus cree
if(pid2==0) {
for (i = 0; i < 10; ++i) {
// Extraire y du tube
read(tube[0], &y, 1);
// afficher les donnees
printf("Consommateur : x=%d y=%d\n", x++, y);
// Bloquer le consommateur pour 2 secondes
sleep(2);
}
exit(0); // Terminer l'execution du producteur
}
// Bloc du processus parent
// Execution du parent concurremment avec ses fils
// Afficher les valeurs de x et y
printf("Parent : x=%d y=%d\n", x, y);
// Synchroniser le processus parent avec les fils
p1 = wait(&code_retour1);
p2 = wait(&code_retour2);
// Indiquer l'ordre de terminaison des fils
if (p1 == pid1)
printf("\nProducteur a termine en premier.");
else
printf("\nConsommateur a termine en premier.");
return 0; // Terminer le processus parent
} // main
RÉPONSE À VENIR
Vous disposez d’une fonction F d’une bibliothèque qui écrit une certaine quantité de données sur la sortie standard (descripteur de fichier 1). Vous aimerez récupérer, en utilisant un pipe anonyme, ces données pour les traiter.
#define Taille 1024
int main () {
char Data[Taille];
/*1*/
F( );
/*2*/
Traiter(Data);
return 0;
}
RÉPONSE À VENIR