大內核鎖、讀寫鎖、大讀者鎖、RCU和順序鎖

協議棧分析過程中遇到的知識點記錄


對比Linux 1.2.13 和 2.6.3 協議棧,最大的區別在與對多核的支持,所以鎖的支持情況很重要

http://www.ibm.com/developerworks/cn/linux/l-synch/part2/


這是本系列文章的第二部分,它詳細地介紹了Linux內核中的同步機制:大內核鎖、讀寫鎖、大讀者鎖、RCU和順序鎖的API,使用要求以及一些典型示例。本系列文章的第一部分則詳細地介紹了 Linux 內核中的其它一些同步機制,包括原子操作、信號量、讀寫信號量和自旋鎖的API,使用要求以及一些典型示例。

六、大內核鎖(BKL--Big Kernel Lock)

大內核鎖本質上也是自旋鎖,但是它又不同於自旋鎖,自旋鎖是不可以遞歸獲得鎖的,因爲那樣會導致死鎖。但大內核鎖可以遞歸獲得鎖。大內核鎖用於保護整個內核,而自旋鎖用於保護非常特定的某一共享資源。進程保持大內核鎖時可以發生調度,具體實現是:在執行schedule時,schedule將檢查進程是否擁有大內核鎖,如果有,它將被釋放,以致於其它的進程能夠獲得該鎖,而當輪到該進程運行時,再讓它重新獲得大內核鎖。注意在保持自旋鎖期間是不運行發生調度的。

需要特別指出,整個內核只有一個大內核鎖,其實不難理解,內核只有一個,而大內核鎖是保護整個內核的,當然有且只有一個就足夠了。

還需要特別指出的是,大內核鎖是歷史遺留,內核中用的非常少,一般保持該鎖的時間較長,因此不提倡使用它。從2.6.11內核起,大內核鎖可以通過配置內核使其變得可搶佔(自旋鎖是不可搶佔的),這時它實質上是一個互斥鎖,使用信號量實現。

大內核鎖的API包括:


void lock_kernel(void);

該函數用於得到大內核鎖。它可以遞歸調用而不會導致死鎖。


void unlock_kernel(void);

該函數用於釋放大內核鎖。當然必須與lock_kernel配對使用,調用了多少次lock_kernel,就需要調用多少次unlock_kernel。

大內核鎖的API使用非常簡單,按照以下方式使用就可以了:


lock_kernel();
//對被保護的共享資源的訪問

unlock_kernel();

七、讀寫鎖(rwlock)

讀寫鎖實際是一種特殊的自旋鎖,它把對共享資源的訪問者劃分成讀者和寫者,讀者只對共享資源進行讀訪問,寫者則需要對共享資源進行寫操作。這種鎖相對於自旋鎖而言,能提高併發性,因爲在多處理器系統中,它允許同時有多個讀者來訪問共享資源,最大可能的讀者數爲實際的邏輯CPU數。寫者是排他性的,一個讀寫鎖同時只能有一個寫者或多個讀者(與CPU數相關),但不能同時既有讀者又有寫者。

在讀寫鎖保持期間也是搶佔失效的。

如果讀寫鎖當前沒有讀者,也沒有寫者,那麼寫者可以立刻獲得讀寫鎖,否則它必須自旋在那裏,直到沒有任何寫者或讀者。如果讀寫鎖沒有寫者,那麼讀者可以立即獲得該讀寫鎖,否則讀者必須自旋在那裏,直到寫者釋放該讀寫鎖。

讀寫鎖的API看上去與自旋鎖很象,只是讀者和寫者需要不同的獲得和釋放鎖的API。下面是讀寫鎖API清單:


rwlock_init(x)

該宏用於動態初始化讀寫鎖x。


DEFINE_RWLOCK(x)

該宏聲明一個讀寫鎖並對其進行初始化。它用於靜態初始化。


RW_LOCK_UNLOCKED

它用於靜態初始化一個讀寫鎖。

DEFINE_RWLOCK(x)等同於rwlock_t x = RW_LOCK_UNLOCKED


read_trylock(lock)

讀者用它來盡力獲得讀寫鎖lock,如果能夠立即獲得讀寫鎖,它就獲得鎖並返回真,否則不能獲得鎖,返回假。無論是否能夠獲得鎖,它都將立即返回,絕不自旋在那裏。


write_trylock(lock)

