Split a POSIX semaphore between multiple processes

I need to create two child processes, each of which calls execvp ater, and executables share POSIX semaphores between them.

Do I need to create shared memory or just implement semaphores with a name?

I received two responses from the following links:

But I am confused by how to act in implementation.

+5
source share
2 answers

Do I need to create shared memory or just implement named semaphores?

Any approach will work. Choose one and go with it, although I personally prefer semaphores because you don't have to deal with memory allocation and setting up shared memory segments. In my opinion, the interface for creating and using the named semaphores is more friendly.

Using the named semaphores, in your example script, here's what happens:

  • You create and initialize a semaphore in the parent process with sem_open(3) . Give it a famous name that child processes will know; this name is used to search for a semaphore in the system.
  • Close the semaphore in the parent as it will not use it.
  • Fork and execution
  • Disconnect the semaphore using sem_unlink(3) . This must be done exactly once; it doesn't really matter where (any process that has a reference to a semaphore object can do this). It is good to unblock the semaphore if other processes still open it: the semaphore is destroyed only when all other processes have closed it, but keep in mind that the name is deleted immediately, so new processes will not be able to find and open the semaphore.

The child processes a call to sem_open(3) with a known name to find and get a link to the semaphore. When a process is executed using a semaphore, you need to close it with sem_close(3) .

Below is an example of what I just described. The parent process creates a named semaphore, and forks + executes 2 child processes, each of which finds and opens the semaphore, using it to synchronize with each other.

It is assumed that the parent fork and executes the binary code ./sem_chld . Keep in mind that the name of the semaphore must start with a slash followed by one or more characters that are not a slash (see man sem_overview ). In this example, the semaphore name is /semaphore_example .

Here is the code for the parent process:

 #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <semaphore.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #define SEM_NAME "/semaphore_example" #define SEM_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) #define INITIAL_VALUE 1 #define CHILD_PROGRAM "./sem_chld" int main(void) { /* We initialize the semaphore counter to 1 (INITIAL_VALUE) */ sem_t *semaphore = sem_open(SEM_NAME, O_CREAT | O_EXCL, SEM_PERMS, INITIAL_VALUE); if (semaphore == SEM_FAILED) { perror("sem_open(3) error"); exit(EXIT_FAILURE); } /* Close the semaphore as we won't be using it in the parent process */ if (sem_close(semaphore) < 0) { perror("sem_close(3) failed"); /* We ignore possible sem_unlink(3) errors here */ sem_unlink(SEM_NAME); exit(EXIT_FAILURE); } pid_t pids[2]; size_t i; for (i = 0; i < sizeof(pids)/sizeof(pids[0]); i++) { if ((pids[i] = fork()) < 0) { perror("fork(2) failed"); exit(EXIT_FAILURE); } if (pids[i] == 0) { if (execl(CHILD_PROGRAM, CHILD_PROGRAM, NULL) < 0) { perror("execl(2) failed"); exit(EXIT_FAILURE); } } } for (i = 0; i < sizeof(pids)/sizeof(pids[0]); i++) if (waitpid(pids[i], NULL, 0) < 0) perror("waitpid(2) failed"); if (sem_unlink(SEM_NAME) < 0) perror("sem_unlink(3) failed"); return 0; } 

Note that sem_unlink(3) is called after the completion of both children; although this is not required if it was called before a race condition occurs between the parent process that cancels the semaphore, and both child processes start and open the semaphore. In general, however, you can cancel the connection as soon as you find out that all the necessary processes have opened the semaphore, and no new processes need to find it.

Here is the code for sem_chld , it's just a small toy program showing the use of a common semaphore:

 #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <semaphore.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #define SEM_NAME "/semaphore_example" #define ITERS 10 int main(void) { sem_t *semaphore = sem_open(SEM_NAME, O_RDWR); if (semaphore == SEM_FAILED) { perror("sem_open(3) failed"); exit(EXIT_FAILURE); } int i; for (i = 0; i < ITERS; i++) { if (sem_wait(semaphore) < 0) { perror("sem_wait(3) failed on child"); continue; } printf("PID %ld acquired semaphore\n", (long) getpid()); if (sem_post(semaphore) < 0) { perror("sem_post(3) error on child"); } sleep(1); } if (sem_close(semaphore) < 0) perror("sem_close(3) failed"); return 0; } 

You can eliminate the need to save a semaphore name synchronized between two source files by defining it in a common header file and including it in the code for each program.

Please note that the error handling in this example is not ideal (it is just illustrative), there are many opportunities for improvement. This is just to make sure that you remember to do the correct error handling when you decide to modify this example to suit your needs.

+7
source

Working with shared memory will also work here, only here the parent process must initialize the shared memory. It seems that there is one error in this code, and not two children, the parent function will have a child process fork 3. Inside there should be a break statement

  if (pids[i] == 0) { if (execl(CHILD_PROGRAM, CHILD_PROGRAM, NULL) < 0) { perror("execl(2) failed"); exit(EXIT_FAILURE); } break; //this is required } 
0
source

All Articles