首頁  >  文章  >  系統教程  >  Linux核心中的互斥鎖、讀寫鎖、自旋鎖、信號量該如何選擇?

Linux核心中的互斥鎖、讀寫鎖、自旋鎖、信號量該如何選擇?

WBOY
WBOY轉載
2024-02-11 18:30:12782瀏覽

一、前言

#Linux 核心中有許多不同類型的鎖,它們都可以用來保護關鍵資源,以避免多個執行緒或行程之間發生競爭條件,從而保護系統的穩定性和可靠性。這些鎖的類型包括:互斥鎖(mutex)、讀寫鎖(rwlock)、自旋鎖定(spinlock)和訊號量(semaphore)。今天,我將向大家介紹 Linux 核心中的各種鎖,以及我們在實際專案中如何選擇使用哪種鎖。

Linux核心中的互斥鎖、讀寫鎖、自旋鎖、信號量該如何選擇?

#二、幾種鎖的介紹

互斥鎖(mutex 是一種常用的鎖,它可以保護共享資源,使得在某個時刻只有一個執行緒或程序能夠存取它。讀寫鎖(rwlock)則可以同時允許多個執行緒或行程讀取共用資源,但只允許一個執行緒或行程寫入它。自旋鎖(spinlock)可以用來保護共享資源,使得在某個時刻只有一個執行緒或進程能夠存取它,但它會使執行緒或進程“自旋”,直到獲得鎖為止。最後,信號量(semaphore)可以用來控制對共享資源的訪問,以確保其他執行緒或進程能夠安全地存取它們。

讀寫鎖定(rwlock 是一種用於控制多執行緒存取共享資源的同步機制。當一個執行緒需要讀取共享資源時,它可以取得讀取鎖,這樣其他執行緒就能夠同時讀取該資源,而不會發生衝突。當一個執行緒需要寫入共享資源時,它可以取得寫入鎖,這樣其他執行緒就不能存取該資源,從而保證資料的完整性和一致性。

自旋鎖定(spinlock 是一種簡單而有效的用於解決多執行緒同步問題的鎖定。它是一種排他鎖,可以在多執行緒環境下保護共享資源,以防止多個執行緒同時存取該資源。自旋鎖的基本原理是,當一個執行緒試圖取得鎖時,它會不斷嘗試取得鎖,直到成功為止。在這段期間,執行緒不會進入休眠狀態,而是一直處於忙碌等待(busy-waiting)狀態,這也就是自旋鎖定名稱的由來。

信號量(semaphore 是一種常用的同步機制,它可以用來控制多個執行緒對共享資源的存取。它有助於確保同一時間只有一個執行緒能夠存取共享資源,從而避免資源衝突和競爭。信號量是一種整數計數器,用於追蹤可用資源的數量。當一個執行緒需要存取共享資源時,它首先必須獲取信號量,這會將信號量的計數器減少1 ,而當它完成對共享資源的存取後,它必須釋放信號量,以便其他執行緒也能夠存取共享資源。

四、互斥鎖(Mutex)

互斥鎖是最基本的鎖類型,在核心中使用較為廣泛。它是一種二元鎖,只能同時有一個執行緒持有該鎖。當一個執行緒請求該鎖時,如果鎖已被佔用,則執行緒會被阻塞直到鎖被釋放。互斥鎖的實作使用了原子操作,因此它的效能比較高,但也容易出現死鎖情況。

在核心中,互斥鎖的定義如下:

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
};

互斥鎖的使用非常簡單,通常只需要呼叫兩個函數即可完成:

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

五、讀寫鎖(Reader-Writer Lock)

讀寫鎖是一種特殊的鎖定類型,它允許多個執行緒同時讀取共享資源,但只允許一個執行緒寫入共享資源。讀寫鎖的實作使用了兩個計數器,分別記錄目前持有鎖的讀執行緒數和寫執行緒數。

在核心中,讀寫鎖定的定義如下:

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

讀寫鎖定的使用也比較簡單,通常只要呼叫三個函數即可完成:

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):函数用于释放写锁

六、自旋鎖(spinlock)

自旋鎖是一種保護共享資源的鎖,它會在等待期間一直佔用CPU。自旋鎖適用於程式碼臨界區比較小的情況,且共享資源的獨佔時間比較短,這樣就可以避免上下文切換的開銷。自旋鎖不能用於需要睡眠的程式碼臨界區,因為在睡眠期間自旋鎖會一直佔用CPU。

在Linux核心中,自旋鎖定使用spinlock_t類型表示,可以透過spin_lock()spin_unlock()函數對其進行操作。

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

使用自旋鎖定時,需要注意以下幾點:

  • 自旋锁只适用于临界区代码比较短的情况,因为自旋等待的过程会占用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,并唤醒可能正在等待信号量的进程。

八、该如何选择正确的锁

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

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

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

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

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

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

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

而如果我們把這個小桶換成自旋鎖,那麼當有人在取走糖果時,其他人就需要一直在那裡等待,直到糖果被取走為止。這樣可能會造成浪費時間的情況,因為其他人可能有更緊急的事情需要處理。

九、總結

#在Linux核心中,有四種常見的鎖:互斥鎖、讀寫鎖、自旋鎖和信號量。這些鎖適用於不同的場景,開發者需要根據實際情況選擇適當的鎖來確保並發存取的正確性和效能。

以上是Linux核心中的互斥鎖、讀寫鎖、自旋鎖、信號量該如何選擇?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:lxlinux.net。如有侵權,請聯絡admin@php.cn刪除