2

I'm implementing a forking Web server in C. The basic idea is creating several worker processes using the fork() system call and putting them to "sleep" waiting for work to be assigned by the parent process. The problem is I can't use busy waiting to synchronize the processes. I need some mechanisms like pthread_cond_broadcast that can put the child processes to sleep, such that the parent process can wake them up as necessary.

Bryan Mena
  • 31
  • 4
  • You could establish a pipe between the parent and each child. The child will suspend waiting for data on the pipe. The parent can "awaken" each child by sending a message on the pipe. – Jim Rogers Apr 25 '20 at 22:15

4 Answers4

0

If you must to use fork() instead of pthreadsthere are ways to map memory between processes in UNIX using shm_open (see also similar question) and mmap.

I would do it with mmap like this:

#include <stdio.h>
#include <unistd.h>

int main(){
  void *shared_mem=mmap(NULL,NUM_BYTES,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANONYMOUS,-1,0);
  if(fork()){
    accessMem(shared_mem);
  }
  else{
    accessMem(shared_mem);
  }
}

Basically the idea is that you mark the region of memory as shared between processes, and then after the fork, that particular region of memory remains constant between the two processes.

Best of luck

Willis Hershey
  • 1,520
  • 4
  • 22
  • The problem is I cannot use threads, I must use forks. Already tried to use shared memory with mmap as you said but I stil cant get it to work, any other idea? – Bryan Mena Apr 25 '20 at 22:32
0

As Jim Rogers mentioned in a comment, you can make the child processes block in a read of a pipe until you have work for them to do. Here's an example of how you'd do that with a single child process:

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

int childmain(int fd) {
    char c;
    if(read(fd, &c, 1) != 1) {
        perror("read");
        return 1;
    }
    printf("Hello from the child process! The parent just woke me up.\n");
    return 0;
}

int main(void) {
    int fds[2];
    if(pipe(fds)) {
        perror("pipe");
        return 1;
    }
    pid_t pid = fork();
    if(pid < 0) {
        perror("fork");
        return 1;
    } else if(pid == 0) {
        if(close(fds[1])) {
            perror("close");
            return 1;
        }
        return childmain(fds[0]);
    }
    if(close(fds[0])) {
        perror("close");
        goto out;
    }
    printf("Child is waiting. Hit Enter to wake it up.\n");
    getchar();
    if(write(fds[1], "", 1) != 1) {
        perror("write");
        goto out;
    }
    printf("Child sent wakeup signal. Now waiting for it to exit...\n");
  out:
    if(wait(NULL) != pid) {
        perror("wait");
        return 1;
    }
    return 0;
}
0

There are a variety of synchronization mechanisms you can use on POSIX systems (I assume you have POSIX as you have fork)

  • pipes (named or unnamed) can be used -- write and read 'tokens' to synchronize
  • SYSV semaphore objects are available via sem_open -- see man sem_overview
  • sigwait and signals can be used
  • sockets (via socketpair or connecting) can be used in a similar way to pipes.
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
0

I ended up using the pthread conditional and mutex data types, I had to configure them as share objects (create them on shared memory and configure their attributes). Sample code of the initialization.

Code to initialize pthread_mutexattr_t:

pthread_mutexattr_t get_mutex_attributes() {
    pthread_mutexattr_t mattr;
    pthread_mutexattr_init(&mattr);
    // Attribute to allow the mutex to be shared between processes
    pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
    return mattr;
}

This function creates the attributes to initialize a mutex lock as PTHREAD_PROCESS_SHARED allowing multiple processes to use the lock

Then, use this function to create shared memory:

void *create_shared_memory(size_t size) {
    int protection = PROT_READ | PROT_WRITE;
    int flags = MAP_SHARED | MAP_ANONYMOUS | MAP_SYNC;

    return mmap(NULL, size, protection, flags, -1, 0);
}

and initialize the mutex:

mut_allt = (pthread_mutex_t *) create_shared_memory(sizeof(pthread_mutex_t));
pthread_mutex_init(mut_allt, &mattr);

With all of this and some conditional mutexes I manage to synchronize the processes, what was keeping me from using pthread conditionals and mutex before was the fact that I wasn't configuring them properly. Hope this helps someone!

Bryan Mena
  • 31
  • 4