9

Let's say I create a semaphore. If I fork a bunch of child processes, will they all still use that same semaphore?

Also, suppose I create a struct with semaphores inside and forked. Do all the child processes still use that same semaphore? If not, would storing that struct+semaphores in shared memory allow the child processes to use the same semaphores?

I'm really confused about how my forked child processes can use the same semaphores.

jmnwong
  • 1,577
  • 6
  • 22
  • 33

3 Answers3

13

Let's say I create a semaphore. If I fork a bunch of child processes, will they all still use that same semaphore?

If you are using a SysV IPC semaphore (semctl), then yes. If you are using POSIX semaphores (sem_init), then yes, but only if you pass a true value for the pshared argument on creation and place it in shared memory.

Also, suppose I create a struct with semaphores inside and forked. Do all the child processes still use that same semaphore? If not, would storing that struct+semaphores in shared memory allow the child processes to use the same semaphores?

What do you mean be 'semaphores inside'? References to SysV IPC semaphores will be shared, because the semaphores don't belong to any process. If you're using POSIX semaphores, or constructing something out of pthreads mutexes and condvars, you will need to use shared memory, and the pshared attribute (pthreads has a pshared attribute for condvars and mutexes as well)

Note that anonymous mmaps created with the MAP_SHARED flag count as (anonymous) shared memory for these purposes, so it's not necessary to actually create a named shared memory segment. Ordinary heap memory will not be shared after a fork.

Community
  • 1
  • 1
