user1743798
user1743798

Reputation: 445

procs, fork(), and mutexes

I want to create n processes running in parallel and have them lock a mutex, increment a counter, and then unlock and exit.

Here is my code:

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <pthread.h>

pthread_mutex_t mutex;

int main(int argc, char **argv) {

  if (argc != 2)
    return 0;
  int n = atoi(argv[1]);
  int i = 0;
  int status = 0;

  pthread_mutex_init(&mutex, NULL);

  pid_t pid = 1;
  static int *x;
  x = mmap(NULL, sizeof *x, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  *x = 0;

  printf("Creating %d children\n", n);

  for(i = 0; i < n; i++) {
    if (pid != 0)
      pid = fork();
  }

  if (pid == 0) {
    pthread_mutex_lock(&mutex);
    *x = *x + 1;
    printf("[CHLD] PID: %d PPID: %d X: %d\n", getpid(), getppid(), *x);
    pthread_mutex_unlock(&mutex);
    exit(0);
  }

  wait(&status);

  printf("[PRNT] PID: %d X: %d\n", getpid(), *x);
  munmap(x, sizeof *x);

  return 0;
}

./procs 10000 however does not return with x=10000 I think this is because the mutex isn't shared between the processes, but what's the correct way to share the mutex?

Upvotes: 12

Views: 15437

Answers (2)

tvsarathy
tvsarathy

Reputation: 11

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>

typedef struct {
    pthread_mutex_t mutex;
    int data;
}MUTEX_N_DATA;

int main(int argc, char **argv) {

    if (argc != 2)
        return 0;
    int n = atoi(argv[1]);
    int i = 0;
    bool wait = true;

    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);

    pid_t pid = 1;
    MUTEX_N_DATA *x;
    x = mmap(NULL, sizeof(MUTEX_N_DATA), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    x->data = 0;
    pthread_mutex_init(&x->mutex, &attr);

    printf("Creating %d children\n", n);
    for(i = 0; i < n; i++) {
        if (pid != 0) {
            pid = fork();
            printf("Created Child %d \n",pid);
        }
    }

    if (pid == 0) {
        pthread_mutex_lock(&x->mutex);
        x->data = x->data + 1;
        printf("[CHLD] PID: %d PPID: %d X: %d\n", getpid(), getppid(), x->data);
        pthread_mutex_unlock(&x->mutex);
        exit(0);
    }

    while(wait){
        pthread_mutex_lock(&x->mutex);
        if(x->data == n){
            wait = false;
        }
        pthread_mutex_unlock(&x->mutex);
        usleep(100000);
    }

    printf("[PRNT] PID: %d X: %d\n", getpid(), x->data);
    munmap(x, sizeof *x);

    return 0;
}

The wait() waits only until one of the child exits. with following changes it works.

  1. mutex attribute set to shared & moved to mmap memory.
  2. parent waiting for the count to reach the number of childrens.

https://man7.org/linux/man-pages/man2/fork.2.html

https://man7.org/linux/man-pages/man2/wait.2.html

Upvotes: 1

goji
goji

Reputation: 7092

Here's a port of my example in the comment using pthread_mutex. First time I've done this, but seems to work:

#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <stdbool.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <pthread.h>

typedef struct
{
  bool done;
  pthread_mutex_t mutex;
} shared_data;

static shared_data* data = NULL;

void initialise_shared()
{
    // place our shared data in shared memory
    int prot = PROT_READ | PROT_WRITE;
    int flags = MAP_SHARED | MAP_ANONYMOUS;
    data = mmap(NULL, sizeof(shared_data), prot, flags, -1, 0);
    assert(data);

    data->done = false;

    // initialise mutex so it works properly in shared memory
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(&data->mutex, &attr);
}

void run_child()
{
  while (true)
  {
      puts("child waiting. .. ");
      usleep(500000);

      pthread_mutex_lock(&data->mutex);
      if (data->done) {
          pthread_mutex_unlock(&data->mutex);
          puts("got done!");
          break;
      }
      pthread_mutex_unlock(&data->mutex);
  }

  puts("child exiting ..");
}

void run_parent(pid_t pid)
{
    puts("parent sleeping ..");
    sleep(2);

    puts("setting done ..");
    pthread_mutex_lock(&data->mutex);
    data->done = true;
    pthread_mutex_unlock(&data->mutex);

    waitpid(pid, NULL, NULL);

    puts("parent exiting ..");
}

int main(int argc, char** argv)
{
    initialise_shared();

    pid_t pid = fork();
    if (!pid) {
        run_child();
    }
    else {
        run_parent(pid);
    }

    munmap(data, sizeof(data));
    return 0;
}

Upvotes: 17

Related Questions