I am hacking away at a uni assignment and have come across a problem with my code which is supposed to spawn 2 processes, where the second process waits for the 1st to complete before executing. This is what I have so far:
sem_t mutex;
int producer; int consumer;
sem_init(&mutex, 0, 1);
producer = fork();
consumer = fork();
if (producer == 0) {
if (VERBOSE) printf("Running producer\n");
/* down semaphore */
sem_wait(&mutex);
/* START CRITICAL REGION */
get_files(N);
/* END CRITICAL REGION */
/* up semaphore */
sem_post(&mutex);
if (VERBOSE) printf("Ending producer\n");
exit(0);
}
if (consumer == 0) {
if (VERBOSE) printf("Running consumer\n");
/* down semaphore */
sem_wait(&mutex);
/* START CRITICAL REGION */
/* do stuff */
/* END CRITICAL REGION */
/* up semaphore */
sem_post(&mutex);
if (VERBOSE) printf("Ending consumer\n");
exit(0);
}
/* parent waits for both to complete */
wait(NULL);
Now, I know that in the "real-world" this is really stupid. If my 'consumer' does nothing while until my 'producer' is finished, then you might as well not have 2 processes like this, but the assignment is trying to illustrate a race-condition, so that why we've been specifically told to do it this way.
So, my problem is that the consumer process isn't waiting for the producer. I assumed that since the semaphore was taken down in the producer (sem_wait(&mutex);
) then it wouldn't be available to the consumer until sem_post(&mutex);
is called in the producer.
Additionally, as best as I can tell, the line wait(NULL);
isn't waiting for both processes to complete.
Have I critically misunderstood something?
Mutexes can synchronize threads within the same process or in other processes. Mutexes can be used to synchronize threads between processes if the mutexes are allocated in writable memory and shared among the cooperating processes (see mmap(2)), and have been initialized for this task.
Mutex is a synchronization primitive that grants exclusive access to the shared resource to only one thread. If a thread acquires a mutex, the second thread that wants to acquire that mutex is suspended until the first thread releases the mutex.
The mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads.
In computer programming, a mutex (mutual exclusion object) is a program object that is created so that multiple program thread can take turns sharing the same resource, such as access to a file.
You should have error-checking on your semaphore calls. Use perror()
to display the error if sem_wait()
, sem_init()
or sem_post()
returns non-zero.
Secondly, you a creating more processes than you think. Your first fork()
results in a parent (with producer
non-zero) and a child (with producer
zero). Both processes then execute the second fork()
, so you now have four processes.
Thirdly, the sem_t
variable must be shared between the processes, so it must be stored in a shared memory region. The easiest way to achieve this is with mmap()
:
sem_t *sem = mmap(NULL, sizeof *sem, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
(Execute that prior to the sem_init()
and the first fork()
).
Forthly, it's not defined which process will run first, so you can't rely on the producer thread calling sem_wait()
before the consumer does. Instead, initialise the semaphore to zero with sem_init()
, and only call sem_wait()
in the consumer - this will block the consumer. The producer executes, and calls sem_post()
when it is done, which allows the consumer to proceed.
The sem_init()
call should specify pshared
as non-zero and value
as 0, so it should look like:
if (sem_init(sem, 1, 0) != 0) {
perror("sem_init");
exit(1);
}
Fifth, wait(NULL)
only waits for a single child process to exit. Call it twice to wait for two child processes.
Just because you fork
the producer thread first doesn't mean the OS will schedule it to run first - its quite possible that the consumer actually runs and gets the lock first.
Also, you should check the return value of sem_wait
- it is posible to return from it without holding the semaphore.
It is also quite possible (as several people have noted in comments) that semaphores may simply not work across fork
ed processes
EDIT - if you pass a non-zero value to argument 2 of sem_init(sem_t *sem, int pshared, unsigned value)
when initializing posix semaphores will work across processes
EDIT - See here for a much better explanation than i could give, complete with source code to do nearly exactly what you want
Have you provided complete code in the question?
If so, you are missing semaphore initialization. You have to call either sem_init
or sem_open
prior to using the semaphore.
Read here.
EDIT You are specifying pshared
= 0 in the sem_init
call. This makes the semaphore process-local (i.e. it can be used only to synchronize threads of one process). fork
creates a child process, so the semaphore does not do anything useful.
If pshared has value 0, then the semaphore is shared between the threads of a process. If pshared is non-zero, then the semaphore is shared between processes.
(the quote is from the link above)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With