Linux kernel design and implementation--kernel synchronization

Source: Internet
Author: User
Tags mutex semaphore

Kernel synchronization
Synchronous introduction
The concept of synchronization

Critical section: Also known as a critical segment, is the code snippet that accesses and operates shared data.

Competition conditions: 2 or more than 2 threads are executed simultaneously in the critical section, which constitutes a competitive condition.

The so-called synchronization, in fact, prevent the formation of competitive conditions in the critical area.

If the critical section is atomic (that is, the entire operation is not interrupted before it is completed), then naturally there is no competitive condition. However, in practical applications, the code in the critical section is often not so simple, so in order to maintain synchronization, the locking mechanism is introduced. But there are some questions about locks.

Deadlock-generated condition: to have one or more execution threads and one or more resources, each thread waits for one of the resources, but all resources are occupied. So threads wait for each other, but they never release the resources that they already possess. So no thread can continue, and a deadlock occurs.

Self-deadlock: If an execution thread tries to get a lock that it has already held, it has to wait for the lock to be released. But because it is busy waiting for this lock, so I will never have the opportunity to release the lock, the deadlock produced.

Starvation (starvation) is a phenomenon that a thread cannot perform for a long time without the resources it needs.

Causes of concurrency

Interrupts-interrupts can occur almost at any point in time, which means that code that is currently running can be interrupted at any time.

Soft interrupts and tasklet--cores can wake up or dispatch interrupts and tasklet at any moment, interrupting code that is currently executing.

Kernel preemption-because the kernel is preemptive, tasks in the kernel may be preempted by another task.

Sleep and user space synchronization--processes that execute in the kernel may sleep, which wakes the scheduler and causes a new user process execution to be dispatched.

Symmetric multi-processing-two or more processors can execute code at the same time.

Simple rules for avoiding deadlocks

The order in which the locks are added is key. When using nested locks, you must ensure that the locks are acquired in the same order, which prevents deadlocks of the deadly hug type. It is best to record the order of the locks so that others can use them in this order.

Prevent the occurrence of hunger. Determine if the execution of this code will end. If a does not occur, will b always wait?

Do not request the same lock repeatedly.

The more complex the locking scheme, the more likely it is to cause deadlocks. ---design should be simple.

The size of the lock

The lock granularity is used to describe the data size of the lock protection. A coarse lock protects large chunks of data, such as all data structures of a subsystem; a fine lock protects small pieces of data, such as an element in a large data structure.

In addition to lock, not only to avoid deadlocks, but also to consider the size of the lock.

The granularity of the lock has a great impact on the scalability of the system, and when locking, consider whether the lock will be frequently used by multiple threads.

If a lock is likely to be subject to frequent contention, you need to refine the granularity of the lock.

Finer locks can improve performance in multi-processor scenarios.


Synchronization method
Atomic operation

Atomic manipulation refers to operations that are not interrupted by other code paths during execution, and the kernel code can safely call them without interruption.

Atomic operations are divided into integer atomic operations and bit-atomic operations.

Spinlock Spin Lock

The spin lock feature is that when a thread acquires a lock, other threads trying to acquire the lock are looping around to acquire the lock until the lock is re-usable.

Since threads actually get this lock in a loop, it can be a waste of CPU processing time, so it's best to use a spin lock for critical sections that can be processed very quickly.

There are 2 points to note when using the spin lock:

1. Spin lock is not recursive, recursive request the same spin lock will Self-lock itself.

2. To suppress interrupts on the current processor before the thread acquires the spin lock. (prevents the thread that acquires the lock and the interrupt form a race condition) For example: After the current thread acquires a spin lock, the interrupt handler is interrupted in the critical section, and then the interrupt handler waits for the lock to be released. The current thread is also waiting for the code to execute the critical section and release the lock after the interrupt has been executed.

The use of a spin lock in the operation of the lower half of the interrupt handling is especially necessary for caution:

1. When the lower half of processing and the process context share data, the process context will suppress the execution of the lower half of the process context, and allow the execution of the lower half when unlocked, because the processing of the lower part can preempt the code of the processes contexts.

2. When the interrupt handler (top half) and the lower half process shared data, because interrupt processing (the upper part) can preempt execution of the lower half, the second half disables interrupt processing (the top half) before locking the shared data, allowing the execution of interrupts when unlocked.

