昨天講完了 mutex, semaphore ,今天仍要繼續朝著各種不同的 lock前進,昨天講的semaphore,有個明顯的缺點,沒辦法知道在 C.S裡的行程究竟是讀還是寫。 rwlock 可以有效的提高並行性,rwlock可以允許多個執行緒存取c.s 但是寫入就只能有一個行程。
rwlock 具有以下的特性
rwlock 有兩種,分別是 spinlock型與 semaphore型。
// <include/linux/rwlock_types.h >
typedef struct{
arch_rwlock_t raw_lock;
}rwlock_t
// <arch/arm/include/asm/spinlock_types.h>
typedef sturct{
u32 lock;
}arch_rwlock_t;
以下是常見的函數:
rwlock_init()
: 初始化rwlock。write_lock()
: 申請寫入鎖。write_unlock()
: 釋放寫入鎖。read_lock()
: 申請讀取鎖。read_unlock()
:釋放讀取鎖。read_lock_irq()
: 關閉中斷並且申請讀取鎖。write_lock_irq()
:關閉中斷並且申請寫入鎖。write_unlock_irq()
:打開中斷並且釋放寫者鎖。//<include/linux/rwsem.h>
struct rw_semaphore {
atomic_long_t count;
struct list_head wait_list;
raw_spinlock_t wait_lock;
#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
struct optimistic_spin_queue osq; /* spinner MCS lock */
/*
* Write owner. Used as a speculative check to see
* if the owner is running on the cpu.
*/
struct task_struct *owner;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};
wait_lock
: 是一個 spinlock變量,用以保護 rw_semaphore
資料結構中的 count
成員。count
:用來保護 read-write semaphore 的計數。wait_list
:管理所有在 semaphore上的睡眠行程,沒有獲取鎖的行程會睡眠在這個鏈表。osq
: 表示MCS鎖。owner
: 寫者獲取鎖的時候, owner
指向鎖持有者的 task_struct
資料結構。
其中將 count
拿出來看
/*
* the semaphore definition
*/
#ifdef CONFIG_64BIT
# define RWSEM_ACTIVE_MASK 0xffffffffL
#else
# define RWSEM_ACTIVE_MASK 0x0000ffffL
#endif
#define RWSEM_UNLOCKED_VALUE 0x00000000L
#define RWSEM_ACTIVE_BIAS 0x00000001L
#define RWSEM_WAITING_BIAS (-RWSEM_ACTIVE_MASK-1)
#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS
#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
count
應該要看成兩段,分別為低位的 [15:0]位元,代表了正在持有鎖的讀取者或是寫入者的個數; [31:16]為元,通常是一個負數,代表有一個正在持有鎖或是處於等待狀態的寫入者,以及睡眠等待隊列有人在等待。
例子如下
RWSEM_ACTIVE_READ_BIAS
= 0x00000001 ,即二元[0,1]表示只有一個讀取者。RWSEM_ACTIVE_WRITE_BIAS
= 0xffff0001, 二元數[-1,1],表示當前只有一個活躍的寫入者。RWSEM_WAITING_BIAS
= 0xffff0000 即[-1, 0]代表睡眠等待隊列中有人在睡眠等待 。rw-lock 在kernel中應用廣泛,特別是在內存管理經常會使用到除了前面的 mm->mmap_sem
的rw semaphore以外, RMAP系統中的 anon_vma->rwsem
、 address space 資料結構中的 i_mmap_rwsem
等。
最後再整理幾個rw lock的重要特性:
down_read()
: 如果行程有了read lock,允許行程繼續申請更多的 read lock,但是申請 write lock則需要睡眠等待down_write()
:如果一個行程有了 write lock ,另一個行程想申請 write lock 時,需要用 spinlock等待,申請 read lock 則需要睡眠等待。up_write()
/ up_read()
:若等待列上第一個成員是寫入者,則只喚醒他; 若為讀取行程,則喚醒等待列最前面幾個連續的讀取者。