寫者用它來盡力獲得讀寫鎖lock,如果能夠立即獲得讀寫鎖,它就獲得鎖並返回真,否則不能獲得鎖,返回假。無論是否能夠獲得鎖,它都將立即返回,絕不自旋在那裏。


read_lock(lock)

讀者要訪問被讀寫鎖lock保護的共享資源,需要使用該宏來得到讀寫鎖lock。如果能夠立即獲得,它將立即獲得讀寫鎖並返回,否則,將自旋在那裏,直到獲得該讀寫鎖。


write_lock(lock)

寫者要想訪問被讀寫鎖lock保護的共享資源,需要使用該宏來得到讀寫鎖lock。如果能夠立即獲得,它將立即獲得讀寫鎖並返回,否則,將自旋在那裏,直到獲得該讀寫鎖。


read_lock_irqsave(lock, flags)

讀者也可以使用該宏來獲得讀寫鎖,與read_lock不同的是,該宏還同時把標誌寄存器的值保存到了變量flags中,並失效了本地中斷。


write_lock_irqsave(lock, flags)

寫者可以用它來獲得讀寫鎖,與write_lock不同的是,該宏還同時把標誌寄存器的值保存到了變量flags中,並失效了本地中斷。


read_lock_irq(lock)

讀者也可以用它來獲得讀寫鎖,與read_lock不同的是,該宏還同時失效了本地中斷。該宏與read_lock_irqsave的不同之處是,它沒有保存標誌寄存器。


write_lock_irq(lock)

寫者也可以用它來獲得鎖,與write_lock不同的是,該宏還同時失效了本地中斷。該宏與write_lock_irqsave的不同之處是,它沒有保存標誌寄存器。


read_lock_bh(lock)

讀者也可以用它來獲得讀寫鎖,與與read_lock不同的是,該宏還同時失效了本地的軟中斷。


write_lock_bh(lock)

寫者也可以用它來獲得讀寫鎖,與write_lock不同的是,該宏還同時失效了本地的軟中斷。


read_unlock(lock)

讀者使用該宏來釋放讀寫鎖lock。它必須與read_lock配對使用。


write_unlock(lock)

寫者使用該宏來釋放讀寫鎖lock。它必須與write_lock配對使用。


read_unlock_irqrestore(lock, flags)

讀者也可以使用該宏來釋放讀寫鎖,與read_unlock不同的是,該宏還同時把標誌寄存器的值恢復爲變量flags的值。它必須與read_lock_irqsave配對使用。


write_unlock_irqrestore(lock, flags)

寫者也可以使用該宏來釋放讀寫鎖,與write_unlock不同的是,該宏還同時把標誌寄存器的值恢復爲變量flags的值,並使能本地中斷。它必須與write_lock_irqsave配對使用。


read_unlock_irq(lock)

讀者也可以使用該宏來釋放讀寫鎖,與read_unlock不同的是,該宏還同時使能本地中斷。它必須與read_lock_irq配對使用。


write_unlock_irq(lock)

寫者也可以使用該宏來釋放讀寫鎖,與write_unlock不同的是,該宏還同時使能本地中斷。它必須與write_lock_irq配對使用。


read_unlock_bh(lock)

讀者也可以使用該宏來釋放讀寫鎖,與read_unlock不同的是,該宏還同時使能本地軟中斷。它必須與read_lock_bh配對使用。


write_unlock_bh(lock)

寫者也可以使用該宏來釋放讀寫鎖,與write_unlock不同的是,該宏還同時使能本地軟中斷。它必須與write_lock_bh配對使用。

讀寫鎖的獲得和釋放鎖的方法也有許多版本,具體用哪個與自旋鎖一樣,因此參考自旋鎖部分就可以了。只是需要區分讀者與寫者,讀者要用讀者版本,而寫者必須用寫者版本。


八、大讀者鎖(brlock-Big Reader Lock)

大讀者鎖是讀寫鎖的高性能版,讀者可以非常快地獲得鎖,但寫者獲得鎖的開銷比較大。大讀者鎖只存在於2.4內核中,在2.6中已經沒有這種鎖(提醒讀者特別注意)。它們的使用與讀寫鎖的使用類似,只是所有的大讀者鎖都是事先已經定義好的。這種鎖適合於讀多寫少的情況,它在這種情況下遠好於讀寫鎖。

大讀者鎖的實現機制是:每一個大讀者鎖在所有CPU上都有一個本地讀者寫者鎖,一個讀者僅需要獲得本地CPU的讀者鎖,而寫者必須獲得所有CPU上的鎖。

