2
votes

Comment faire incrémenter plusieurs processus enfants et suivre la même variable globale

J'ai été chargé de créer un programme qui analyse un fichier / répertoire et donne des informations à leur sujet. Vous pouvez définir un indicateur de récursivité pour analyser chaque sous-répertoire. Chaque répertoire est analysé par un nouveau processus (c'est une exigence du projet) et je veux envoyer un signal à chaque fois qu'un nouveau fichier (SIGUSR2) ou répertoire (SIGUSR1) est trouvé. Dans le gestionnaire de ces signaux, je veux incrémenter des variables globales qui gardent une trace du nombre de fichiers / répertoires trouvés par le programme. J'ai des problèmes pour faire incrémenter les différents processus de la même variable globale. J'ai essayé des tubes mais je n'arrive pas à le faire fonctionner.

Voici ma fonction qui analyse un répertoire:

void sigusr1_handler(int sig)
{
  if (sig != SIGUSR1)
  {
    fprintf(stderr, "Wrong signal received! Expected: SIGUSR1\n");
  }

  dirsFound++;
  printf("New directory: %ld/%ld directories/files at this time.\n", dirsFound, filesFound);
}

void sigusr2_handler(int sig)
{
  if (sig != SIGUSR2)
  {
    fprintf(stderr, "Wrong signal received! Expected: SIGUSR2\n");
  }

  filesFound++;
}

Voici mes gestionnaires:

void process_dir(const ProgramConfig program_config, const char *dname, FILE *outstream)
{

  raise(SIGUSR1);

  /* Create a new process */
  pid_t pid = fork();

  if (pid == 0)
  {

    /* Child process */
    struct dirent *ent;
    DIR *dir;

    /* Open directory */
    if ((dir = opendir(dname)) != NULL)
    {
      /* Go through each file in this directory */
      while ((ent = readdir(dir)) != NULL)
      {
        /* Ignore anything that isn't a file or a directory */
        if (ent->d_type != DT_DIR && ent->d_type != DT_REG)
          continue;

        /* Ignore the '.' and '..' directories */
        if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
          continue;

        /* Prepend this directory name to file name */
        char name[256];
        strcpy(name, dname);
        strcat(name, "/");
        strcat(name, ent->d_name);

        if (ent->d_type == DT_DIR && program_config.r_flag)
        {
          /* Found a subdirectory, process it if -r flag enabled */
          process_dir(program_config, name, outstream);
        }
        else
        {
          /* Found a file, process it */
          process_file(program_config, name, outstream);
        }
      }
    }
    else
    {
      /* Error opening directory */
    }

    /* Exit from child process */
    exit(0);
  }
  else if (pid < 0)
  {
    /* Error creating process */
  }
  else
  {

    /* Parent process */
    wait(NULL);

    /* Log this event */
    if (program_config.v_flag)
    {
      char act[100];
      sprintf(act, "PROCESSED DIR %s", dname);
      log_event(act);
    }
  }
}

L'utilisation des fils n'est pas une option pour cette affectation.


8 commentaires

vous trouverez peut-être ici que les threads sont une solution plus pratique que les processus.


Je sais, j'ai appris sur les fils maintenant. J'ai oublié d'ajouter que l'une des exigences de ce projet est d'utiliser différents processus pour chaque répertoire. Les threads sont donc hors de l'équation.


utiliser le même disque à partir de plusieurs threads ou processus simultanément semble être un énorme problème de performances potentiel ... est-ce comme une tâche de travail ou quelque chose? :)


Oui, c'est un projet uni.


eh bien, aussi peu judicieux que cela puisse être, cela peut être résolu avec un fichier mappé en mémoire partagé et un incrément atomique ... ou regarder dans la fonction "kill" pour envoyer des signaux au processus parent.


n'effectuez aucune impression dans les gestionnaires de signaux. ils ne sont pas censés y fonctionner correctement. assurez-vous également que dirsFound et filesFound sont volatils .


Il semble qu'il y ait un problème fondamental: vous n'êtes pas assuré que le gestionnaire sera appelé une fois pour chaque fois que le signal est envoyé. Si plusieurs signaux arrivent en succession proche, de sorte qu'un second arrive alors que le premier est toujours en attente (peut-être que votre processus est planifié ou en attente d'E / S), ils peuvent être "fusionnés" en un seul appel au gestionnaire. En tant que tel, votre nombre de fichiers trouvés peut être insuffisant. Vous pourriez être en mesure de résoudre ce problème en utilisant sigqueue (2) ; Je ne sais pas grand-chose à ce sujet. [...]


Mais à première vue, il semble que les signaux ne soient pas le bon mécanisme IPC pour résoudre ce problème. Je pense que vous devez reconcevoir.


3 Réponses :


0
votes

Si chaque processus enfant envoie un signal, le compteur n'a pas besoin d'être partagé ou "global". Le gestionnaire de signaux qui capte tous les signaux des enfants pourrait l'incrémenter. Si aucune autre partie du programme n'a besoin de le vérifier, il peut s'agir d'une variable locale statique . Sinon, il peut être déclaré comme statique au module avec toutes les fonctions qui l'obtiennent et le définissent.

Si vous voulez que plusieurs processus incrémentent la même variable partagée, vous voudrez mettre une variable atomique de , telle qu'un atomic_int , dans la mémoire partagée, que vous pouvez obtenir de shmget () , et l'incrémenter avec atomic_fetch_add () .

Je n'écrirai pas de solution complète, car il semble que vous vouliez résoudre ce problème de devoirs vous-même.


3 commentaires

Je pense que la solution que je souhaite concerne les tuyaux, pensez-vous que c'est possible? (d'une manière pas très compliquée)


Oui. La solution classique consiste à ouvrir une paire de canaux dans le processus parent qui sont connectés entre eux, à bifurquer pour que l'enfant en hérite, puis à fermer une extrémité dans le processus parent et l'autre extrémité dans le processus enfant. Vous vérifieriez les messages avec poll () ou select () .


Si vous souhaitez transmettre des messages à partir de plusieurs processus enfants, vous pouvez cependant essayer une file d'attente de messages.



0
votes

Si je comprends correctement, vous gérez les signaux dans le même processus qui les élève. En conséquence, chaque processus descendant incrémente son propre compteur de répertoires et de fichiers trouvés dans un sous-répertoire spécifique. Ce que vous devez faire est de transmettre l'ID de processus de l'ancêtre le plus élevé à tous ses descendants et d'envoyer des signaux des descendants à l'ancêtre le plus élevé. Voir Comment envoyer un signal à un processus en C? pour obtenir des conseils sur l'envoi de signaux à un autre processus.


1 commentaires

J'ai essayé de sauvegarder le pid du processus principal, puis d'utiliser kill () pour envoyer les signaux aux processus principaux au lieu de rise () mais cela m'a donné des résultats étranges



0
votes

Vous pourriez utiliser des sémaphores nommés je pense:

https://www.systutorials.com/docs/linux/ man / 7-sem_overview /

En gros, ouvrez le sémaphore avec le même nom dans tous les processus, sem_post pour incrémenter, sem_getvalue pour obtenir une valeur.


2 commentaires

Je n'ai pas le droit d'utiliser des sémaphores car je ne les ai pas encore appris: /


@PedroAlves Je vois, donc les tuyaux sont une condition préalable à l'affectation. Je laisserai la réponse au cas où quelqu'un viendrait ici avec un vrai problème. Bonne chance pour votre mission!