Kernel synchronization and mutex Analysis Report

Source: Internet
Author: User
Article Title: synchronization and mutex Analysis Report in the kernel. Linux is a technology channel of the IT lab in China. Including desktop applications, Linux system management, kernel research, embedded systems, open source, and other basic categories

First, let's look at the mutex between processes. In the Linux kernel, the semaphore mechanism and the spin_lock mechanism are used. Main
The difference is that in the semaphore mechanism, when the critical section cannot be entered, the process will be switched, while the spin_lock is just executed.
Busy (in SMP ).
First look at the semaphore mechanism in the kernel. The premise is to increase or decrease the atomic operation of the reference count. Kernel
To
The data structure of mic_t and a series of operations on it, such as atomic_add () and atomic_sub. (
Defined in atomic. h)
The semaphone mechanism is mainly implemented through up () and down () operations.
The semaphone structure is
Struct semaphore {
Atomic_t count;
Int sleepers;
Wait_queue_head_t wait;
};
The corresponding down () function is
Static inline void down (struct semaphore * sem)
{
/* 1 */sem-> count --; // atomic operation
If (sem-> count <0)
{
Struct task_struct * tsk = current;
DECLARE_WAITQUEUE (wait, tsk );
Tsk-> state = TASK_UNINTERRUPTIBLE;
Add_wait_queue_exclusive (& sem-> wait, & wait );
Spin_lock_irq (& semaphore_lock );
/* 2 */sem-> sleepers ++;
For (;;){
Int sleepers = sem-> sleepers;
/*
* Add "everybody else" into it. They aren't
* Playing, because we own the spinlock.
*/
/* 3 */if (! Atomic_add_negative (sleepers-1, & sem-> count )){
/* 4 */sem-> sleepers = 0; // then sem-> count = 0
Break;
}
/* 4 */sem-> sleepers = 1;/* us-see-1 above * // then sem
-> Count
=-1
Spin_unlock_irq (& semaphore_lock );
Schedule ();
Tsk-> state = TASK_UNINTERRUPTIBLE;
Spin_lock_irq (& semaphore_lock );
}
Spin_unlock_irq (& semaphore_lock );
Remove_wait_queue (& sem-> wait, & wait );
Tsk-> state = TASK_RUNNING;
Wake_up (& sem-> wait );
}
}
The corresponding up () function is
Void up (struct semaphore * sem)
{
Sem-> count ++; // atomic operation
If (sem-> count <= 0)
{
// Wake up a qualified process in the waiting queue (because the TASK_EXCLUSIVE flag is added to each process)
.
};
Assume that at the beginning, count = 1; sleepers = 0
When process A executes down (), it references count --. If its value is greater than or equal to 0
Returns. If count is less than 0, the state of A is changed to TASK_INTERRUPTIBLE, and then enters the semaphore.
Wait for the queue to be in, and make sleepers ++; then re-calculate count = sleepers-1 + count. If you reference
The count is still less than 0 (generally-1, because count =-sleepers, but in the SMP structure, do not
The process may execute up () and down () so that the reference count value may change), then the process is switched.

When process A gets another chance to run, it first executes the wake_up (& sem-> wait) operation to wake up one of the waiting queues.
Processes, and then it enters the critical section, when the critical section is out, execute the up () operation, make sem-> count ++, (if
Process A is returned directly from the down (), because the waiting queue must be empty, so it does not need to execute the wake_up () Operation
And run the up () operation to make the sem-> count ++) operation ). Then
If the value of count is less than or equal to 0, it indicates that there is another process during the critical section (maybe it enters
When the process is sleep, the wake_up () operation is executed. Otherwise, if the count value is already
If the number is greater than 0, it indicates that there are no other processes in the critical section (including those that have been awakened when it enters the critical section ).
That process) goes to sleep, then it can directly return.
From the wake-up process, we can see that if the wake-up process does not execute up (), it will get a running opportunity,
Then it recalculates count = sleepers-1 + count =-1; then sleepers is assigned 1;
Scheduling is required to give the running opportunity to other processes and sleep on their own. This is exactly what happens when the process that wakes it up is
When running in the critical section.
If the process that wakes up it executes the up () operation, it will get a running opportunity.
When no other processes run down () during the critical section, the value of count is still 0 before the process runs up ().
In up (), it is unnecessary to execute the wake_up () function.
An example is provided to illustrate the specific implementation. Start sem-> count = sem-> sleepers = 0. That is
Lock but no waiting queue (a process is running ). Three down () operations and three up (
) Operation, as follows:
For convenience, only some steps that change sleepers and count values are retained, and the steps are followed from left to right.
.
Down1:
Count (0->-1), sleepers (0-> 1), sleepers-1 + count (-1), count (-1), sleepers (1), scheduling
Down2:
Count (-1->-2), sleepers (1-> 2), sleepers-1 + count (-1), count (-1), sleepers (1), scheduling

Down3:
Count (-1->-2), sleepers (1-> 2), sleepers-1 + count (-1), count (-1), sleepers (1), scheduling

Up1:
Count (-1-> 0), wake up a sleep process (set to 1), (process 1 gets the opportunity to run) sleepers-1 + count
(0), count (0), sleepers (0), break,
Wake up another sleep process (set to 2 ),
(Process 2 gets the opportunity to run) sleepers-1 + count (-1), count (-1), sleepers (1), scheduling (not up
Condition, you have to go to bed again)
It may also be like this:
Up1 ':
Count (-1-> 0), wake up a sleep process (set to 1), (process 1 gets the opportunity to run) sleepers-1 + count
(0), count (0), sleepers (0), break,
Wake up another sleep process (set to 2 ),
Process 2 will get the opportunity to run later)
Up2:
Count (-1-> 0), (because count <= 0) Wake up a sleep process (set to 2 ),
Process 2 gets the opportunity to run) sleepers-+ count (0), count (0), sleepers (0), break,
Wake up another sleep process (set to 3 ),
Process 3 get the opportunity to run) sleepers-1 + count (-1), count (-1), sleepers (1), scheduling (not up
And you have to go to bed again)
Corresponding to the above 1 ':
Up2 ':
Count (0-> 1), (return directly because count> 0)
Process 2 gets the opportunity to run) sleepers-1 + count (0), count (0), sleepers (0), break,
Wake up another sleep process (set to 3)
Up3:
Count (-1-> 0), (because count <= 0) Wake up a sleep process (set to 3 ),
Process 3 gets the opportunity to run) sleepers-1 + count (0), count (0), sleepers (0), break,
Wake up another sleep process (now there is no process in the queue)
Process 3 stops running and runs up () to make count = 1. Then it becomes unlocked)
Corresponds to 2' above ':
Up3 ':
Count (0-> 1), (return directly because count> 0)
Process 3 gets the opportunity to run) sleepers-1 + count (0), count (0), sleepers (0), break,
Wake up another sleep process (now there is no process in the queue)
Process 3 stops running and runs up () to make count = 1. Then it becomes unlocked)
Of course, another case is that the up () operation and down () operation are handed in,
The general rule is that if a process has another process in the critical section (no matter which process, the new process
The wake-up one) will change the count value from 0 to-1, and the process will execute up in the critical section.
() To ensure that all processes can be awakened, because it is okay to wake up multiple times.
. If no other process goes to sleep during the critical section, it will be used for up () execution from the critical section.
Don't execute wake_up () (of course, it does not affect the execution, but it is redundant ).
Why do we need to separate wake_up () from count ++? We can see from up1 above. For example, process 2 is the first
When you get the opportunity to run it, the process that wakes it up has not been executed up (), but other processes may have been executed.
Up (), so it is possible to find count = 1, then it really does not need to go to bed, so count = sl
Eepers = 0, and you can continue to execute.
We can also see that, in general, the value range of (count, sleepers) is (n, 0) [n> 0] and (0
, 0
) And (1,-1 ).
Let's take a look at the spin_lock mechanism.
The Spin_lock method is used to run one process, while the other process is busy, because there is only one cpu
Hosts
At the micro level, only one process is running. So in the UP, both the spin_lock and the spin_unlock are empty.
.
In SMP, spin_lock () and spin_unlock () are defined as follows.
Typedef struct {
Volatile unsigned int lock;
} Spinlock_t;
Static inline void spin_lock (spinlock_t * lock)
{
_ Asm _ volatile __(
"\ N1: \ t"
"Lock; decb % 0 \ n \ t"
"Js 2f \ n" // lock-> lock <0, jmp 2 forward
". Section. text. lock, \" ax \ "\ n"
"2: \ t"
"Cmpb $0, % 0 \ n \ t" // wait lock-> lock = 1
"Rep; nop \ n \ t"
"Jle 2b \ n \ t"
"Jmp 1b \ n"
". Previous"
: "= M" (lock-> lock): "memory ");
}
Static inline void spin_unlock (spinlock_t * lock)
{
_ Asm _ volatile __(
"Movb $1, % 0"
: "= M" (lock-> lock): "memory"); // lock-> lock = 1
}
This is generally used as follows:
# Define SPIN_LOCK_UNLOCKED (spinlock_t) {1}
Spinlock_t xxx_lock = SPIN_LOCK_UNLOCKED;
Spin_lock _ (& xxx_lock)
...
Critical section
...
Spin_unlock (& xxx_lock)
It can be seen that both the semaphore mechanism and the semaphore mechanism solve the mutex problem between two processes, and both allow a process to exit.

The method that another process enters only after the demarcation area, but the sempahore mechanism is used to temporarily let the process out of the CPU
The policy of waiting in the queue, while the implementation of the spin_lock is but the process is idling in the original place, waiting for another process to complete

Bundle policy.
The impact of interruption on the critical section is considered below. To be mutually exclusive, there are also processes and service interruption programs. When a process
Code in a critical section may be interrupted, and the interrupt function may call the code in this critical section.
If it is not allowed to enter, a deadlock will occur. In this case, an effective method is to break down.
# Define local_irq_save (x) _ asm _ volatile _ ("pushfl; popl % 0;
Cli ":
"= G" (x):/* no input */: "memory ")
# Define local_irq_restore (x) _ asm _ volatile _ ("pushl % 0; popfl ":/*
No
Output */: "g" (x): "memory ")
# Define local_irq_disable () _ asm _ volatile _ ("cli": "memory ")
# Define local_irq_enable () _ asm _ volatile _ ("sti": "memory ")
# Define cpu_bh_disable (cpu) do {local_bh_count (cpu) ++; barrier () ;}while (
0)
# Define cpu_bh_enable (cpu) do {barrier (); local_bh_count (cpu) --;} while
(0
)
# Define local_bh_disable () cpu_bh_disable (smp_processor_id ())
# Define local_bh_enable () cpu_bh_enable (smp_processor_id ())
For UP, the above is enough, but for SMP, it is also necessary to prevent the impact from other CPUs, this
The solution can also integrate the above spin_lock mechanism.
# Define spin_lock_irqsave (lock, flags) do {
Local_irq_save (flags); sp
In_lock (lock);} while (0)
# Define spin_lock_irq (lock) do {local_irq_disable ();
Spin_lock (lo
Ck);} while (0)
# Define spin_lock_bh (lock) do {local_bh_disable ();
Spin_lock (loc
K);} while (0)
# Define spin_unlock_irqrestore (lock, flags) do {spin_unlock (lock); local_ I
Rq_restore (flags);} while (0)
# Define spin_unlock_irq (lock) do {spin_unlock (lock );
Local_irq_enable ();
} While (0)
# Define spin_unlock_bh (lock) do {spin_unlock (lock );
Local_bh_enable ();
} While (0)
As mentioned above, for UP, spin_lock () is empty, so the above definitions apply to UP and SM together.
P.
Read_lock_irqsave (lock, flags), read_lock_irq (lock ),
Read_lock_bh (lock) and
Write_lock_irqsave (lock, flags), write_lock_irq (lock ),
Write_lock_bh (lock
) Is a small variant of the spin_lock.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.