大讀者鎖的API非常類似於讀寫鎖,只是鎖變量爲預定義的鎖ID。


void br_read_lock (enum brlock_indices idx);

讀者使用該函數來獲得大讀者鎖idx,在2.4內核中,預定義的idx允許的值有兩個:BR_GLOBALIRQ_LOCK和BR_NETPROTO_LOCK,當然,用戶可以添加自己定義的大讀者鎖ID 。


void br_read_unlock (enum brlock_indices idx);

讀者使用該函數釋放大讀者鎖idx。


void br_write_lock (enum brlock_indices idx);

寫者使用它來獲得大讀者鎖idx。


void br_write_unlock (enum brlock_indices idx);

寫者使用它來釋放大讀者鎖idx。


br_read_lock_irqsave(idx, flags)

讀者也可以使用該宏來獲得大讀者鎖idx,與br_read_lock不同的是,該宏還同時把寄存器的值保存到變量flags中,並且失效本地中斷。


br_read_lock_irq(idx)

讀者也可以使用該宏來獲得大讀者鎖idx,與br_read_lock不同的是,該宏還同時失效本地中斷。與br_read_lock_irqsave不同的是,該宏不保存標誌寄存器。


br_read_lock_bh(idx)

讀者也可以使用該宏來獲得大讀者鎖idx,與br_read_lock不同的是,該宏還同時失效本地軟中斷。


br_write_lock_irqsave(idx, flags)

寫者也可以使用該宏來獲得大讀者鎖idx,與br_write_lock不同的是,該宏還同時把寄存器的值保存到變量flags中,並且失效本地中斷。


br_write_lock_irq(idx)

寫者也可以使用該宏來獲得大讀者鎖idx,與br_write_lock不同的是,該宏還同時失效本地中斷。與br_write_lock_irqsave不同的是,該宏不保存標誌寄存器。


br_write_lock_bh(idx)

寫者也可以使用該宏來獲得大讀者鎖idx,與br_write_lock不同的是,該宏還同時失效本地軟中斷。


br_read_unlock_irqrestore(idx, flags)

讀者也使用該宏來釋放大讀者鎖idx,它與br_read_unlock不同之處是,該宏還同時把變量flags的值恢復到標誌寄存器。


br_read_unlock_irq(idx)

讀者也使用該宏來釋放大讀者鎖idx,它與br_read_unlock不同之處是,該宏還同時使能本地中斷。


br_read_unlock_bh(idx)

讀者也使用該宏來釋放大讀者鎖idx,它與br_read_unlock不同之處是,該宏還同時使能本地軟中斷。


br_write_unlock_irqrestore(idx, flags)

寫者也使用該宏來釋放大讀者鎖idx,它與br_write_unlock不同之處是,該宏還同時把變量flags的值恢復到標誌寄存器。


br_write_unlock_irq(idx)

寫者也使用該宏來釋放大讀者鎖idx,它與br_write_unlock不同之處是,該宏還同時使能本地中斷。


br_write_unlock_bh(idx)

寫者也使用該宏來釋放大讀者鎖idx,它與br_write_unlock不同之處是,該宏還同時使能本地軟中斷。

這些API的使用與讀寫鎖完全一致。


九、RCU(Read-Copy Update)

RCU(Read-Copy Update),顧名思義就是讀-拷貝修改,它是基於其原理命名的。對於被RCU保護的共享數據結構,讀者不需要獲得任何鎖就可以訪問它但寫者在訪問它時首先拷貝一個副本,然後對副本進行修改,最後使用一個回調(callback)機制在適當的時機把指向原來數據的指針重新指向新的被修改的數據。這個時機就是所有引用該數據的CPU都退出對共享數據的操作。

RCU也是讀寫鎖的高性能版本,但是它比大讀者鎖具有更好的擴展性和性能。 RCU既允許多個讀者同時訪問被保護的數據,又允許多個讀者和多個寫者同時訪問被保護的數據(注意:是否可以有多個寫者並行訪問取決於寫者之間使用的同步機制),讀者沒有任何同步開銷,而寫者的同步開銷則取決於使用的寫者間同步機制。但RCU不能替代讀寫鎖,因爲如果寫比較多時,對讀者的性能提高不能彌補寫者導致的損失。

RCU的API如下;


rcu_read_lock()

讀者在讀取由RCU保護的共享數據時使用該函數標記它進入讀端臨界區。


rcu_read_unlock()

