Linux Synchronization Method Analysis

Source: Internet
Author: User
Tags bitmask

This article discusses a large number of synchronization or lock mechanisms available in the Linux kernel. These mechanisms provide application interfaces (APIS) for many available methods of kernel 2.6.23 ). However, before learning APIs in depth, you must first understand the problems to be solved.

Concurrency and lock

The synchronization method must be used when concurrent features exist. When two or more processes exist in the same time period and they interact with each other (for example, share the same resource ),ConcurrencySymptom.

Concurrency may occur on a single processor (uniprocessor, UP) host, where multiple threads share the same CPU and preemption creates a race condition.PreemptionThe CPU is shared by temporarily interrupting one thread and executing another thread.Race ConditionWhen two or more threads manipulate a shared data item, the result depends on the execution time. Concurrency also exists in the multi-processor (MP) computer, where each thread that shares the same data is executed simultaneously. Note that in the case of MP, real parallelism (Parallelism) exists, because the thread is executed at the same time. In the case of UP, the parallel operation is created through preemption. It is difficult to implement concurrency in both modes.

The Linux kernel supports concurrency in both modes. The kernel itself is dynamic and there are many ways to create competing conditions. The Linux kernel also supports multiprocessing (SMP ).

The concept of critical segments is generated to solve the problem of competitive conditions. OneCritical SectionIs a protected code that does not allow multiple access. This code can manipulate shared data or shared services (such as hardware peripherals ). During the critical segment operation, the mutual exclusion principle is adhered to (when a thread is in the critical segment, all other threads cannot enter the critical segment ).

One problem to be solved in the critical segment is the deadlock condition. Consider two independent critical segments to protect different resources. Each resource has a lock, which is called A and B in this example. Assume that two threads need to access these resources. Thread x acquires lock a and thread y acquires lock B. When these locks are held, each thread tries to occupy the lock currently held by other threads (thread X wants to lock B, thread y wants to lock ). At this time, the threads are deadlocked because they both hold a lock and want other locks. A simple solution is to always obtain locks in the same order, so that one of the threads can be completed. Other solutions are required to detect this situation. Table 1 defines some important concurrent terms used here.

Table 1. Important definitions of concurrency

Terms Definition
Race Condition When two or more threads operate resources at the same time, inconsistent results will occur.
Critical Section A code segment used to coordinate access to shared resources.
Mutex lock Software features that ensure exclusive access to shared resources.
Deadlock A special situation caused by two or more processes and resource locks can reduce the efficiency of processes.

Linux Synchronization Method

If you understand some basic theories and understand the problems that need to be solved, you will learn the methods for supporting concurrency and mutex locks in Linux. Previously, mutex locks were provided by disabling interrupt, but the locking efficiency in this form is relatively low (this still exists in the kernel ). This method cannot be extended, and the mutex lock on Other Processors cannot be guaranteed.

In the following discussion about the locking mechanism, we first take a look at the Atomic operators, which can protect simple variables (counters and bitmask )). Then we will introduce the simple spin locks and read/write locks, which constitute an SMP architecture of busy wait locks (busy-Wait lock) coverage. Finally, we will discuss the kernel mutex lock built on the atomic API.

Atomic operation

The simplest synchronization method in Linux is atomic operation.AtomThis means that the critical segment is included in the API function. No additional lock is required because the API function already contains a lock. Since C cannot implement atomic operations, Linux relies on the underlying architecture to provide this function. Various underlying architectures are very different, so the implementation methods of atomic functions are also different. Some methods are fully implemented through the assembly language, while others rely on the C language and uselocal_irq_saveAndlocal_irq_restoreDisable interruption.

Old locking method
One bad way to implement locking in the kernel is to disable hard interruptions of the local CPU. These functions can be used and are still used (sometimes used for atomic operators), but we do not recommend them.local_irq_saveThe routine disables interruption, whilelocal_irq_restoreRestores previously enabled interruptions. These routines are reentrant, that is, they can be called in the context of other routines.

When the data to be protected is very simple, such as a counter, atomic operators are an ideal method. Despite its simple principle, the atomic API provides many operators for different situations. The following is an example of using this API.

To declare an atomic variable (atomic variable), first declareatomic_tType variable. This structure contains a singleintElement. Next, make sure that your atomic variables are used.ATOMIC_INITThe symbol constant is initialized. In the case of Listing 1, the atomic counter is set to 0. You can also useatomic_set functionInitialize atomic variables at runtime.

Listing 1. Creating and initializing atomic variables

                atomic_t my_counter ATOMIC_INIT(0);... or ...atomic_set( &my_counter, 0 );