bdonlan
  • 224,562
  • 31
  • 268
  • 324
  • To clarify, I created a struct and inside the struct I put my POSIX semaphores. Then I put this struct into shared memory. In doing so, can I assume that all my child processes are accessing the same semaphores properly? – jmnwong Jul 27 '11 at 18:17
  • I created my semaphore with sem_init(&p.mysem, 1, 1). Where p is a struct and mysem is a sem_t inside of the struct. Does this look correct? – jmnwong Jul 27 '11 at 18:19
  • @canistr, the struct must be placed in shared memory (memory mapped using mmap's `MAP_SHARED` flag), and the semaphores must be created with `sem_init` passing 1 for the pshared flag. If these conditions are satisfied, the child processes will share the semaphore properly. – bdonlan Jul 27 '11 at 18:20
  • So how do I use mmap with shared memory? – jmnwong Jul 27 '11 at 18:44
  • 1
    @canistr, this is a bit beyond the scope of this question. There are a lot of resources on using mmap for shared memory if you search a bit, and if you still have trouble feel free to open another question on the topic – bdonlan Jul 27 '11 at 18:47
  • To make an anonymous mmap one also needs the MAP_ANONYMOUS flag. Otherwise the memory is not shared across fork but copied and the parent and child process will work on two different semaphores. – Étienne May 21 '13 at 21:30
8

Let's say I create a semaphore. If I fork a bunch of child processes, will they all still use that same semaphore?

It depends how you created the semaphore, to do that with an IPC semaphore see semaphore.c: Illustration of simple semaphore passing for an example.

Also, suppose I create a struct with semaphores inside and forked. Do all the child processes still use that same semaphore? If not, would storing that struct+semaphores in shared memory allow the child processes to use the same semaphores?

For that to work your semaphore needs to be stored in an area shared between the parent and the child process like shared memory, and not just created on the stack or on the heap because it will be copied when the process forks.

I'm really confused about how my forked child processes can use the same semaphores.

The semaphore can be shared across threads or processes. Cross-process sharing is implemented on the operating system level. Two or more different processes can share the same semaphore even if those processes were not created by forking a single parent process.

See this example for sharing an unnamed UNIX semaphore between a parent process and its child (to compile with gcc you'll need the -pthread flag):

#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

int main(void)
{
  /* place semaphore in shared memory */
  sem_t *sema = mmap(NULL, sizeof(*sema), 
      PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,
      -1, 0);
  if (sema == MAP_FAILED) {
    perror("mmap");
    exit(EXIT_FAILURE);
  }

  /* create/initialize semaphore */
  if ( sem_init(sema, 1, 0) < 0) {
    perror("sem_init");
    exit(EXIT_FAILURE);
  }
  int nloop=10;
  int pid = fork();
  if (pid < 0) {
    perror("fork");
    exit(EXIT_FAILURE);
  }
  if (pid == 0) { 
    /* child process*/
    for (int i = 0; i < nloop; i++) {
      printf("child unlocks semaphore: %d\n", i);
      if (sem_post(sema) < 0) {
          perror("sem_post");
      }
      sleep(1);
    }
    if (munmap(sema, sizeof(sema)) < 0) {
      perror("munmap");
      exit(EXIT_FAILURE);
    }
      exit(EXIT_SUCCESS);
  }
  if (pid > 0) {
    /* back to parent process */
    for (int i = 0; i < nloop; i++) {
      printf("parent starts waiting: %d\n", i);
      if (sem_wait(sema) < 0) {
        perror("sem_wait");
      }
      printf("parent finished waiting: %d\n", i);
    }
    if (sem_destroy(sema) < 0) {
      perror("sem_destroy failed");
      exit(EXIT_FAILURE);
    }
    if (munmap(sema, sizeof(sema)) < 0) {
      perror("munmap failed");
      exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
  }
}

The output will be:

parent starts waiting: 0
child unlocks semaphore: 0
parent finished waiting: 0
parent starts waiting: 1
child unlocks semaphore: 1
parent finished waiting: 1
...

You may want to read Semaphores in Linux as well, but be aware that the example of UNIX semaphores across fork given doesn't work because the author forgot to use the MAP_ANONYMOUS flag in mmap.

Benjamin Leinweber
  • 2,774
  • 1
  • 24
  • 41
  • So if I want forked children to share a semaphore, they need to be IPC semaphores. Can I store these semaphores in a struct and pass it to different functions? I'm confused about your second point because I want to use shared memory to pass information between child processes and protect them with those semaphores. – jmnwong Jul 27 '11 at 17:29
  • If I wanted to specifically use unnamed semaphores, would this differ greatly from IPC semaphores? – jmnwong Jul 27 '11 at 17:36
  • @canistr: You can indeed use shared memory to share information between processes, just don't put a semaphore into that memory. Just go trough the first example I have provided - it does `fork ()` and both child and parent work with the same semaphore without shared memory. Throw shared memory with the information you want to share on top of this example and you will be OK. –  Jul 27 '11 at 17:37
  • @canistr: use `sem_init` to initialize unnamed semaphores, that should do the job. See the man page for a nice description - http://www.kernel.org/doc/man-pages/online/pages/man3/sem_init.3.html –  Jul 27 '11 at 17:40
  • @Vlad, semaphores can be shared in shared memory if you pass the proper pshared arguments; see my answer for references – bdonlan Jul 27 '11 at 18:00
  • Thanks for your help so far. I just realized I'm required to use POSIX semaphores. I know I keep asking the same stupid question but even with POSIX semaphores, forked child processes will always see the same set of semaphores correct? Right now I have several child processes that have been forked and then call some other function. Within these functions I use the semaphores to protect shared memory that shares information between the functions belonging to separate processes. I just want to be sure they are using the same semaphores and that they aren't all using their own versions. – jmnwong Jul 27 '11 at 18:02
  • @canistr, take a look at my answer, I cover POSIX semaphores specifically – bdonlan Jul 27 '11 at 18:12
  • 1
    The use of unnamed semaphores across a fork example you are linking to in "semaphores in Linux" is bugged. You need to either add the anonymous flag, or use named semaphores. – Étienne May 21 '13 at 21:25
  • 1
    @Étienne: Cool, do you mind editing the answer and making it a community wiki? Working code examples are welcome :) –  May 21 '13 at 21:39
  • @VladLazarenko: I posted a working code example as answer, since I can not turn your post community wiki, and I didn't want my edit to be rejected. (I think you're the only one who can turn your posts community wiki). Feel free to integrate my post in yours if you want to turn it community wiki, I'll delete my answer then. – Étienne May 22 '13 at 19:32
  • Do you want to let both answers separate or merge them in a community wiki? – Étienne May 28 '13 at 18:29
-1

Try this

child and parent would increment the shared variable alternatively

#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

struct test {
        sem_t mutex1;
        sem_t mutex2;
        int temp;
}test1;

int main(int argc, char **argv)
{
  int fd, i,count=0,nloop=10,zero=0,*ptr;
  struct test *testptr;
  //open a file and map it into memory
        sem_t mutex;
  fd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU);
  write(fd,&zero,sizeof(int));
  ptr = mmap(NULL, sizeof(struct test),PROT_READ |PROT_WRITE,MAP_SHARED,fd,0);
  close(fd);
  memcpy(ptr, &test1, sizeof(test1));
  testptr = (struct test *)ptr;
  // testptr = (struct test *)&test1;
  /* create, initialize semaphore */
  if( sem_init(&(testptr->mutex1),1,1) < 0)
    {
      perror("semaphore initilization");
      exit(0);
    }
  /* create, initialize semaphore */
  if( sem_init(&(testptr->mutex2),1,0) < 0)
    {
      perror("semaphore initilization");
      exit(0);
    }
  if (fork() == 0) { /* child process*/
    for (i = 0; i < nloop; i++) {
      sem_wait(&(testptr->mutex2));
      printf("child: %d\n", testptr->temp++);
      sem_post(&(testptr->mutex1));
    }
    exit(0);
 /* back to parent process */
  for (i = 0; i < nloop; i++) {
    sem_wait(&testptr->mutex1);
    printf("parent: %d\n", testptr->temp++);
    sem_post(&(testptr->mutex2));
  }
  exit(0);
}
user997487
  • 81
  • 7
  • 3
    -1: You copied this code from http://www.linuxdevcenter.com/pub/a/linux/2007/05/24/semaphores-in-linux.html?page=5 without mentioning it was not yours. Plus this code DOESN'T WORK! See http://blog.superpat.com/2010/07/14/semaphores-on-linux-sem_init-vs-sem_open/ for the explanation. – Étienne May 21 '13 at 21:20