算了,既然給你那麼多秘籍了,也不在乎這剩下的兩三招:
出招表五:順序鎖(seqlock)
使用順序鎖,讀執行單元絕不會被寫執行單元阻塞,同時寫執行單元也不需要等待所有讀執行單元完成讀操作後才進行寫操作。但是寫執行單元之間仍然是互斥的。如果讀執行單元在讀操作期間,寫執行單元已經發生了操作,那麼,讀執行單元必須重新讀取資料,以便確保得到的資料是完整的。
致命弱點:順序鎖有一個限制,就是它必須要求被保護的共用資源不含有指標。因為寫執行單元可能使得指標失效,但讀執行單元如果正要訪問該指標,將導致Oops。
在Linux核心中。讀執行單元設計如下順序讀操作。
1)讀開始
unsigned read_seqbegin(const seqlock_t *s1);
read_seqbegin_irqsave(lock, flag);【read_seqbegin_irqsave(lock, flag)=local_irq_save() + read_seqbegin();】
2)重讀
int read_seqretry(const seqlock_t *s1, unsigned iv);
read_seqretry_irqrestore(lock, iv, flag);【read_seqretry_irqrestore(lock, iv, flag)= read_seqretry()+ local_irq_restore();】
讀執行單元使用順序鎖的模式如下:
do{
seqnum = read_seqbegin(&seqlock_a);
//讀作業碼塊
。。。
}while(read_seqretry(&seqlock_a, seqnum));
在Linux核心中。寫執行單元設計如下順序讀操作。
1)獲得順序鎖
void write_seqlock(seqlock_t *s1);
int write_ tryseqlock(seqlock_t *s1);
write_seqlock_irqsave(lock, flags);【=local_irq_save() + write_seqlock()】
write_seqlock_irq(lock);【=local_irq_disable() + write_seqlock()】
write_seqlock_bh(lock);【=local_bh_disable() + write_seqlock()】
2)釋放順序鎖
void write_sequnlock(seqlock_t *s1);
write_sequnlock_irqrestore(lock, flag);【=write_sequnlock() + local_irq_restore()】
write_sequnlock_irq(lock);【=write_sequnlock() + local_irq_enable()】
write_sequnlock_bh(lock);【write_sequnlock()+local_bh_enable()】
寫執行單元使用順序鎖的模式如下:
write_seqlock(&seqlock_a);
…//寫作業碼
write_sequnlock(&seqlock_a);
出招表六:RCU(Read-Copy-Update)
對於被RCU保護的共用資料結構,讀者不需要獲得任何鎖就可以訪問它,但寫者在訪問它時首先備份一個副本,然後對副本進行修改,然後對副本進行修改,最後使用一個回調(callback)機制在適當的時機把原來資料的指標重新指向新的被修改的資料。這個時機就是所有引用該資料的CPU都退出對共用資料的操作時。
1)讀鎖定。 2)讀解鎖。 使用RCU進行讀的模式如下:
rcu_read_lock(); rcu_read_unlock(); rcu_read_lock()
rcu_read_lock_bh(); rcu_read_unlock_bh(); …//讀臨界區
rcu_read_unlock()
3)與RCU相關的寫者函數包括:
struct rcu_head{
struct rcu_head *next;//下一個RCU
void (*func)(struct rcu_head *head);//獲得競爭條件後的處理函數
};
synchronize_rcu(void);//阻塞讀者,直到所有的讀者已經完成讀端臨界區,寫者才可以繼續下一步操作
synchronize_sched();//等待所有CPU都處在可搶佔狀態,保證所有中斷(不包括非強制中斷)處理完畢
void call_rcu(struct rcu_head *head, void (*func)(void *arg),void arg);//不阻塞寫者,可以在中斷上下文或非強制中斷使用,
使用synchronize_rcu的寫操作流程如下:
DEFINE_SPINLOCK(foo_spinlock);
Int a_new;
spin_lock(&foo_spinlock);
//a_new = a;
//write a_new;
synchronize_rcu();
//a=a_new;
spin_unlock(&foo_spinlock);
//使用call_rcu的寫操作流程如下:
struct protectRcu
{
int protect;
struct rcu_head rcu;
};
struct protectRcu *global_pr;
//一般用來釋放老的資料
void callback_function(struct rcu_head *r)
{
struct protectRcu *t;
t=container_of(r, struct protectRcu, rcu);
kfree(t);
}
void write_process()
{
struct protectRcu *t, *old;
t = kmalloc (sizeof(*t),GFP_KERNEL);//棄置站台
spin_lock(&foo_spinlock);
t->protect = xx;
old= global_pr;
global_pr=t;//用副本替換
spin_unlock(&foo_spinlock);
call_rcu(old->rcu, callback_function);
}