Output Table 7: semaphores (semaphores are actually the same as spin locks, that is, they are a little different: When the semaphores are not obtained, the process does not repeat in the same place but enters the sleep waiting state)
In Linux, there are four main semaphore-related operations:
1) define the semaphore struct semaphore sem;
2) initialize the semaphore
Void sema_init (struct semphore * sem, int val); // set sem to val
Void init_MUTEX (struct semaphore * sem); // initialize a mutex semaphores. sem is set to 1.
Void init_MUTEX_LOCKED (struct semaphore * sem); // initialize a mutex semaphores. The sem value is set to 0.
DECLARE_MUTEX (name); // This macro defines the semaphore name and initializes 1
DECLARE_MUTEX_LOCKED (name); // This macro defines the semaphore name and initializes 0
3) obtain the semaphore
Void down (struct semaphore * sem); // This function is used to obtain semaphores sem, which can cause sleep and cannot be interrupted by signals, so it cannot be used in the interrupt context.
Int down_interruptible (struct semaphore * sem); // This function is returned because the sleeping process can be interrupted by a signal.
Int down_trylock (struct semaphore * sem); // try to obtain the semaphores sem. If yes, get and return 0; otherwise, return non-0, which does not cause the caller to sleep, can be used in interrupt Context
Generally
If (down_interruptible (& sem ))
{
Return-ERESTARTSYS;
}
4) Release semaphores
Void up (struct semaphore * sem); // release semaphores sem to wake the waiting person
Semaphores are generally used as follows:
// Defines the semaphore.
DECLARE_MUTEX (mount_sem );
Down (& mount_sem); // obtain the semaphore to protect the critical section
...
Critical section // critical section
...
Up (& mount_sem );
Now, let's take an example to see how to use semaphores to enable a device only by one process:
Static DECLARE_MUTEX (xxx_lock); // defines the mutex.
Static int xxx_open (struct inode * inode, struct file * filp)
{
...
If (down_trylock (& xxx_lock) // obtain the unlock lock
Return-EBUSY; // The device is busy.
...
Return 0; // success
}
Static int xxx_release (struct inode * inode, struct file * filp)
{
Up (& xxx_lock); // release the unlock lock
Return 0;
}
In the above-mentioned operations on semaphores, we mentioned a problem that is the semaphores initialization. Generally, there is no limit on semaphores initialization, but if the semaphores are initialized to 0, it can be used for synchronization. Here, we have to introduce a new trick.
Output Table 8: synchronization (it means that the execution of one execution unit needs to wait for another Execution Unit to complete its tasks, ensuring the execution order)
In this figure, before execution unit A executes code Area B, it is necessary to wait for Execution Unit B to execute code Unit c, and the semaphore can help to complete this synchronization process.
At this time, you may ask, synchronization like this is often encountered in reality. for example, you must wait for the previous one to complete the last one .. wait, is it a synchronization method ..
Haha, it's really smart. The little dish has finally grown up and become a big dish. in fact, the Linux system provides a better synchronization mechanism-the amount of completion. Well, if you are eager to study, it will be passed to you together, no charge.
Table 9: completion, which is used for one execution unit to wait for another execution unit to finish
Usage: 1) define the completion volume
Struct completion my_completion;
2) initialization
Init_completion (& my_completion); // If you find these two steps troublesome, you will be given a macro to define and initialize DECLARE_COMPLETION (my_completion );
3) waiting for completion
Void wait_for_completion (structcompletion * c); // wait for a completion to be awakened
Wait_for_completion_interruptible (struct completion * c); // wait_for_completion
Unsigned long wait_for_completion_timeout (struct completion * x, unsigned long timeout); // wait_for_completion with timeout
4) Number of wake-up completions
Void complete (struct completion * c); // wake up only one waiting execution unit.
Void complete_all (struct completion * c); // wake up all execution units waiting for this completion
If I have said so much about this memory, how can I forget to provide the completion structure:
Struct completion
{
Unsigned int done; // indicates whether it has been completed. If it is not finished, It is a negative value, indicating the number of waits. The value is 0.
Wait_queue_head_t wait;
}
If you do not understand it or are not addicted to it, I will give you a picture:
How is this feeling? Much better...
To be honest, I didn't even think it would take so long. Although I don't want to, I can only say: Let's talk about things later ..