2

I was tasked with making a program that analyzes a file/directory and gives information about them. You can set a recursion flag to analyze every subdirectory. Every directory is analyzed by a new process (this is a requirement of the project) and I want to send a signal every time a new file (SIGUSR2) or directory (SIGUSR1) is found. In the handler for those signals I want to increment global variables that keep track of the number of files/directories found by the program. I'm having problems with making the different processes increment the same global variable. I've tried pipes but I can't seem to get it to work.

Here is my function that analyzes a directory:

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);
    }
  }
}

Here are my handlers:

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++;
}

Using threads is not an option for this assignment.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
136
  • 1,083
  • 1
  • 7
  • 14
  • 2
    you might find threads to be a more practical solution here than processes. – bmargulies Mar 31 '19 at 18:49
  • I know, I've been learning about threads now. I forgot to add that one of the requirements of this project is to use various processes for each directory. So threads are out of the equation. – 136 Mar 31 '19 at 18:56
  • using the same disk from either multiple threads or processes simultaneously seems like a huge potential performance issue... is this like a homework task or something? :) – archo Mar 31 '19 at 18:57
  • Yes, it's a uni project. – 136 Mar 31 '19 at 18:59
  • well, as ill-advised as it may be, this can be solved with one shared memory-mapped file and atomic increment... or look into the "kill" function to send signals to the parent process. – archo Mar 31 '19 at 19:08
  • do not do any prints in signal handlers. they are not supposed to work properly there. also make sure that dirsFound and filesFound are `volatile`. – Serge Mar 31 '19 at 19:25
  • It seems like there's a fundamental problem: you are not guaranteed that the handler will be called once for every time the signal is sent. If multiple signals arrive in close succession, so that a second arrives while the first is still pending (maybe your process is scheduled out, or waiting for I/O), they can be "coalesced" into a single call to the handler. As such, your count of files found may come up short. You *might* be able to fix this by using `sigqueue(2)`; I don't know a lot about that. [...] – Nate Eldredge Apr 02 '19 at 22:35
  • But on the face of it, it seems that signals are not the right IPC mechanism to solve this problem. I think you need to redesign. – Nate Eldredge Apr 02 '19 at 22:36

3 Answers3

0

If every child process sends a signal, then the counter doesn’t need to be shared or "global." The signal handler that catches all the signals from the children could increment it. If no other part of the program needs to check it, it could be a static local variable. If not, it might be declared as static to the module with all the functions that get and set it.

If you want multiple processes to increment the same shared variable, you would want to put an atomic variable from <stdatomic.h>, such as an atomic_int, in shared memory, which you might obtain from shmget(), and increment it with atomic_fetch_add().

I won’t write a complete solution, because it sounds like you want to solve this homework problem yourself.

Davislor
  • 14,674
  • 2
  • 34
  • 49
  • I believe the solution I want involves pipes, do you think this is possible? (in a not very complicated way) – 136 Mar 31 '19 at 20:23
  • Yes. The classic solution is to open a pair of pipes in the parent process that are connected to each other, fork so the child inherits them, then close one end in the parent and the other end in the child process. You would check for messages with either `poll()` or `select()`. – Davislor Apr 01 '19 at 05:29
  • If you want to pass messages from multiple child processes, however, you might try a message queue. – Davislor Apr 01 '19 at 05:29
0

If I get if correctly, you handle the signals in the same process which raises them. As a result each descendant process increments its own counter of directories and files found in a specific subdirectory. What you need to do is passing the topmost ancestor process id to all its descendants and send signals from descendants to the topmost ancestor. See How to send a signal to a process in C? for hints on sending signals to another process.

CiaPan
  • 9,381
  • 2
  • 21
  • 35
  • I've tried saving the main process' pid and then use kill() to send the signals to the main processes instead of raise() but that got me some weird results – 136 Mar 31 '19 at 19:57
0

You could use named semaphores I think:

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

Basically open the semaphore with same name across processes, sem_post to increment, sem_getvalue to get value.

Andreas
  • 5,086
  • 3
  • 16
  • 36
  • I'm not allowed to use semaphores since I haven't learned them yet :/ – 136 Mar 31 '19 at 20:20
  • @PedroAlves I see, so pipes is an assignment prerequisite. I'll leave the answer in case someone comes here with a real problem. Good luck on your assignment! – Andreas Apr 01 '19 at 08:04