3. The same tasklet cannot run concurrently, so the shared data in the same tasklet does not need to be protected.

4. When sharing data in different classes of tasklet, one of the Tasklet obtains the lock without prohibiting the execution of other tasklet because there is no tasklet preemption on the same processor

5. Soft interrupts of the same type or non-identical type when sharing data, do not prohibit the lower half, because there is no soft interrupt on the same processor to preempt each other situation

Read-Write Spin lock

If the data protected by the critical section is readable and writable, then as long as there is no write operation, concurrent operations can be supported for read. For this requirement that only write operations are mutually exclusive, it is obviously not enough to use a spin lock (it is too wasteful for read operations). This kernel provides another kind of lock-read/write spin lock, read spin lock is also called shared spin Lock, write spin lock is also called exclusive spin lock.

Read/write spin lock is a smaller than the spin lock mechanism, it retains the concept of "spin", but in the write operation, can only have a write process, in the read operation, while there can be more than one read execution unit, of course, read and write cannot simultaneously.

Spin locks provide a fast and simple way to achieve the results. If the lock time is not long and the code does not sleep, the use of spin locks is the best choice. If the lock time may be long or the code may sleep while holding the lock, it is better to use the semaphore to complete the lock function.

Signal Volume

The semaphore in Linux is a sleep lock, and if a task attempts to acquire a semaphore that is already occupied, the semaphore pushes it into a waiting queue and then sleeps, where the processor can regain its freedom to execute other code, and when the semaphore-holding process releases the semaphore, Which task in the waiting queue is awakened and gets the semaphore.

1) Since the process of contention for semaphores will sleep when waiting for the lock to become available, the semaphore is applied to the case where the lock is held for a long time, whereas the signal volume is less appropriate when the lock is held for a short time. Because sleep, maintenance wait queues, and wake-up costs may take longer than the total amount of time the lock is occupied.

2) because the execution thread sleeps when the lock is contended, the semaphore lock can only be acquired in the context of the process because the interrupt context is not scheduled.
3) You can go to sleep when you have a semaphore, because when another process tries to get the same semaphore, it does not deadlock (because the process is just going to sleep and will eventually continue).

4) You cannot use a spin lock while you are using the semaphore. Because you may sleep while you wait for semaphores, you are not allowed to sleep when you hold a spin lock.

5) The semaphore allows any number of lock holders at the same time, while the spin lock allows a maximum of one task to hold it at a time. The reason is that the semaphore has a count value, such as a count value of 5, indicating that there can be 5 threads accessing the critical section. If the initial value of the semaphore starts at 1, the semaphore is the mutex (mutex). For a non-0-value semaphore greater than 1, it can also be called a count semaphore (counting semaphore). The amount of semaphores used for a generic driver is a mutually exclusive semaphore.

Semaphores support two atomic operations: p/v Primitive operation (also known as down operation and up operation):

P: If the semaphore value is greater than 0, the value of the semaphore is decremented, the program continues execution, otherwise, the sleep wait semaphore is greater than 0.

V: Increments the value of the semaphore, or wakes the waiting process if the value of the incremented semaphore is greater than 0.

The down operation is available in two versions, respectively, for sleep interruption and sleep interruption.

Read-Write signal volume

The relationship between read and write semaphores and semaphores is similar to that between read and write spin locks and normal spin locks.

Read-write Semaphore is a two-value signal, that is, the maximum count of 1, increase the reader, the counter is unchanged, increase the writer, the counter is reduced by one. That is to say, read and write semaphore protection of the critical area, at most only one writer, but can have multiple readers.

All read-write lock sleep is not interrupted by a signal, so it has only one version of the down operation.

Knowing when to use spin locks and semaphores is important for writing good code, but in most cases it does not require much consideration because only spin locks are used in the interrupt context and only semaphores are used when the task sleeps.


Completion variables

Recommended Lock-up method

Low Overhead plus lock

Priority use of spin lock

Short-term plus lock

Priority use of spin lock

Long-term lock

Priority use of semaphores

Interrupt Context plus lock

Use spin lock

Holding a lock requires sleep

Using semaphores

Completion variables

if a task in the kernel needs to signal that a particular event has occurred on another task, take advantage of the completion variable ( Completion Variable ) is a simple way to synchronize two tasks. If a task is performing some work, another task waits on the completion variable. When the task is finished, it uses the completion variable to wake up the waiting task. For example, when a child process executes or exits, thevfork () system call wakes the parent process using the completion variable.