The atomic API supports a rich function set that covers many use cases. Availableatomic_readYou can also useatomic_addAdd a specified value to a variable. The most common operation is to useatomic_incIncrease the number of variables. You can also use the minus sign operator, which is opposite to the addition and increment operations. Listing 2 demonstrates these functions.

Listing 2. Simple arithmetic atomic Functions

                val = atomic_read( &my_counter );atomic_add( 1, &my_counter );atomic_inc( &my_counter );atomic_sub( 1, &my_counter );atomic_dec( &my_counter );

This API also supports many other common use cases, including operate-and-test routines. These routines allow you to manipulate and test atomic variables (as an atomic operation ). Aatomic_add_negativeIs added to the atomic variable, and returns true if the result value is negative ). This is used by semaphores that depend on the architecture in the kernel.

Many functions do not return variable values, except for the two functions. They return results (atomic_add_returnAndatomic_sub_return), As shown in listing 3.

Listing 3. Operate-and-test atomic Functions

                if (atomic_sub_and_test( 1, &my_counter )) {  // my_counter is zero}if (atomic_dec_and_test( &my_counter )) {  // my_counter is zero}if (atomic_inc_and_test( &my_counter )) {  // my_counter is zero}if (atomic_add_negative( 1, &my_counter )) {  // my_counter is less than zero}val = atomic_add_return( 1, &my_counter ));val = atomic_sub_return( 1, &my_counter ));

If your architecture supports 64-bit long type (BITS_PER_LONGIs 64), then you can uselong_t atomicOperation. You can view available long operations in Linux/include/ASM-generic/Atomic. h ).

The atomic API also supports bitmask operations. Unlike the arithmetic operation mentioned above, it only contains setting and clearing operations. Many drivers use these atomic operations, especially SCSI. Bitmask atomic operations are slightly different from arithmetic operations because there are only two available operations (set the mask and clear the mask ). Before using these operations, you need to provide a value and a bitmask for the operation to be performed, as shown in Listing 4.

Listing 4. bitmask atomic Functions

                unsigned long my_bitmask;atomic_clear_mask( 0, &my_bitmask );atomic_set_mask( (1<<24), &my_bitmask );

Atomic API prototype
Atomic operations depend on the architecture and can be found in./Linux/include/ASM-/Atomic. h.

Spin lock

The spin lock is a special method to use the wait lock to ensure the mutex lock. If the lock is available, the lock is obtained, the mutex lock is executed, and the lock is released. If the lock is unavailable, the thread will wait for the lock until it is available. Busy waiting seems inefficient, but it is actually much faster than sleeping a thread and then waking it up when the lock is available.

Spin locks are only useful in SMP systems, but it is wise to add them to the up system because your code will eventually run on the SMP system.

Spin locks are available in two forms: Full locks and read/write locks. First, let's take a look at the complete lock.

First, create a new spin lock with a simple declaration. You can callspin_lock_init. Each variable shown in listing 5 implements the same result.

Listing 5. Create and initialize a spin lock

                spinlock_t my_spinlock = SPIN_LOCK_UNLOCKED;... or ...DEFINE_SPINLOCK( my_spinlock );... or ...spin_lock_init( &my_spinlock );

After the spin lock is defined, a large number of locked variables can be used. Each variable is used in different contexts.

As shown in Listing 6spin_lockAndspin_unlockVariable. This is the simplest variable. It does not disable interruption, but contains all the memory barriers (memory barrier ). This variable assumes that there is no interaction between the interrupt handler and the lock.

Listing 6. spin lock and unlock Functions

Spin_lock (& my_spinlock );

// Critical section

Spin_unlock (& my_spinlock );

