Advanced Tutorial: Linux kernel synchronization mechanism 1

Source: Internet
Author: User
Article title: master advanced: Linux kernel synchronization mechanism 1. Linux is a technology channel of the IT lab in China. Includes basic categories such as desktop applications, Linux system management, kernel research, embedded systems, and open source.
This article describes in detail the synchronization mechanism in Linux kernel: Atomic operations, semaphores, read/write semaphores and spin locks APIs, usage requirements and some typical examples.
  
   I. INTRODUCTION
  
In modern operating systems, multiple kernel execution streams may be executed at the same time, therefore, like multi-process and multi-thread programming, the kernel also needs some synchronous machine mechanisms to synchronize access to shared data from each execution unit. In a multi-processor system, synchronization mechanisms are required to synchronize the access of execution units on different processors to shared data.
  
In the mainstream Linux kernel, there are almost all synchronization mechanisms available in modern operating systems, including: Atomic operations, semaphores (semaphore), read/write semaphores (rw_semaphore), spinlock, BKL (Big Kernel Lock), rwlock, brlock (only included in the 2.4 Kernel), RCU (only included in the 2.6 Kernel) and seqlock (only included in the 2.6 kernel ).
  
   II. atomic operations
  
The so-called atomic operation means that the operation will never be interrupted by any other task or event before execution is completed. that is to say, its smallest execution unit cannot have a smaller execution unit than it, so here the atom actually uses the concept of material particles in physics.
  
Atomic operations require hardware support, so they are architecture-related. their APIs and atomic types are defined in the kernel source code tree include/asm/atomic. in the H file, they are all implemented in assembly language, because the C language cannot implement such operations.
  
Atomic operations are mainly used to count resources. many Reference Counting (refcnt) is implemented through atomic operations. The atomic type is defined as follows:
  
Typedef struct
{
Volatile int counter;
}
Atomic_t;
  
The volatile modifier field tells gcc not to optimize the data of this type, and its access is to the memory instead of the register.
  
Atomic operation APIs include:
  
Atomic_read (atomic_t * v );
  
This function performs atomic read operations on atomic variables. it returns the value of atomic variable v.
  
Atomic_set (atomic_t * v, int I );
  
This function sets the v value of the atomic type to I.
  
Void atomic_add (int I, atomic_t * v );
  
This function is used to add value to variable v of the atomic type.
  
Atomic_sub (int I, atomic_t * v );
  
This function deducts I from variable v of the atomic type.
  
Int atomic_sub_and_test (int I, atomic_t * v );
  
This function deducts I from the variable v of the atomic type, and determines whether the result is 0. if it is 0, true is returned. otherwise, false is returned.
  
Void atomic_inc (atomic_t * v );
  
This function increases the value of v to 1.
  
Void atomic_dec (atomic_t * v );
  
This function is used to subtract 1 from the v atom variable of the atomic type.
  
Int atomic_dec_and_test (atomic_t * v );
  
This function subtract 1 from the v atom variable of the atomic type and determines whether the result is 0. if it is 0, true is returned. otherwise, false is returned.
  
Int atomic_inc_and_test (atomic_t * v );
  
This function increases the value of v to 1 and determines whether the result is 0. if the value is 0, the system returns TRUE. Otherwise, the system returns false.
  
Int atomic_add_negative (int I, atomic_t * v );
  
This function increases I on the v atom of the atomic type variable and determines whether the result is negative. If yes, it returns true; otherwise, it returns false.
  
Int atomic_add_return (int I, atomic_t * v );
  
This function adds I to the v atom variable of the atomic type and returns a pointer to v.
  
Int atomic_sub_return (int I, atomic_t * v );
  
This function deducts I from variable v of the atomic type and returns a pointer to v.
  
Int atomic_inc_return (atomic_t * v );
  
This function increases the value of v to 1 and returns a pointer to v.
  
Int atomic_dec_return (atomic_t * v );
  
This function reduces the value of v atom by 1 and returns a pointer to v.
  
Atomic operations are usually used to implement reference counting of resources. in the IP fragment processing of the TCP/IP protocol stack, reference counting is used, and the fragmentation queue structure struct ipq describes an IP fragment, the refcnt field refers to the reference counter. its type is atomic_t. When an IP fragment is created (in the ip_frag_create function), use the atomic_set function to set it to 1. when the IP fragment is referenced, use the atomic_inc function to add 1 to the reference count.
  