Seq Lock (Sequential Lock)

This lock provides a very simple mechanism for reading and writing shared data. The implementation of such a lock relies primarily on a sequence counter. When there is doubt that the data is written, a lock is obtained and the sequence value is incremented. The serial number is read before and after the data is read. If the value of the serial number being read is the same, the description is not interrupted by a write operation during the read operation. In addition, if the read value is an even number, then it indicates that the write operation did not occur (understand that because the initial value of the lock is 0, the write lock causes the values to be odd, and the release becomes even).

The SEQ lock helps provide a very lightweight and extensible look when multiple readers and a few write-in share a lock. But the SEQ lock is more advantageous to the writer, as long as there is no other writer, the write lock can always be successfully obtained. The pending writer will continuously make the read operation loop (the previous example) until there is no longer any writer to hold the lock.

Prohibit preemption

Because the kernel is preemptive, processes in the kernel can stop at any point in time to run another process with a higher priority. This means that a task and a preempted task may run in the same critical section. To avoid this, kernel preemption code uses spin locks (which can prevent true concurrency and kernel preemption on multiprocessor machines) as a token of a non-preemptive zone. If a spin lock is held, the kernel cannot be preempted.

In practice, there are situations where it is not necessary to emulate true concurrency on a multiprocessor machine, but to prevent kernel preemption, it does not require a spin lock, but it still needs to turn off kernel preemption. In order to solve this problem, kernel preemption can be forbidden through preempt_disable. This is a function that can be nested and can be called any time. Each invocation must have a corresponding preempt_enable call. Kernel preemption is only re-occupied when the last preempt_enable is called.

Order and barrier

For a piece of code, the compiler or processor may perform some optimizations on the execution order as it is compiled and executed, which makes the execution order of the code somewhat different from the code we write.

In general, this is not a problem, but in the concurrency conditions, the obtained values may be inconsistent with the expected value, such as the following code:

/  * * Thread A and thread B share variables A and b * Initial values a=1, b=2 */int a = 1, b = 2;/* * Assume thread A and B operations */void thread_a () {    a = 5;    b = 4;} /* * Assume that the operations of A and B in thread B */void thread_b () {    if (B = = 4)        printf ("a =%d\n", a);}

Because of optimizations in the compiler or processor, the order of assignment in thread A may be assigned by B before a value is assigned.

So if thread A is b=4; The execution is complete, a=5; Before execution, thread B begins execution, and thread B prints the initial value of a of 1.

This is inconsistent with what we expected, we expect a to be assigned before B, so thread B either does not print the content, and if it prints, the value of a should be 5.

In some cases of concurrency, a series of barrier methods are introduced to prevent compiler and processor optimizations in order to ensure code execution.

Method

Describe

RMB ()

Prevents a reorder of loading actions that span barriers

Read_barrier_depends ()

Prevent loading actions with data dependencies that span barriers from reordering

WMB ()

Prevent reordering of storage actions that span barriers

MB ()

Prevents loading and storage actions across barriers from reordering

SMP_RMB ()

RMB () function on SMP with barrier () function on up

Smp_read_barrier_depends ()

Read_barrier_depends () feature on SMP with barrier () function on up

SMP_WMB ()

WMB () feature on SMP with barrier () function on up

SMP_MB ()

MB () feature on SMP with barrier () function on up

Barrier ()

Prevents the compiler from crossing barriers to optimize loading or storage operations

To make the above small example work correctly, modify the function of thread A with the functions in the table above:

/* * Assume that the operations of A and B in thread a */void thread_a () {    a = 5;    MB ();      /* * MB () ensures that all operations of loading and storing values prior to the loading and storing of values (value is 4) of B are     complete (i.e. a = 5; completed)     * As long as the assignment of a is guaranteed before the assignment of B, Then thread B executes the same result as expected     . */    B = 4;}

Summarize:

From http://www.cnblogs.com/wang_yb/archive/2013/05/01/3052865.html

Reference:

Http://www.cnblogs.com/wang_yb/archive/2013/05/01/3052865.html

Http://www.cnblogs.com/pennant/archive/2012/12/28/2833383.html

Linux kernel design and implementation

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.