Using semaphores between processes [duplicate] - c

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.

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.

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.

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

Related

POSIX Semaphores parent semaphore value is unaffected

I am learning POSIX Semaphores.
Wrote a basic code which allows share POSIX Semaphores between parent and child. Why is the semaphore value not changed in parent after child updates it.
#include <stdio.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <stdlib.h>
void print_sem_value(sem_t *sem)
{
int sem_value;
if (sem_getvalue(sem, &sem_value) != 0) {
perror("sem_getvalue");
} else {
printf("%d:Semaphore value:%d\n", getpid(), sem_value);
}
}
int main(int argc, char *argv[])
{
pid_t pid;
sem_t sem;
sem_init(&sem, 1, 3);
pid = fork();
if (pid == 0) {
print_sem_value(&sem);
sem_wait(&sem);
print_sem_value(&sem);
sem_wait(&sem);
print_sem_value(&sem);
} else if (pid > 0) {
wait(NULL);
print_sem_value(&sem);
sem_post(&sem);
print_sem_value(&sem);
}
sem_destroy(&sem);
return 0;
}
Why is the semaphore value not changed in parent after child updates it.
You create an unnamed, process-shared semaphore, which is appropriate for your purpose, but you have not caused it actually to be shared. As the sem_init manual page documents:
If pshared is nonzero, then the semaphore is shared between
processes, and should be located in a region of shared memory (see
shm_open(3), mmap(2), and shmget(2)).
If you don't adhere to that then the child gets its own copy of the semaphore, which does not interact with the parent's, process-shared status notwithstanding.
Once you fix that, do note also that only one process should sem_destroy() the semaphore, whereas in your current code, both processes (attempt to) do.

Process Synchronisation in C does not execute the first time

