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: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:
I find this approach as a more complicated approach than others, therefore I want to encourage people to use
sem_open()
instead ofsem_init()
.Below you can see a complete program illustrates the following:
OUTPUT
It is not bad to check
shmkey
since whenftok()
fails, it returns -1. However if you have multiple shared variables and if theftok()
function fails multiple times, the shared variables that have ashmkey
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 theftok()
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
vssem_t* sem
). Moreover, you should use them as they appear in this example. You cannot definesem_t*
and use it insem_init()
.Linux minimal anonymous
sem_init
+mmap
MAP_ANONYMOUS
exampleI 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 likesem_open
.main.c:
Compile:
Run with
sem_wait
:Run without
sem_wait
:Without this synchronization, the
assert
is very likely to fail, since the child sleeps for one whole second:Tested on Ubuntu 18.04. GitHub upstream.