When you do not need to reference the IP fragment, you can use the ipq_put function to release the IP fragment. ipq_put uses the atomic_dec_and_test function to reduce the reference count by 1 and determine whether the reference count is 0, if yes, the IP fragmentation will be released. The ipq_kill function deletes IP fragments from the ipq queue and reduces the reference count of the deleted IP fragments by 1 (implemented by using the atomic_dec function ).
  
   3. semaphore)
  
The Linux kernel semaphore is the same as the user-state System v ipc mechanism semaphore in terms of concept and principle, but it cannot be used outside the kernel, therefore, it has nothing to do with the IPC mechanism semaphores of System V.
  
When creating a semaphore, you must set an initial value to indicate that several tasks can access the shared resources protected by the semaphore at the same time. when the initial value is 1, it becomes Mutex ), that is, only one task can access shared resources protected by semaphores.
  
To access shared resources, a task must first obtain a semaphore. The Semaphore acquisition operation will reduce the semaphore value by 1. if the current semaphore value is negative, it indicates that the Semaphore cannot be obtained, the task must be suspended in the wait queue of the semaphore to wait for the semaphore to be available. if the current semaphore value is not negative, it means that the semaphore can be obtained, so that the shared resources protected by the semaphore can be accessed immediately.
  
After a task accesses a shared resource protected by semaphores, it must release the semaphores. by adding the semaphores value to 1, if the semaphores value is not a positive number, it indicates that a task is waiting for the current semaphore, so it also wakes up all tasks waiting for the semaphore.
  
Semaphore APIs include:
  
DECLARE_MUTEX (name)
  
This macro declares a semaphore name and initializes its value to 0, that is, it declares a mutex lock.
  
DECLARE_MUTEX_LOCKED (name)
  
This macro declares a mutex lock name, but sets its initial value to 0, that is, the lock is in the locked state when it is created. Therefore, the lock is usually obtained after being released.
  
Void sema_init (struct semaphore * sem, int val );
  
This function is used to initialize and set the initial value of the semaphore. it sets the sem value of the semaphore to val.
  
Void init_MUTEX (struct semaphore * sem );
  
This function is used to initialize a mutex lock, that is, it sets the semaphores sem value to 1.
  
Void init_MUTEX_LOCKED (struct semaphore * sem );
  
This function is also used to initialize a mutex lock, but it sets the semaphores sem value to 0, that is, it is in the locked state at the beginning.
  
Void down (struct semaphore * sem );
  
This function is used to obtain semaphores sem, which causes sleep and therefore cannot be used in the interrupt context (including IRQ context and softirq context. This function will reduce the sem value by 1. if The semaphores sem value is not negative, it will return directly. Otherwise, the caller will be suspended until other tasks release The semaphores to continue running.
  
Int down_interruptible (struct semaphore * sem );
  
This function is similar to the down function. The difference is that the down function will not be interrupted by the signal (signal), but down_interruptible can be interrupted by the signal, therefore, this function has a return value to identify whether the signal is normal or interrupted. if 0 is returned, it indicates that the semaphore is returned normally. if the signal is interrupted, The-EINTR is returned.
  
Int down_trylock (struct semaphore * sem );
  
This function tries to obtain the semaphores sem. if it can be obtained immediately, it obtains the semaphores and returns 0. Otherwise, it indicates that the semaphores sem cannot be obtained, and the return value is not 0. Therefore, it does not cause the caller to sleep and can be used in the interrupted context.
  
Void up (struct semaphore * sem );
  
This function releases the semaphores sem, that is, adding the sem value to 1. if the sem value is not a positive number, it indicates that a task is waiting for the semaphores, so these waiting persons are awakened.
  
Semaphores are used as mutex locks in most cases. the following uses the console driver system as an example to describe how to use semaphores.
  
In kernel/printk. c of the kernel source code tree, the macro DECLARE_MUTEX is used to declare a mutex console_sem, which is used to protect the console driver list lele_drivers and synchronize access to the entire console driver system.
  
The function acquire_console_sem is defined to obtain the le_sem, the release_console_sem is defined to release the le_sem, and the function try_acquire_console_sem is defined to obtain the le_sem. These three functions are actually simple packaging of the down, up, and down_trylock functions.
  
To access the console_drivers driver list, use acquire_console_sem to protect the console_drivers list. after accessing this list, call release_console_sem to release the semaphores lele_sem.
  
The console_unblank, console_device, console_stop, console_start, register_console, and unregister_console functions all need to access lele_drivers. Therefore, they both use functions to protect lele_drivers.
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.