NextirqsaveAndirqrestoreYes, as shown in listing 7.spin_lock_irqsaveThe function requires a spin lock and the interrupt is disabled on the local processor (in the SMP case.spin_unlock_irqrestoreThe function releases the spin lock andflagsParameter.

Listing 7. spin lock variables with local CPU interruptions disabled

                spin_lock_irqsave( &my_spinlock, flags );// critical sectionspin_unlock_irqrestore( &my_spinlock, flags );

spin_lock_irqsave/spin_unlock_irqrestoreA less secure variant of isspin_lock_irq/spin_unlock_irq. I recommend that you do not use this variant because it assumes the interruption status.

Finally, if the kernel thread shares data through the bottom half method, another variation of the spin lock can be used.Bottom halfThis method can delay the operation in the device driver until it is interrupted. This spin lock disables soft interruptions on the local CPU. This prevents softirq, tasklet, and bottom half from running on the local CPU. This variation is shown in listing 8.

Listing 8. Implement bottom-half interaction using the spin lock function

                spin_lock_bh( &my_spinlock );// critical sectionspin_unlock_bh( &my_spinlock );

Read/write lock

In many cases, data access is performed by a large number of reads and a small number of write operations (Reading data is more common than writing data ). Read/write locks are created to support this model. This model is interesting because it allows multiple threads to access the same data at the same time, but only one thread can write data at the same time. If the thread that executes the write operation holds this lock, the critical segment cannot be read by other threads. If a thread that executes the read operation holds this lock, multiple read threads can enter the critical segment. Listing 9 demonstrates this model.

Listing 9. read/write spin lock function

                rwlock_t my_rwlock;rwlock_init( &my_rwlock );write_lock( &my_rwlock );// critical section -- can read and writewrite_unlock( &my_rwlock );read_lock( &my_rwlock );// critical section -- can read onlyread_unlock( &my_rwlock );

The read/write spin locks are modified for bottom half and interrupt request (IRQ) based on the lock requirements. Obviously, if you are using the original read/write lock, use this spin lock according to the standard spin lock usage, without distinguishing between the read thread and the write thread.

Kernel mutex lock

In the kernel, mutex locks can be used to implement semaphore behavior. Kernel mutex is implemented on the atomic API, but this is invisible to kernel users. Mutex lock is simple, but some rules must be kept in mind. Only one task can hold a mutex lock at a time, and only this task can unlock the mutex lock. Mutex locks cannot be recursively locked or unlocked, and they may not be used in interactive context. However, mutex locks are faster and more compact than the current kernel semaphore options, so if they meet your needs, they will be your wise choice.

You can useDEFINE_MUTEXA macro uses an operation to create and initialize a mutex lock. This creates a new mutex lock and initializes its structure. You can view this implementation in./Linux/include/Linux/mutex. h.

                DEFINE_MUTEX( my_mutex );

The mutex lock API provides five functions: three of them are used for locking, one for unlocking, and the other for testing the mutex lock. First, let's take a look at the locking function. You can use the first function when you need to lock immediately and want to control the mutex lock when it is unavailable.mutex_trylock. This function is shown in listing 10.

Listing 10. Try to usemutex_trylockObtain mutex lock

                ret = mutex_trylock( &my_mutex );if (ret != 0) {  // Got the lock!} else {  // Did not get the lock}

If you want to wait for this lock, you can callmutex_lock. This call is returned when the mutex lock is available. Otherwise, it will sleep before the mutex lock is available. In either case, when the control is returned, the caller holds a mutex lock. Finally, when the caller sleepmutex_lock_interruptible. In this case, the function may return-EINTR. The two calls are shown in listing 11.

Listing 11. Locking a mutex lock that may be in sleep state

                mutex_lock( &my_mutex );// Lock is now held by the caller.if (mutex_lock_interruptible( &my_mutex ) != 0)  {  // Interrupted by a signal, no mutex held}

When a mutex lock is locked, it must be unlocked. This is causedmutex_unlockFunction. This function cannot be called from the interrupt context. Finally, you can callmutex_is_lockedCheck the status of the mutex. This call is actually compiled into an inline function. If the mutex lock is held (locked), 1 is returned; otherwise, 0 is returned. Listing 12 demonstrates these functions.

List 12. Usemutex_is_lockedTest mutex lock

                mutex_unlock( &my_mutex );if (mutex_is_locked( &my_mutex ) == 0) {  // Mutex is unlocked}

The mutex lock API has its own limitations because it is based on the atomic API. However, it is highly efficient and can be used if it meets your needs.

Big kernel lock)

Finally, let's take a look at the big kernel lock (BLK ). Its usage in the kernel is getting smaller and smaller, but there are still some reserved usage. BKL makes multi-processor Linux possible, but the fine-grained (finer-grained) Lock is slowly replacing BKL. BKL passedlock_kernelAndunlock_kernelFunctions are provided. For more information, see./Linux/lib/kernel_lock.c.

Conclusion

Linux has extraordinary performance, and the locking method is the same. The atomic lock not only provides a locking mechanism, but also provides arithmetic or bitwise operations. The spin lock provides a locking mechanism (mainly used in SMP), and The read/write spin lock allows multiple read threads and only one write thread obtains the given lock. Finally, mutex lock is a new locking mechanism that provides a simple API built on the atom. Whatever you need, Linux provides a locking solution to protect your data.

Source of the original article (Click here)

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.