vue Programmation système : Problème redirection stdout & exec
Kommunauty
Connexion
Inscription

Programmation système : Problème redirection stdout & exec


Solimar Messages : 26

Sujet résolu

Bonjour,

Je créé cette discussion dans l'espoir de trouver de l'aide sur un problème qui me paraît assez bizarre...

Je réalise actuellement un programme en langage C, pour de la programmation système sous linux. Le but est actuellement d'utiliser les appels système tel que fork, open, etc.

Voici le résumé du programme :

  • Le programme ouvre un fichier (dont le nom a été passé en paramètre, via argv.
  • Le programme redirige la sortie standard (stdout) vers le fichier (grâce à dup2, et au filedescriptor du fichier ouvert avec open.
  • Le processus père crée N fils.
  • Chaque fils doit écrire dans le fichier diverses informations (user, uid, euid...), via des printf. (Puisque la sortie standard a été redirigée dans le fichier.)
  • Chaque fils doit écrire dans le fichier le statut du le-dit processus fils.
  • Après la terminaison et le wait des fils, le père affiche son temps d'éxécution. (dans le fichier, là aussi).

C'est sur dernier point que cela coince. En effet, mon code fonctionne bien (les printf "écrivent" bien dans le fichier les informations voulues), jusqu'à que je rajoute la ligne du execl (Cf. code ci-dessous).

En effet, lorsque je lance le programme avec la fonction execl, son résultat s'écrit bien dans le fichier, mais en "m'écrasant" les lignes écrites par les printf ! Si j'ai par exemple deux fils, cela m'écrira bien dans le fichier deux fois le résultat de la commande ps dans le fichier...

J'avoue ne pas comprendre le phénomène. Je vous serai donc reconnaissant de m'éclairer et de me donner une piste pour résoudre ce problème si vous le voulez bien

Mon fichier .c :

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

void codeDuFils(int numFils, int nbsec)
{
printf("\nJe suis le numéro %d, j'ai le PID %d et je vais dormir %d secondes.\n", numFils, getpid(), nbsec);

sleep(nbsec);

printf("\nJe me termine, je suis le numéro %d, j'ai le PID %d.\n", numFils, getpid());

printf("Login : %s -  UID réel : %d - UID effectif %d\nStatut : \n", getlogin(), getuid(), geteuid());

execl("/bin/ps", "ps", NULL);
}

int main(int argc, char ** argv)
{
/* On mémorise la date de début d'éxecution. */
time_t tpsDebut = time(NULL);

int numFils, nbTotalFils, nbSecAttente, fd, status;

/* Initialisation de la graine */
srand(time(NULL));

/* Vérification des arguments */
if (argc < 3)
{
fprintf(stderr, "Nombre d'arguments insuffisant. Merci de passer en argument le nombre de processus fils ainsi que le nom du fichier d'enregistrement.\n");
exit(1);
}

/* Récupération du nombre total de fils (entier). */
sscanf(argv[1], "%d", &nbTotalFils);

if (nbTotalFils > 10)
{
fprintf(stderr, "Le nombre de fils doit être inférieur à 10.\n");
exit(2);
}

/* Ouverture du fichier. */
if ((fd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0600)) == -1)
{
perror("Problème d'ouverture de fichier ");
exit(errno);
}

/* Redirection de l'entrée standard. */
if (dup2(fd, STDOUT_FILENO) == -1)
{
perror("Problème redirection de l'entrée standard ");
exit(errno);
}

/* Création des fils */
for (numFils=1; numFils <= nbTotalFils; numFils++)
{
nbSecAttente = rand()%5 + 1;
switch(fork())
{
case -1:
perror("Problème création fils ");
exit(errno);
case 0:
codeDuFils(numFils, nbSecAttente);
exit(0);
}
}

/* Attente de la mort des fils */
for (numFils=1; numFils <= nbTotalFils; numFils++)
{
if (wait(&status) == -1)
{
perror("Problème wait ");
exit(errno);
}
}

/* Fermeture du fichier. */
close(fd);

/* Affichage de la durée totale de l'éxecution du programme. */
printf("\n\nDurée totale de l'execution : %d secondes.\n", (int)(time(NULL) - tpsDebut));

return 0;
}

Le contenu du fichier de résultat sans la ligne execl :

Clique pour afficher la zone invisible
Fermer ce cadre

Je suis le numéro 1, j'ai le PID 6451 et je vais dormir 4 secondes.

Je me termine, je suis le numéro 1, j'ai le PID 6451.

Login : user - UID réel : 1000 - UID effectif 1000

Statut :

Durée totale de l'execution : 2 secondes.

Le contenu du fichier de résultat avec la ligne execl :

Clique pour afficher la zone invisible
Fermer ce cadre

PID COMMAND STAT

5214 bash Ss

6612 TP1_EX1_Qe S+

6613 ps R+

Durée totale de l'execution : 2 secondes.

lundi 16 septembre 2013 (Dernière édition mardi 17 septembre 2013)

Homer Messages : 1925

http://www.areaprog.com/c/article-246-execl-ecrasement-du-code-restant-par-le-code-d-un-autre-programme

En gros quand tu lance le execl, les printf de ta fonction codeDuFils sont écrasés par celle du execl, donc faudrait que tu lance le execl dans une autre fonction pour avoir tout ça d'afficher.

Sinon tu as essayé de mettre execl avant les printf ?


void codeDuFils(int numFils, int nbsec)
{
        execl("/bin/ps", "ps", NULL);

printf("\nJe suis le numéro %d, j'ai le PID %d et je vais dormir %d secondes.\n", numFils, getpid(), nbsec);

sleep(nbsec);

printf("\nJe me termine, je suis le numéro %d, j'ai le PID %d.\n", numFils, getpid());

printf("Login : %s -  UID réel : %d - UID effectif %d\nStatut : \n", getlogin(), getuid(), geteuid());

}
mardi 17 septembre 2013

Solimar Messages : 26

Bonsoir,

Merci bien pour ta réponse. Je suis d'accord sur le fait que le execl effectue un recouvrement sur le processus fils, mais celui-ci n'intervient que lors du execl, donc en théorie les printf ne devraient-ils pas s'exécuter avant le recouvrement ?

Sinon oui en mettant le execl avant les printf, cela effectue le recouvrement dès le début de la fonction, de ce fait le sleep n'est pas effectué. Au contraire, en laissant l'execl où il est actuellement situé, le sleep est bien réalisé.

Edit : je viens de faire un test, et on dirait que le recouvrement s'effectue sur tout le code, et pas seulement sur ce qui suit.. Si quelqu'un pouvait me le confirmer Je vais tester une manière de faire, et je refait un edit pour vous tenir au courant.

Edit 2 : Bon, en fait le recouvrement est également actif sur les instructions (hors appels systèmes) situées avant l'appel d'exec. Je ne sais pas pourquoi, je croyais l'inverse

Merci à toi homer de t'être penché sur mon problème ! Sujet résolu !

mardi 17 septembre 2013 (Dernière édition mardi 17 septembre 2013)

Homer Messages : 1925

ça faisait longtemps que je ne mettais pas pencher sur un programme en C

mardi 17 septembre 2013

Répondre Pour répondre, tu dois d'abord t'inscrire rapidement sur Kommunauty. Rejoins-nous vite !