RCU機制是Linux2.6之後提供的一種數據一致性訪問的機制,從RCU(read-copy-update)的名稱上看,我們就能對他的實現機制有一個大概的了解,在修改數據的時候,首先需要讀取數據,然後生成一個副本,對副本進行修改,修改完成之後再將老數據update成新的數據,此所謂RCU。
在操作系統中,數據一致性訪問是一個非常重要的部分,通常我們可以采用鎖機制實現數據的一致性訪問。例如,semaphore、spinlock機制,在訪問共享數據時,首先訪問鎖資源,在獲取鎖資源的前提下才能實現數據的訪問。這種原理很簡單,根本的思想就是在訪問臨界資源時,首先訪問一個全局的變量(鎖),通過全局變量的狀態來控制線程對臨界資源的訪問。但是,這種思想是需要硬件支持的,硬件需要配合實現全局變量(鎖)的讀-修改-寫,現代CPU都會提供這樣的原子化指令。采用鎖機制實現數據訪問的一致性存在如下兩個問題:
1. 效率問題。鎖機制的實現需要對內存的原子化訪問,這種訪問操作會破壞流水線操作,降低了流水線效率。這是影響性能的一個因素。另外,在采用讀寫鎖機制的情況下,寫鎖是排他鎖,無法實現寫鎖與讀鎖的並發操作,在某些應用下會降低性能。
2. 擴展性問題。當系統中CPU數量增多的時候,采用鎖機制實現數據的同步訪問效率偏低。並且隨著CPU數量的增多,效率降低,由此可見鎖機制實現的數據一致性訪問擴展性差。
為了解決上述問題,Linux中引進了RCU機制。該機制在多CPU的平台上比較適用,對於讀多寫少的應用尤其適用。
RCU的思路實際上很簡單,如下所示:
1. 對於讀操作,可以直接對共享資源進行訪問,但是前提是需要CPU支持訪存操作的原子化,現代CPU對這一點都做了保證。但是RCU的讀操作上下文是不可搶占的(這一點在下面解釋),所以讀訪問共享資源時可以采用read_rcu_lock(),該函數的工作是停止搶占。
2 . 對於寫操作,其需要將原來的老數據作一次備份(copy),然後對備份數據進行修改,修改完畢之後再用新數據更新老數據,更新老數據時采用了rcu_assign_pointer()宏,在該函數中首先屏障一下memory,然後修改老數據。這個操作完成之後,需要進行老數據資源的回收。操作線程向系統注冊回收方法,等待回收。采用數據備份的方法可以實現讀者與寫者之間的並發操作,但是不能解決多個寫者之間的同步,所以當存在多個寫者時,需要通過鎖機制對其進行互斥,也就是在同一時刻只能存在一個寫者。
3. 在RCU機制中存在一個垃圾回收的daemon,當共享資源被update之後,可以采用該daemon實現老數據資源的回收。回收時間點就是在update之前的所有的讀者全部退出。由此可見寫者在update之後是需要睡眠等待的,需要等待讀者完成操作,如果在這個時刻讀者被搶占或者睡眠,那麼很可能會導致系統死鎖。因為此時寫者在等待讀者,讀者被搶占或者睡眠,如果正在運行的線程需要訪問讀者和寫者已經占用的資源,那麼死鎖的條件就很有可能形成了。
RCU機制是Linux2.6之後提供的一種數據一致性訪問的機制,目前在linux內核中被大量的使用。在修改數據的時候,首先需要讀取數據,然後生成一個副本,對副本進行修改,修改完成之後再將老數據update成新的數據,此所謂RCU。
從上述分析來看,RCU思想是比較簡單的,其核心內容緊緊圍繞“寫時拷貝”,采用RCU機制,能夠保證在讀寫操作共享資源時,基本不需要取鎖操作,能夠在一定程度上提升性能。但是該機制的應用是有條件的,對於讀多寫少的應用,機制的開銷比較小,性能會大幅度提升,但是如果寫操作較多時,開銷將會增大,性能不一定會有所提升。總體來說,RCU機制是對rw_lock的一種優化。