Home  >  Article  >  System Tutorial  >  How to choose mutex locks, read-write locks, spin locks, and semaphores in the Linux kernel?

How to choose mutex locks, read-write locks, spin locks, and semaphores in the Linux kernel?

WBOY
WBOYforward
2024-02-11 18:30:12782browse

I. Introduction

There are many different types of locks in the Linux kernel, all of which can be used to protect critical resources to avoid race conditions between multiple threads or processes, thereby protecting the stability and reliability of the system. The types of these locks include: mutex lock (mutex), read-write lock (rwlock), spin lock (spinlock) and semaphore (semaphore). Today, I will introduce to you the various locks in the Linux kernel and how we choose which lock to use in real projects.

How to choose mutex locks, read-write locks, spin locks, and semaphores in the Linux kernel?

2. Introduction to several types of locks

Mutex lock (mutex) is a commonly used lock that can protect shared resources so that only one thread or process can access it at a certain time. A read-write lock (rwlock) allows multiple threads or processes to read a shared resource at the same time, but only allows one thread or process to write to it. Spin lock (spinlock) can be used to protect shared resources so that only one thread or process can access it at a certain time, but it will cause the thread or process to "spin" until the lock is obtained. Finally, semaphores (semaphore) can be used to control access to shared resources to ensure that other threads or processes can access them safely.

Read-write lock (rwlock) is a synchronization mechanism used to control multi-threaded access to shared resources. When a thread needs to read a shared resource, it can acquire a read lock so that other threads can read the resource at the same time without conflict. When a thread needs to write to a shared resource, it can acquire a write lock so that other threads cannot access the resource, thereby ensuring data integrity and consistency.

Spin lock (spinlock) is a simple and effective lock used to solve multi-thread synchronization problems. It is an exclusive lock that protects shared resources in a multi-threaded environment to prevent multiple threads from accessing the resource at the same time. The basic principle of a spin lock is that when a thread tries to acquire a lock, it will continue to try to acquire the lock until it succeeds. During this period, the thread will not enter the sleep state, but will always be in the busy waiting (busy-waiting) state. This is the origin of the name of the spin lock.

Semaphore (semaphore) is a commonly used synchronization mechanism, which can be used to control multiple threads' access to shared resources. It helps ensure that only one thread can access shared resources at a time, thereby avoiding resource conflicts and contention. A semaphore is an integer counter used to track the amount of available resources. When a thread needs to access a shared resource, it must first acquire the semaphore, which decrements the semaphore's counter by 1, and when it completes accessing the shared resource, it must release the semaphore so that Other threads can also access shared resources.

4. Mutex lock (Mutex)

Mutex lock is the most basic lock type and is widely used in the kernel. It is a binary lock that can only be held by one thread at the same time. When a thread requests the lock, if the lock is already occupied, the thread is blocked until the lock is released. The implementation of mutex locks uses atomic operations, so its performance is relatively high, but it is also prone to deadlock situations.

In the kernel, the mutex lock is defined as follows:

struct mutex {
    raw_spinlock_t      wait_lock;
    struct list_head    wait_list;
    struct task_struct  *owner;
    int                 recursion;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    struct lockdep_map  dep_map;
#endif
};

The use of mutex locks is very simple and usually only requires calling two functions:

void mutex_init(struct mutex *lock):函数用于初始化互斥锁
void mutex_lock(struct mutex *lock):函数用于获取互斥锁
void mutex_unlock(struct mutex *lock):函数用于释放互斥锁

5. Reader-Writer Lock

Read-write lock is a special type of lock that allows multiple threads to read shared resources at the same time, but only allows one thread to write to shared resources. The implementation of the read-write lock uses two counters to record the number of read threads and the number of write threads currently holding the lock.

In the kernel, the read-write lock is defined as follows:

struct rw_semaphore {
    long            count;
    struct list_head    wait_list;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    struct lockdep_map  dep_map;
#endif
};

The use of read-write locks is also relatively simple. Usually it only needs to call three functions to complete:

init_rwsem(struct rw_semaphore *sem):函数用于初始化读写锁
down_read(struct rw_semaphore *sem):函数用于获取读锁
up_read(struct rw_semaphore *sem):函数用于释放读锁
down_write(struct rw_semaphore *sem):函数用于获取写锁
up_write(struct rw_semaphore *sem):函数用于释放写锁

6. Spinlock

A spin lock is a lock that protects shared resources. It will occupy the CPU while waiting. Spin locks are suitable for situations where the critical section of the code is relatively small and the exclusive time of the shared resource is relatively short, so that the overhead of context switching can be avoided. Spin locks cannot be used in critical sections of code that require sleep, because the spin lock will always occupy the CPU during sleep.