該函數與rcu_read_lock配對使用,用以標記讀者退出讀端臨界區。


synchronize_rcu()

該函數由RCU寫端調用,它將阻塞寫者,直到經過grace period後,即所有的讀者已經完成讀端臨界區,寫者纔可以繼續下一步操作。如果有多個RCU寫端調用該函數,他們將在一個grace period之後全部被喚醒。


synchronize_kernel()

其他非RCU的內核代碼使用該函數來等待所有CPU處在可搶佔狀態,目前功能等同於synchronize_rcu,但現在已經不建議使用,而使用synchronize_sched。


synchronize_sched()

該函數用於等待所有CPU都處在可搶佔狀態,它能保證正在運行的中斷處理函數處理完畢,但不能保證正在運行的softirq處理完畢。注意,synchronize_rcu只保證所有CPU都處理完正在運行的讀端臨界區。


void fastcall call_rcu(struct rcu_head *head,
void (*func)(struct rcu_head *rcu))
struct rcu_head {
struct rcu_head *next;
void (*func)(struct rcu_head *head);
};

函數call_rcu也由RCU寫端調用,它不會使寫者阻塞,因而可以在中斷上下文或softirq使用。該函數將把函數func掛接到RCU回調函數鏈上,然後立即返回。一旦所有的CPU都已經完成端臨界區操作,該函數將被調用來釋放刪除的將絕不在被應用的數據。參數head用於記錄回調函數 func,一般該結構會作爲被RCU保護的數據結構的一個字段,以便省去單獨爲該結構分配內存的操作。需要指出的是,函數synchronize_rcu 的實現實際上使用函數call_rcu。


void fastcall call_rcu_bh(struct rcu_head *head,
void (*func)(struct rcu_head *rcu))

函數call_ruc_bh功能幾乎與call_rcu完全相同,唯一差別就是它把softirq的完成也當作經歷一個quiescent state,因此如果寫端使用了該函數,在進程上下文的讀端必須使用rcu_read_lock_bh。


#define rcu_dereference(p) ({ \
typeof(p) _________p1 = p; \
smp_read_barrier_depends(); \
(_________p1); \
})

該宏用於在RCU讀端臨界區獲得一個RCU保護的指針,該指針可以在以後安全地引用,內存柵只在alpha架構上才使用。

除了這些API,RCU還增加了鏈表操作的RCU版本,因爲對於RCU,對共享數據的操作必須保證能夠被沒有使用同步機制的讀者看到,所以內存柵是非常必要的。


static inline void list_add_rcu(struct list_head *new, struct list_head *head)

該函數把鏈表項new插入到RCU保護的鏈表head的開頭。使用內存柵保證了在引用這個新插入的鏈表項之前,新鏈表項的鏈接指針的修改對所有讀者是可見的。


static inline void list_add_tail_rcu(struct list_head *new,
struct list_head *head)

該函數類似於list_add_rcu,它將把新的鏈表項new添加到被RCU保護的鏈表的末尾。


static inline void list_del_rcu(struct list_head *entry)

該函數從RCU保護的鏈表中移走指定的鏈表項entry,並且把entry的prev指針設置爲LIST_POISON2,但是並沒有把entry的next指針設置爲LIST_POISON1,因爲該指針可能仍然在被讀者用於便利該鏈表。


static inline void list_replace_rcu(struct list_head *old, struct list_head *new)

該函數是RCU新添加的函數,並不存在非RCU版本。它使用新的鏈表項new取代舊的鏈表項old,內存柵保證在引用新的鏈表項之前,它的鏈接指針的修正對所有讀者可見。


list_for_each_rcu(pos, head)

該宏用於遍歷由RCU保護的鏈表head,只要在讀端臨界區使用該函數,它就可以安全地和其它_rcu鏈表操作函數(如list_add_rcu)併發運行。


list_for_each_safe_rcu(pos, n, head)

該宏類似於list_for_each_rcu,但不同之處在於它允許安全地刪除當前鏈表項pos。


list_for_each_entry_rcu(pos, head, member)

該宏類似於list_for_each_rcu,不同之處在於它用於遍歷指定類型的數據結構鏈表,當前鏈表項pos爲一包含struct list_head結構的特定的數據結構。


list_for_each_continue_rcu(pos, head)

該宏用於在退出點之後繼續遍歷由RCU保護的鏈表head。


static inline void hlist_del_rcu(struct hlist_node *n)