So I am having this issue with a Process Synchronization program in C.
I am supposed to make a code that, using fork(), will produce something like this:
PARENT
PARENT
CHILD
PARENT
CHILD
PARENT
Using a code I found here, I was able to make it work but for some reasons, the first result that comes to screen is messed while all the others work just fine.
To compile, type : gcc test.c display.c -o test -pthread
Anyway, here is the code I am testing (I repeat: it's not my code):
#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)
{
int i;
/* place semaphore in shared memory */
sem_t *sema = mmap(NULL, sizeof(sema), PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1, 0);
/* 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)
{
for (i = 0; i < nloop; i++)
{
// child unlocks semaphore
display("CHILD\n");
if (sem_post(sema) < 0)
perror("sem_post");
sleep(1);
}
if (munmap(sema, sizeof(sema)) < 0)
{
perror("munmap");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
else
{
for (i = 0; i < nloop; i++)
{ // parent starts waiting
display("PARENT\n");
if (sem_wait(sema) < 0)
perror("sem_wait");
// parent finished waiting
}
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);
}
}
Here is the output:
PACREHNT
ILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
Why does this happen in the beginning?
You can't write a program that alternates between a parent and a child process with a single semaphore (without resorting to some form of busy waiting with flags or something), because both processes will race to acquire the semaphore; there is no way to predict which process will acquire it first. Your code (other than the first iteration) seems to work as expected because the child sleeps for a very long time, but technically it is still a race condition, there is no guarantee that the parent will get a chance to acquire the semaphore before the child wakes up (although it is very unlikely).
So, you need 2 semaphores: one used by the child to notify the parent that it's his turn, and another used by the parent to notify the child. To pick who starts first, initialize the corresponding semaphore to 1 (and the other to 0).
Also, this is wrong:
sem_t *sema = mmap(NULL, sizeof(sema), PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1, 0);
The 2nd argument should be sizeof(*sema) since you want to allocate memory for the semaphore object, not for the pointer.
You never #include "display.h", you probably should.
Error handling could be improved, but for this toy program I don't think it's a big deal.
Here's a working version using the 2-semaphore approach (this initializes the parent semaphore to 1, so the parent will start first):
#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)
{
int i;
/* place semaphore in shared memory */
sem_t *child_sem = mmap(NULL, sizeof(*child_sem), PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1, 0);
sem_t *parent_sem = mmap(NULL, sizeof(*parent_sem), PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1, 0);
/* create/initialize semaphore */
if ( sem_init(child_sem, 1, 0) < 0)
{
perror("sem_init");
exit(EXIT_FAILURE);
}
if (sem_init(parent_sem, 1, 1) < 0) {
perror("sem_init");
exit(EXIT_FAILURE);
}
int nloop=10;
int pid = fork();
if (pid == 0)
{
for (i = 0; i < nloop; i++)
{
if (sem_wait(child_sem) < 0)
perror("sem_wait");
display("CHILD\n");
if (sem_post(parent_sem) < 0)
perror("sem_post");
sleep(1);
}
}
else
{
for (i = 0; i < nloop; i++)
{ // parent starts waiting
if (sem_wait(parent_sem) < 0)
perror("sem_wait");
display("PARENT\n");
if (sem_post(child_sem) < 0)
perror("sem_post");
}
}
}
I removed the munmap(2) and sem_destroy(3) calls for conciseness, and because they are unnecessary since the process is exiting anyway.
Notice that both processes follow the same pattern: wait for their semaphore, do work, notify the other process' semaphore. This is a nice opportunity to do some refactoring and move it all to a function that receives the string to display, the semaphore to wait on, and the semaphore to notify afterwards.
You should also get used to compiling with -Wall.

How to use POSIX semaphores on forked processes in C?

I want to fork multiple processes and then use a semaphore on them. Here is what I tried:
sem_init(&sem, 1, 1); /* semaphore*, pshared, value */
.
.
.
if(pid != 0){ /* parent process */
wait(NULL); /* wait all child processes */
printf("\nParent: All children have exited.\n");
.
.
/* cleanup semaphores */
sem_destroy(&sem);
exit(0);
}
else{ /* child process */
sem_wait(&sem); /* P operation */
printf(" Child(%d) is in critical section.\n",i);
sleep(1);
*p += i%3; /* increment *p by 0, 1 or 2 based on i */
printf(" Child(%d) new value of *p=%d.\n",i,*p);
sem_post(&sem); /* V operation */
exit(0);
}
And the output is:
child(0) forked
child(1) forked
Child(0) is in critical section.
Child(1) is in critical section.
child(2) forked
Child(2) is in critical section.
child(3) forked
Child(3) is in critical section.
child(4) forked
Child(4) is in critical section.
Child(0) new value of *p=0.
Child(1) new value of *p=1.
Child(2) new value of *p=3.
Child(3) new value of *p=3.
Child(4) new value of *p=4.
Parent: All children have exited.
This clearly means the semaphore didn't work as it was supposed to. Can you explain how I should use semaphores on forked processes?
The problem you are facing is the misunderstanding of sem_init() function. When you read the manual page
you will see this:
The pshared argument indicates whether this semaphore is to be shared
between the threads of a process, or between processes.
If you are done reading up to this point, you will think that the non-zero value of pshared will make the semaphore inter-process semaphore. However, this is wrong.
You should continue reading and you'll understand that you have to locate the semaphore in a shared memory region. To do that, several functions can be used as
you can see below:
If pshared is nonzero, then the semaphore is shared between processes,
and should be located in a region of shared memory (see shm_open(3),
mmap(2), and shmget(2)). (Since a child created by fork(2) inherits
its parent's memory mappings, it can also access the semaphore.) Any
process that can access the shared memory region can operate on the
semaphore using sem_post(3), sem_wait(3), etc.
I find this approach as a more complicated approach than others, therefore I want to encourage people to use sem_open() instead of sem_init().
Below you can see a complete program illustrates the following:
How to allocate shared memory and use shared variables between forked
processes.
How to initialize a semaphore in a shared memory region and is used
by multiple processes.
How to fork multiple processes and make the parent wait until all of
its children exit.
#include <stdio.h> /* printf() */
#include <stdlib.h> /* exit(), malloc(), free() */
#include <sys/types.h> /* key_t, sem_t, pid_t */
#include <sys/shm.h> /* shmat(), IPC_RMID */
#include <errno.h> /* errno, ECHILD */
#include <semaphore.h> /* sem_open(), sem_destroy(), sem_wait().. */
#include <fcntl.h> /* O_CREAT, O_EXEC */
int main (int argc, char **argv){
int i; /* loop variables */
key_t shmkey; /* shared memory key */
int shmid; /* shared memory id */
sem_t *sem; /* synch semaphore *//*shared */
pid_t pid; /* fork pid */
int *p; /* shared variable *//*shared */
unsigned int n; /* fork count */
unsigned int value; /* semaphore value */
/* initialize a shared variable in shared memory */
shmkey = ftok ("/dev/null", 5); /* valid directory name and a number */
printf ("shmkey for p = %d\n", shmkey);
shmid = shmget (shmkey, sizeof (int), 0644 | IPC_CREAT);
if (shmid < 0){ /* shared memory error check */
perror ("shmget\n");
exit (1);
}
p = (int *) shmat (shmid, NULL, 0); /* attach p to shared memory */
*p = 0;
printf ("p=%d is allocated in shared memory.\n\n", *p);
/********************************************************/
printf ("How many children do you want to fork?\n");
printf ("Fork count: ");
scanf ("%u", &n);
printf ("What do you want the semaphore value to be?\n");
printf ("Semaphore value: ");
scanf ("%u", &value);
/* initialize semaphores for shared processes */
sem = sem_open ("pSem", O_CREAT | O_EXCL, 0644, value);
/* name of semaphore is "pSem", semaphore is reached using this name */
printf ("semaphores initialized.\n\n");
/* fork child processes */
for (i = 0; i < n; i++){
pid = fork ();
if (pid < 0) {
/* check for error */
sem_unlink ("pSem");
sem_close(sem);
/* unlink prevents the semaphore existing forever */
/* if a crash occurs during the execution */
printf ("Fork error.\n");
 }
else if (pid == 0)
break; /* child processes */
}
/******************************************************/
/****************** PARENT PROCESS ****************/
/******************************************************/
if (pid != 0){
/* wait for all children to exit */
while (pid = waitpid (-1, NULL, 0)){
if (errno == ECHILD)
break;
}
printf ("\nParent: All children have exited.\n");
/* shared memory detach */
shmdt (p);
shmctl (shmid, IPC_RMID, 0);
/* cleanup semaphores */
sem_unlink ("pSem");
sem_close(sem);
/* unlink prevents the semaphore existing forever */
/* if a crash occurs during the execution */
exit (0);
}
/******************************************************/
/****************** CHILD PROCESS *****************/
/******************************************************/
else{
sem_wait (sem); /* P operation */
printf (" Child(%d) is in critical section.\n", i);
sleep (1);
*p += i % 3; /* increment *p by 0, 1 or 2 based on i */
printf (" Child(%d) new value of *p=%d.\n", i, *p);
sem_post (sem); /* V operation */
exit (0);
}
}
OUTPUT
./a.out
shmkey for p = 84214791
p=0 is allocated in shared memory.
How many children do you want to fork?
Fork count: 6
What do you want the semaphore value to be?
Semaphore value: 2
semaphores initialized.
Child(0) is in critical section.
Child(1) is in critical section.
Child(0) new value of *p=0.
Child(1) new value of *p=1.
Child(2) is in critical section.
Child(3) is in critical section.
Child(2) new value of *p=3.
Child(3) new value of *p=3.
Child(4) is in critical section.
Child(5) is in critical section.
Child(4) new value of *p=4.
Child(5) new value of *p=6.
Parent: All children have exited.
It is not bad to check shmkey since when ftok() fails, it returns -1. However if you have multiple shared variables and
if the ftok() function fails multiple times, the shared variables that have a shmkey with value -1 will reside in the same
region of the shared memory resulting in a change in one affecting the other. Therefore the program execution will get messy. To avoid this, it is better checked if the ftok()
returns -1 or not (better to check in source code rather than printing to screen like I did, although I wanted to show you the key values in case there is a collision).
Pay attention to how the semaphore is declared and initialized. It's different than what you have done in the question (sem_t sem vs sem_t* sem). Moreover, you should use them as they appear in this example. You cannot define sem_t* and use it in sem_init().
Linux minimal anonymous sem_init + mmap MAP_ANONYMOUS example
I like this setup as it does not pollute any global namespace as sem_open does.
The only downside is that MAP_ANONYMOUS is not POSIX, and I don't know any replacement: Anonymous shared memory? shm_open for example takes a global identifier just like sem_open.
main.c:
#define _GNU_SOURCE
#include <assert.h>
#include <semaphore.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv) {
pid_t pid;
typedef struct {
sem_t sem;
int i;
} Semint;
Semint *semint;
size_t size = sizeof(Semint);
semint = (Semint *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
assert(semint != MAP_FAILED);
/* 1: shared across processes
* 0: initial value, wait locked until one post happens (making it > 0)
*/
sem_init(&semint->sem, 1, 0);
semint->i = 0;
pid = fork();
assert(pid != -1);
if (pid == 0) {
sleep(1);
semint->i = 1;
msync(&semint->sem, size, MS_SYNC);
sem_post(&semint->sem);
exit(EXIT_SUCCESS);
}
if (argc == 1) {
sem_wait(&semint->sem);
}
/* Was modified on the other process. */
assert(semint->i == 1);
wait(NULL);
sem_destroy(&semint->sem);
assert(munmap(semint, size) != -1);
return EXIT_SUCCESS;
}
Compile:
gcc -g -std=c99 -Wall -Wextra -o main main.c -lpthread
Run with sem_wait:
./main
Run without sem_wait:
./main 1
Without this synchronization, the assert is very likely to fail, since the child sleeps for one whole second:
main: main.c:39: main: Assertion `semint->i == 1' failed.
Tested on Ubuntu 18.04. GitHub upstream.

semaphore concurrency

In the following code, wouldn't a mutex be created, in the child, as a copy of its parent's? Hence there're two copies of mutexs now -- one in child, and one in parent. How can it be synchronized? As far as I can remember, you need one copy that are shared by multiple processes in order to make it synchronize.
#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(int argc, char **argv)
{
int fd, i,count=0,nloop=10,zero=0,*ptr;
sem_t mutex;
//open a file and map it into memory
fd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU);
write(fd,&zero,sizeof(int));
ptr = mmap(NULL,sizeof(int), PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
close(fd);
/* create, initialize semaphore */
if( sem_init(&mutex,1,1) < 0)
{
perror("semaphore initilization");
exit(0);
}
if (fork() == 0) { /* child process*/
for (i = 0; i < nloop; i++) {
sem_wait(&mutex);
printf("child: %d\n", (*ptr)++);
sem_post(&mutex);
}
exit(0);
}
/* back to parent process */
for (i = 0; i < nloop; i++) {
sem_wait(&mutex);
printf("parent: %d\n", (*ptr)++);
sem_post(&mutex);
}
exit(0);
}
You must not confuse a mutex with a semaphore. A semaphore might allow several threads/processes to access a resource, a mutex allows only ONE concurrent access to a resource.
As stated here you would need to create a named semaphore to make cross-process-synchronisation possible.
You must create a semaphore in your parent process and access is using sem_open in the child process to achieve synchronisation.

Do forked child processes use the same semaphore?

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

Resources