In the Linux kernel, spin locks are represented by the spinlock_t type, which can be operated through the spin_lock() and spin_unlock() functions.

spin_lock_init(spinlock_t *lock):用于初始化自旋锁,将自旋锁的初始状态设置为未加锁状态。
spin_lock(spinlock_t *lock):用于获得自旋锁,如果自旋锁已经被占用,则当前进程会自旋等待,直到自旋锁可用。
spin_trylock(spinlock_t *lock):用于尝试获取自旋锁,如果自旋锁当前被占用,则返回0,否则返回1。
spin_unlock(spinlock_t *lock):用于释放自旋锁。

When using spin locks, you need to pay attention to the following points:

  • 自旋锁只适用于临界区代码比较短的情况,因为自旋等待的过程会占用CPU资源。
  • 自旋锁不可重入,也就是说,如果一个进程已经持有了自旋锁,那么它不能再次获取该自旋锁。
  • 在持有自旋锁的情况下,应该尽量避免调用可能会导致调度的内核函数,比如睡眠函数,因为这可能会导致死锁的发生。
  • 在使用自旋锁的时候,应该尽量避免嵌套使用不同类型的锁,比如自旋锁和读写锁,因为这可能会导致死锁的发生。
  • 当临界区代码较长或者需要睡眠时,应该使用信号量或者读写锁来代替自旋锁。

七、信号量(semaphore)

信号量是一种更高级的锁机制,它可以控制对共享资源的访问次数。信号量可分为二元信号量和计数信号量。二元信号量只有01两种状态,常用于互斥锁的实现;计数信号量则可以允许多个进程同时访问同一共享资源,只要它们申请信号量的数量不超过该资源所允许的最大数量。

在Linux内核中,信号量使用struct semaphore结构表示,可以通过down()up()函数对其进行操作。

void sema_init(struct semaphore *sem, int val):初始化一个信号量,val参数表示初始值。
void down(struct semaphore *sem):尝试获取信号量,如果信号量值为 0,调用进程将被阻塞。
int down_interruptible(struct semaphore *sem):尝试获取信号量,如果信号量值为 0,调用进程将被阻塞,并可以被中断。
int down_trylock(struct semaphore *sem):尝试获取信号量,如果信号量值为 0,则立即返回,否则返回错误。
void up(struct semaphore *sem):释放信号量,将信号量的值加 1,并唤醒可能正在等待信号量的进程。

八、该如何选择正确的锁

当需要对共享资源进行访问和修改时,我们通常需要采用同步机制来保证数据的一致性和正确性,其中锁是最基本的同步机制之一。不同类型的锁适用于不同的场景。

互斥锁适用于需要保护共享资源,只允许一个线程或进程访问共享资源的场景。例如,当一个线程正在修改一个数据结构时,其他线程必须等待该线程释放锁后才能修改该数据结构。

读写锁适用于共享资源的读写操作频繁且读操作远大于写操作的场景。读写锁允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。例如,在一个数据库管理系统中,读取操作比写入操作频繁,使用读写锁可以提高系统的并发性能。

自旋锁适用于保护共享资源的访问时间很短的场景,当线程需要等待的时间很短时,自旋锁比互斥锁的性能更好。例如,在访问共享资源时需要进行一些简单的操作,如对共享资源进行递增或递减等操作。

信号量适用于需要协调多个线程或进程对共享资源的访问的场景,允许多个线程或进程同时访问共享资源,但同时访问的线程或进程数量有限。例如,在一个并发下载系统中,可以使用信号量来限制同时下载的文件数量。

举个生活中的例子:当我们在买咖啡的时候,柜台前可能会有一个小桶,上面写着“请取走您需要的糖果,每人一颗”这样的字样。这个小桶就是一个信号量,它限制了每个人能够取走的糖果的数量,从而保证了公平性。

如果我们把这个小桶换成互斥锁,那么就可以只允许一个人在柜台前取走糖果。如果使用读写锁,那么在非高峰期的时候,多个人可以同时取走糖果,但在高峰期时只允许一个人取走。

And if we replace this keg with a spin lock, then when someone is taking away the candy, others will need to wait there until the candy is taken away. This can lead to a waste of time because someone else may have more urgent matters to attend to.

9. Summary

In the Linux kernel, there are four common locks: mutex locks, read-write locks, spin locks and semaphores. These locks are suitable for different scenarios, and developers need to choose appropriate locks based on the actual situation to ensure the correctness and performance of concurrent access.

The above is the detailed content of How to choose mutex locks, read-write locks, spin locks, and semaphores in the Linux kernel?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:lxlinux.net. If there is any infringement, please contact admin@php.cn delete