I have a single HW interface I want to use from two applications (processes) on the same workstation. The HW requires a single initialization call then either app uses the same function (in the same library) to do many transactions with the HW.
So each app should act like this:
main()
// I don't know if another app already init'ed the HW
ret = hw_init_lock(non-blocking)
if ret = OK
// no one else has done this, I have to
init_hw()
else
//someone else has already init'ed the HW, I gotta make sure it stays that way
//as long as I'm alive
increment_hw_init_ref_counter()
hw_trans_lock(blocking)
hw_trans()
hw_trans_unlock()
....
//exit app, uninit hw if we are last out
ret = decrement_hw_init_ref_counter()
if ret == 0
uninit_hw()
exit(0)
What is the mechanism I can use in the lock and reference count calls that is shared between two applications? I'm thinking named pipes i.e. mkfifo().
POSIX semaphore is the way to go. Since you want to share the same semaphore across processes, you need to use a named semaphore.:
A named semaphore is identified by a name of the form /somename. Two processes can operate on the same named semaphore by passing the same name to sem_open(3).
Semaphores and Mutexes/condition variables are good, very high-performance primitives which are appropriate for use in between threads or in between processes.
All of these are based on the idea (and usually, on the reality) of test-and-set or other atomic operations performed upon shared memory.
If you expect to distribute your processes over the network, then semaphores and mutexes may not be right for you--they only work on a single machine. Pipes and sockets are more generally network-extensible.
A brief summary of mutexes, condition variables and semaphores:
Mutexes
A mutex is a primitive which can be either locked, or unlocked. The process/thread which locked it must be the one to unlock it. This ownership aspect allows the operating system to apply some interesting optimizations, such as priority inheritance and priority ceiling protocol (to avoid priority inversion). however, the mutex does not have a count associated with it. You can't lock an already locked-mutex, in general, and retain memory that it was "locked twice" (there are some extensions that allow this, I think, but they are not available everywhere)
Condition Variables
A mutex is great for...well, MUTual EXclusion. But what if you need to block on a condition associated with the object to which you have mutual exclusion? For this, you use a condition variable, or CV. A CV is associated with a mutex. For example, say I have a queue of input data which my processes want to access. One grabs the mutex so it can look at the queue without fear of interference. However, it finds the queue empty and wants to wait for something to come in on the queue. It therefore waits on the "queue not empty" condition variable. The interesting part here is that, because the CV is associated with the mutex, the mutex gets automatically re-acquired once the condition variable is signalled. Thus, once the process wakes up after waiting on the CV, it knows that it has exclusive access again to the queue. What it does not know is whether the queue really has anything on it--perhaps two processes waited on the CV--one thing came in--and the first priority got in and dequeued the "thing" before the second thing woke up. Thus, whenever you use a CV, you need to RECHECK the condition, like this:
mutex_enter(m);
while (! condition) {
cond_wait(m, c); // drop mutex lock; wait on cv; reacquire mutex
}
//processing related to condition
mutex_exit(m);
Semaphores
OK, that is mutexes and condition variables. Semaphores are simpler. They can be incremented and decremented by any processes. They have memory--they count--so you can use them to determine how many of a condition have occurred. Not so with condiiton variables. Also, because semaphores can be decremented by one process and incremented by another, they do not have the ownership aspect--so no priority inheritance, no priority inversion avoidance is possible.
Now, finally--all of these mechanisms require shared memory for an efficient implementation. This may be fine for you, but be aware--if you believe that your appliction may eventually be distributed, then mutexes, condition variables and semaphores may not be for you. Pipes and sockets, while much higher-overhead, have the possibility of being extended over the network fairly straightforwardly.
Use the POSIX semaphores.
Since you only need a semaphore count of one, a mutex suffices.
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