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
.