它從由RCU保護的哈希鏈表中移走鏈表項n,並設置n的ppre指針爲LIST_POISON2,但並沒有設置next爲LIST_POISON1,因爲該指針可能被讀者使用用於遍利鏈表。


static inline void hlist_add_head_rcu(struct hlist_node *n,
struct hlist_head *h)

該函數用於把鏈表項n插入到被RCU保護的哈希鏈表的開頭,但同時允許讀者對該哈希鏈表的遍歷。內存柵確保在引用新鏈表項之前,它的指針修正對所有讀者可見。


hlist_for_each_rcu(pos, head)

該宏用於遍歷由RCU保護的哈希鏈表head,只要在讀端臨界區使用該函數,它就可以安全地和其它_rcu哈希鏈表操作函數(如hlist_add_rcu)併發運行。


hlist_for_each_entry_rcu(tpos, pos, head, member)

類似於hlist_for_each_rcu,不同之處在於它用於遍歷指定類型的數據結構哈希鏈表,當前鏈表項pos爲一包含struct list_head結構的特定的數據結構。

對於RCU更詳細的原理、實現機制以及應用請參看作者專門針對RCU發表的一篇文章,"Linux 2.6內核中新的鎖機制--RCU(Read-Copy Update)"。



十、順序鎖(seqlock)

順序鎖也是對讀寫鎖的一種優化,對於順序鎖,讀者絕不會被寫者阻塞,也就說,讀者可以在寫者對被順序鎖保護的共享資源進行寫操作時仍然可以繼續讀,而不必等待寫者完成寫操作,寫者也不需要等待所有讀者完成讀操作纔去進行寫操作。但是,寫者與寫者之間仍然是互斥的,即如果有寫者在進行寫操作,其他寫者必須自旋在那裏,直到寫者釋放了順序鎖。

這種鎖有一個限制,它必須要求被保護的共享資源不含有指針,因爲寫者可能使得指針失效,但讀者如果正要訪問該指針,將導致OOPs。

如果讀者在讀操作期間,寫者已經發生了寫操作,那麼,讀者必須重新讀取數據,以便確保得到的數據是完整的。

這種鎖對於讀寫同時進行的概率比較小的情況,性能是非常好的,而且它允許讀寫同時進行,因而更大地提高了併發性。

順序鎖的API如下:


void write_seqlock(seqlock_t *sl);

寫者在訪問被順序鎖s1保護的共享資源前需要調用該函數來獲得順序鎖s1。它實際功能上等同於spin_lock,只是增加了一個對順序鎖順序號的加1操作,以便讀者能夠檢查出是否在讀期間有寫者訪問過。


void write_sequnlock(seqlock_t *sl);

寫者在訪問完被順序鎖s1保護的共享資源後需要調用該函數來釋放順序鎖s1。它實際功能上等同於spin_unlock,只是增加了一個對順序鎖順序號的加1操作,以便讀者能夠檢查出是否在讀期間有寫者訪問過。

寫者使用順序鎖的模式如下:


write_seqlock(&seqlock_a);
//寫操作代碼塊

write_sequnlock(&seqlock_a);

因此,對寫者而言,它的使用與spinlock相同。


int write_tryseqlock(seqlock_t *sl);

寫者在訪問被順序鎖s1保護的共享資源前也可以調用該函數來獲得順序鎖s1。它實際功能上等同於spin_trylock,只是如果成功獲得鎖後,該函數增加了一個對順序鎖順序號的加1操作,以便讀者能夠檢查出是否在讀期間有寫者訪問過。


unsigned read_seqbegin(const seqlock_t *sl);

讀者在對被順序鎖s1保護的共享資源進行訪問前需要調用該函數。讀者實際沒有任何得到鎖和釋放鎖的開銷,該函數只是返回順序鎖s1的當前順序號。


int read_seqretry(const seqlock_t *sl, unsigned iv);

讀者在訪問完被順序鎖s1保護的共享資源後需要調用該函數來檢查,在讀訪問期間是否有寫者訪問了該共享資源,如果是,讀者就需要重新進行讀操作,否則,讀者成功完成了讀操作。

因此,讀者使用順序鎖的模式如下:


do {
seqnum = read_seqbegin(&seqlock_a);
//讀操作代碼塊
...
} while (read_seqretry(&seqlock_a, seqnum));
write_seqlock_irqsave(lock, flags)

寫者也可以用該宏來獲得順序鎖lock,與write_seqlock不同的是,該宏同時還把標誌寄存器的值保存到變量flags中,並且失效了本地中斷。


write_seqlock_irq(lock)

寫者也可以用該宏來獲得順序鎖lock,與write_seqlock不同的是,該宏同時還失效了本地中斷。與write_seqlock_irqsave不同的是,該宏不保存標誌寄存器。


write_seqlock_bh(lock)

寫者也可以用該宏來獲得順序鎖lock,與write_seqlock不同的是,該宏同時還失效了本地軟中斷。


write_sequnlock_irqrestore(lock, flags)

寫者也可以用該宏來釋放順序鎖lock,與write_sequnlock不同的是,該宏同時還把標誌寄存器的值恢復爲變量flags的值。它必須與write_seqlock_irqsave配對使用。


write_sequnlock_irq(lock)

寫者也可以用該宏來釋放順序鎖lock,與write_sequnlock不同的是,該宏同時還使能本地中斷。它必須與write_seqlock_irq配對使用。


write_sequnlock_bh(lock)

寫者也可以用該宏來釋放順序鎖lock,與write_sequnlock不同的是,該宏同時還使能本地軟中斷。它必須與write_seqlock_bh配對使用。


read_seqbegin_irqsave(lock, flags)

讀者在對被順序鎖lock保護的共享資源進行訪問前也可以使用該宏來獲得順序鎖lock的當前順序號,與read_seqbegin不同的是,它同時還把標誌寄存器的值保存到變量flags中,並且失效了本地中斷。注意,它必須與read_seqretry_irqrestore配對使用。


read_seqretry_irqrestore(lock, iv, flags)

讀者在訪問完被順序鎖lock保護的共享資源進行訪問後也可以使用該宏來檢查,在讀訪問期間是否有寫者訪問了該共享資源,如果是,讀者就需要重新進行讀操作,否則,讀者成功完成了讀操作。它與read_seqretry不同的是,該宏同時還把標誌寄存器的值恢復爲變量flags的值。注意,它必須與 read_seqbegin_irqsave配對使用。

因此,讀者使用順序鎖的模式也可以爲:


do {
seqnum = read_seqbegin_irqsave(&seqlock_a, flags);
//讀操作代碼塊
...
} while (read_seqretry_irqrestore(&seqlock_a, seqnum, flags));

讀者和寫者所使用的API的幾個版本應該如何使用與自旋鎖的類似。

如果寫者在操作被順序鎖保護的共享資源時已經保持了互斥鎖保護對共享數據的寫操作,即寫者與寫者之間已經是互斥的,但讀者仍然可以與寫者同時訪問,那麼這種情況僅需要使用順序計數(seqcount),而不必要spinlock。

順序計數的API如下:


unsigned read_seqcount_begin(const seqcount_t *s);

讀者在對被順序計數保護的共享資源進行讀訪問前需要使用該函數來獲得當前的順序號。


int read_seqcount_retry(const seqcount_t *s, unsigned iv);

讀者在訪問完被順序計數s保護的共享資源後需要調用該函數來檢查,在讀訪問期間是否有寫者訪問了該共享資源,如果是,讀者就需要重新進行讀操作,否則,讀者成功完成了讀操作。

因此,讀者使用順序計數的模式如下:


do {
seqnum = read_seqbegin_count(&seqcount_a);
//讀操作代碼塊
...
} while (read_seqretry(&seqcount_a, seqnum));
void write_seqcount_begin(seqcount_t *s);

寫者在訪問被順序計數保護的共享資源前需要調用該函數來對順序計數的順序號加1,以便讀者能夠檢查出是否在讀期間有寫者訪問過。


void write_seqcount_end(seqcount_t *s);

寫者在訪問完被順序計數保護的共享資源後需要調用該函數來對順序計數的順序號加1,以便讀者能夠檢查出是否在讀期間有寫者訪問過。

寫者使用順序計數的模式爲:


write_seqcount_begin(&seqcount_a);
//寫操作代碼塊

write_seqcount_end(&seqcount_a);

需要特別提醒,順序計數的使用必須非常謹慎,只有確定在訪問共享數據時已經保持了互斥鎖纔可以使用。


小結

自linux 2.4以來,內核對SMP的支持越來越好,很大程度上,對SMP的支持,這些鎖機制是非常必要和重要的。基本上,內核開發者在開發中都會需要使用一些同步機制,本文通過詳細地講解內核中所有的同步機制,使得讀者能夠對內核鎖機制有全面的瞭解和把握。

發佈了24 篇原創文章 · 獲贊 8 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章