亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
樓主: albcamus
打印 上一主題 下一主題

Unreliable Guide to Locking -by Rusty Russell-中文版 [復制鏈接]

論壇徽章:
0
11 [報告]
發(fā)表于 2005-11-25 17:56 |只看該作者
第8章. 加鎖速度


在考慮使用鎖的代碼的速度問題上,主要有三件事需要關注。首先是并發(fā)性:當鎖被別人持有時,有多少事情是我們需要等待的。其次是,需要花多少時間來獲取與釋放一把無爭議的鎖。第三呢,是盡量使用更少的──或曰更靈活的──鎖。當然,這里我已經假定鎖的使用非常頻繁,否則你都不用擔心效率問題的。

并發(fā)性依賴于鎖被持有多長時間:你持有鎖的時間,就應該同確實需要持有它的時間一樣長,不要更長。在上面那個關于cache的例子中,我們在創(chuàng)建對象時是不持有鎖的,只有在已經準備好把它插入到鏈表中時才去獲取鎖。

獲取鎖的時間依賴于加鎖操作對于管道線(pipeline stalls)做了多少破壞性工作,以及本CPU是最后一個試圖獲取該鎖的CPU(亦即:對本CPU來說,鎖是否為緩存密集的(cache-hot))的可能性有多大:在具有很多CPU的機器上,這種可能性迅速降低?紤]一個700MHz的Intel Pentium III處理器:一條指令大約花費0.7納秒,一次原子增加操作大約花費58納秒,一次緩存密集的的加鎖操作大約花費160納秒,而從其他CPU上的一次緩存行轉換還要額外花費170到360納秒。(這些數據來自于Paul McKenney的Linux Journal RCU article )

這兩個目標會發(fā)生沖突:通過把鎖分成不同的的部分,可以減少持有鎖的時間(例如上面例子中“每個對象一把的鎖”),但是這增加了獲取鎖這一操作的數量,因而結果一般是比只有一把鎖的情況要慢一些。這也是推崇加鎖簡潔性的一個理由。

第三個考慮點放到了下面:的確存在一些方法能夠減少鎖的使用。


8.1. 讀/寫鎖變體


自旋鎖與信號量都具有讀/寫變體:rwlock_t和struct rw_semaphore。它們把使用者分成了兩類:讀者和寫者。如果你只需要讀數據,你該獲取讀鎖;但如果你需要寫數據,你需要獲取寫鎖?梢杂泻芏嗍褂谜咄瑫r獲取讀鎖,但寫鎖卻只能有單個使用者持有。

如果你的代碼可以簡潔的劃分出讀和寫的界限(就象我們上面的cache代碼那樣),而且讀者通常需要很長時間的持有讀鎖,使用讀寫鎖會更好。它們要比普通的鎖要稍微慢點兒,所以在實踐中rwlock_t通常并不很值得使用。


8.2. 避免鎖的使用:RCU


有一種特殊的讀/寫鎖定方法叫做RCU(Read Copy Update),讀者可以完全避免使用鎖:由于我們希望我們的cache被讀取遠比被更新來的頻繁(否則的話,還引入cache干什么?),使用RCU就是一個可選擇的優(yōu)化。

如何移除讀鎖呢?移除讀鎖意味著寫者可能在存在讀者的情況下更新鏈表。這很簡單:要向鏈表中添加元素的代碼足夠小心,我們就可以在它添加的同時讀取鏈表。例如,把new元素添加到list鏈表中:


  1.         new->next = list->next;
  2.         wmb();
  3.         list->next = new;
  4.    
復制代碼

wmb()是一個寫內存屏障。它保證了第一個操作(設置新元素的next指針)是完整的,而且立即為其它CPU所見,這發(fā)生在第二個操作(把新元素添加到鏈表中)之前。這是很重要的,因為現代CPU和編譯器可能會改變指令的執(zhí)行順序,除非明確告訴它們不要這樣:我們希望一個讀者要么完全看不到新元素的插入,要么能夠看到新元素插入之后的next指針正確指向了鏈表的剩余部分。

幸運的是,有一個函數專門添加標準的struct list_head鏈表:list_add_rcu() (include/linux/list.h)。

從鏈表中刪除元素更簡單:假設我們要刪除的元素為A,我們讓指向A的指針重新指向A的后繼者,讀者要么完全看到了它,要么完全忽略了它。


        list->next = old->next;   

有一個list_del_rcu()函數(include/linux/list.h)來做該工作(而普通版本的刪除操作會毒害(poison)要移除的對象,這是我們不愿看到的)。

讀者也一定要小心:有些CPU會觀察next指針以便預取下一個元素,但這樣的話,如果next指針恰恰在這時候發(fā)生了變化,那么這些CPU預取的(pre-fetched)內容就是錯誤的。還好,有一個list_fro_each_entry_rcu()函數(include/linux/list.h)可以幫助你。當然,寫者可以只使用list_for_each_entry() (譯者案:這里的“只使用”,是說不用但是是否需要一個XXX_rcu()這樣的函數),因為不可能同時有兩個寫者。

我們的困難最終是:什么時候我們可以真正的刪除元素?記住,一個讀者可能正在訪問要被刪除的元素:如果我們釋放掉這個元素,而讓next指針指向新元素,讀者將立即訪問到垃圾內存并崩潰。我們必須等待到所有正在遍歷鏈表的讀者都結束了遍歷,才能真正釋放掉元素。我們用call_rcu()函數注冊一個回調函數,當所有讀者結束遍歷時,它會真正銷毀對象。

但是,RCU如何得知什么時候讀者結束遍歷了呢?首先,讀者總是在rcu_read_lock()/rcu_read_unlock()之間執(zhí)行遍歷:這會禁止搶占(而且只是禁止搶占,其它什么都不做),因此讀者在遍歷鏈表期間不會睡眠。

RCU一直等待到其他的CPU至少睡眠了一次:因為讀者不會在讀取過程中睡眠,此時我們就知道我們等待其結束的那些讀者終于都結束了。于是回調函數被調用。真實的RCU代碼要比這里講述的更優(yōu)化些,但這里講的是它的基礎。


  1. --- cache.c.perobjectlock 2003-12-11 17:15:03.000000000 +1100
  2. +++ cache.c.rcupdate    2003-12-11 17:55:14.000000000 +1100
  3. @@ -1,15 +1,18 @@
  4. #include <linux/list.h>
  5. #include <linux/slab.h>
  6. #include <linux/string.h>
  7. +#include <linux/rcupdate.h>
  8. #include <asm/semaphore.h>
  9. #include <asm/errno.h>

  10. struct object
  11. {
  12. -        /* 下面兩個域由cache_lock保護 */
  13. +        /* 下面由RCU保護 */
  14.          struct list_head list;
  15.          int popularity;

  16. +        struct rcu_head rcu;
  17. +
  18.          atomic_t refcnt;

  19.          /* 一旦創(chuàng)建就不要更改. */
  20. @@ -40,7 +43,7 @@
  21. {
  22.          struct object *i;

  23. -        list_for_each_entry(i, &cache, list) {
  24. +        list_for_each_entry_rcu(i, &cache, list) {
  25.                  if (i->id == id) {
  26.                          i->popularity++;
  27.                          return i;
  28. @@ -49,19 +52,25 @@
  29.          return NULL;
  30. }

  31. +/* 一旦我們知道沒有讀者了,立即最終丟棄對象. */
  32. +static void cache_delete_rcu(void *arg)
  33. +{
  34. +        object_put(arg);
  35. +}
  36. +
  37. /* 必須在持有cache_lock的情況下調用 */
  38. static void __cache_delete(struct object *obj)
  39. {
  40.          BUG_ON(!obj);
  41. -        list_del(&obj->list);
  42. -        object_put(obj);
  43. +        list_del_rcu(&obj->list);
  44.          cache_num--;
  45. +        call_rcu(&obj->rcu, cache_delete_rcu, obj);
  46. }

  47. /* 必須在持有cache_lock的情況下調用 */
  48. static void __cache_add(struct object *obj)
  49. {
  50. -        list_add(&obj->list, &cache);
  51. +        list_add_rcu(&obj->list, &cache);
  52.          if (++cache_num > MAX_CACHE_SIZE) {
  53.                  struct object *i, *outcast = NULL;
  54.                  list_for_each_entry(i, &cache, list) {
  55. @@ -85,6 +94,7 @@
  56.          obj->popularity = 0;
  57.          atomic_set(&obj->refcnt, 1); /* cache本身占一個引用計數 */
  58.          spin_lock_init(&obj->lock);
  59. +        INIT_RCU_HEAD(&obj->rcu);

  60.          spin_lock_irqsave(&cache_lock, flags);
  61.          __cache_add(obj);
  62. @@ -104,12 +114,11 @@
  63. struct object *cache_find(int id)
  64. {
  65.          struct object *obj;
  66. -        unsigned long flags;

  67. -        spin_lock_irqsave(&cache_lock, flags);
  68. +        rcu_read_lock();
  69.          obj = __cache_find(id);
  70.          if (obj)
  71.                  object_get(obj);
  72. -        spin_unlock_irqrestore(&cache_lock, flags);
  73. +        rcu_read_unlock();
  74.          return obj;
  75. }
復制代碼


注意讀者會在__cache_find()中改變popularity域的值,但是沒有使用鎖。我們的方案應該把它變成atomic_t類型的,但是對本例來說我們不會導致競態(tài)條件:近似的結果就已經不錯了,所以我沒做改變。

結果是,cache_find()函數并不需要與其它函數同步,因此它在SMP上幾乎跟在UP上一樣快。

此處還存在一個更縱深的可能的優(yōu)化:想一想我們最初的cache代碼,那時沒有引用計數,無論何時調用者想訪問對象,就要持有鎖。這仍然是可能的:如果你持有鎖,就沒人能夠刪掉對象,因此你就不必讀取并寫回引用計數了。

由于RCU中的“讀鎖”會禁止搶占(而且只是禁止搶占,其它什么都不做),調用者如果調用cache_find()或object_put()之前已經禁止搶占了,就并不需要讀取并寫回引用計數:我們可以把__cache_find()變成非static的從而暴露給其它文件,這時候調用者就可以直接調用這些函數了。

此處的優(yōu)勢是:引用計數并不被寫入,對象并不被更改,這在SMP機器上會非?飑ぉび捎贑PU的caching的原因。


8.3. Per-CPU數據


另一種使用相當廣泛的避免鎖的技術是,為每個CPU使用數據的一份拷貝。例如,如果你想為一個普通的條件保持一個計數,你可能會使用自旋鎖和一個計數器,這簡單而漂亮。

如果它表現的太慢了(通常不會,但是如果你的確測試到這種情況發(fā)生了),你就該改變一下策略:為每個CPU保持一個計數器,這樣,沒有一個計數器需要互斥的鎖來保護。參考DEFINE_PER_CPU(),get_cpu_var()和put_cpu_var() (include/linux/percpu.h)。

Per-CPU計數器的另一個用場是local_t數據類型,和cpu_local_inc()以及其它的相關函數。這在某些體系結構上是比簡單的加鎖代碼更高效的。(include/asm/local.h)

注意,對計數器來說,根本不存在簡單而可靠的方法可以精確的得到它的值而不需要引入鎖。只不過某些情況下這不是問題罷了。


8.4. 中斷服務例程通常使用哪些數據


如果數據只是在中斷服務例程內部訪問,你根本不需要鎖:內核本身已經保證了中斷服務例程不會同時在多個CPU上運行。

Manfred Spraul指出,即使數據會偶爾被用戶上下文或softirqs/tasklets訪問,你仍然可以這樣。中斷服務例程自身不需要鎖,其它所有的訪問者這樣做:


  1.         spin_lock(&lock);
  2.         disable_irq(irq);
  3.         ...
  4.         enable_irq(irq);s
  5.         spin_unlock(&lock);
復制代碼

disable_irq()禁止了中斷服務例程的運行(如果此刻已經在別的CPU上運行了,那就等待它的結束)。自旋鎖禁止了同時刻其它任何可能的訪問。自然,這比單個spin_lock_irq()調用要慢些,所以只有這種訪問很少發(fā)生時這種用法才是合理的。

論壇徽章:
0
12 [報告]
發(fā)表于 2005-11-25 17:57 |只看該作者
第9章. 中斷里調用哪些函數是安全的?


內核中許多函數會導致睡眠(亦即,直接或間接地調用了scheduler()):當持有鎖或禁止了搶占時,你不可以調用它們。這同樣意味著只有在用戶上下文才可以調用:在中斷里調用它們是非法的。


9.1. 一些可能睡眠的函數


下面列出了最常見的幾個,但通常來講,你需要閱讀代碼來判斷其它的函數是否可以在中斷里安全的調用。如果每一個人都是在可睡眠環(huán)境下調用某個函數的,那么多半你也要保證可睡眠的環(huán)境(譯者案:原文是“If everyone else who calls it can sleep, you probably need to be able to sleep, too.”) 特別地,注冊與注銷函數通常需要在用戶上下文中調用,它們可能睡眠。

    *

      訪問用戶空間:
          o

            copy_from_user()
          o

            copy_to_user()
          o

            get_user()
          o

            put_user()
    *

      kmalloc(GFP_KERNEL)
    *

      down_interruptible() 和 down()

      有一個down_trylock()函數,它可以在中斷上下文中調用,不會睡眠。Up()決不會導致睡眠。

9.2. 一些不會睡眠的函數


有些函數可以在任何上下文中調用,可以持有任何的鎖。

    *

      printk()
    *

      kfree()
    *

      add_timer() 和 del_timer()

論壇徽章:
0
13 [報告]
發(fā)表于 2005-11-25 17:57 |只看該作者
第10章. 進一步閱讀


    *

      Documentation/spinlock.txt: 這是Linus Torvalds的內核源代碼中的自旋鎖教程
    *

      Unix Systems for Modern Architectures: Symmetric Multiprocessing and Caching for Kernel Programmers.

      Curt Schimmel的一部對內核級加鎖的極好的著作(不是為Linux寫的,但幾乎都適用)。書可能貴了些,但對于理解SMP加鎖來說,它值這個價錢。[ISBN:0201633388]

      (譯者案:中文譯本《現代體系結構上的Unix系統(tǒng):內核程序員的SMP和Caching技術》,人民郵電出版社2003年4月出版,定價¥39,共289頁,翻譯非常不錯,是本值得閱讀的好書。)

第11章. 致謝


感謝Telsa Gwynne在DocBook化文檔上的幫助,整理與添加風格。

感謝Martin Pool,Phlipp Rumpf,Stephen rothwell,Paul Mackerras,Ruedi Aschwanden,Alan Cox,Manfred Spraul,Time Waugh,Pete Zaitcev,James Morris,Robert Love,Paul McKennney,John Ashby,感謝他們的校對、勘誤、爭論和評注。

感謝小團體(譯者案:原詞cabal,原意是陰謀團體,似乎具有反諷意味,諷刺有些人把Linux內核開發(fā)團隊稱為cabal的FUD行為)沒有影響該文檔。

論壇徽章:
0
14 [報告]
發(fā)表于 2005-11-25 17:58 |只看該作者
術語

(譯者案:為了方便理解,本頁所有術語條目一律保留其英文)


搶占,preemption
    在2.5內核之前,或編譯時CONFIG_PREEMPT未打開,內核中用戶上下文的進程不會彼此搶占(亦即:只要你占有CPU,你就一直占有,直到自己放棄或者發(fā)生硬件中斷)。在2.5.4以后且打開了CONFIG_PREEMPT,事情改變了:在用戶上下文,更高優(yōu)先級的任務可以搶占當前任務:自旋鎖為此改寫,以添加禁止搶占的效果,即使在UP上也是如此。

bh
    下半部:由于歷史原因,帶有'_bh'后綴的函數現在通常指所有的軟中斷。例如spin_lock_bh()將阻止任何軟中斷在本CPU上的運行。原來的BH是不該繼續(xù)使用的,它終將被tasklets取代。在任一給定時刻,一個下半部只可能有一個實例在運行。
    (譯者注:Linux內核的各種下半部的命名混亂不堪,例如上面這句其實就不太嚴謹。tasklet/timer誠然是“任何時刻只可能有一個實例在運行”,但是一個softirq就有可能同時在多個CPU上運行。推薦閱讀Robert Love的Linux Kernel Development 一書來理清個中關系)

硬件中斷/硬中斷,Hardware Interrupt/Hardware IRQ
    硬件中斷請求。在硬件中斷處理函數中,in_irq()宏返回true。

中斷上下文,Interrupt Context
    非用戶上下文:正在處理一個硬件中斷或軟中斷。這種情況下,in_interrupt()宏返回true。

對稱多處理器,SMP
    對稱多處理器:為多CPU機器編譯的內核。(CONFIG_SMP=y).

軟中斷/softirq,Software Interrupt/softirq
    軟中斷處理程序。in_irq()返回false;in_softirq()返回true。Tasklets和softirqs都屬于軟中斷。
    嚴格來講,一個softirq是一個最多能有32個枚舉類型的軟中斷,它可以同時運行在多個CPU上。有時softirq一詞也用來指謂tasklets(也就是說,指謂所有的軟中斷)。

tasklet
    一種可動態(tài)注冊的軟中斷,保證了同一時刻只能有一個CPU在運行它。

定時器,timer
    一種可動態(tài)注冊的軟中斷,它在給定時刻(或接近給定時刻)運行。當運行時,它就象tasklet一樣(事實上它們是作為TIMER_SOFTIRQ被調用的)。

單處理器,UP
    單處理器:非SMP。(CONFIG_SMP=n)

用戶上下文,User Context
    內核代表某個進程(亦即,一次系統(tǒng)調用或陷阱)或內核線程在運行。你可以用current宏得知究竟是哪個進程。注意不要與“用戶空間”一詞搞混。它可以被硬件或軟中斷打斷執(zhí)行。

用戶空間,Userspace
    進程在內核之外執(zhí)行自己的代碼。

翻譯后記

    由于本人水平所限,加上沒有SMP環(huán)境試驗一些理解正確與否,文檔誤譯之處肯定不少(有些不會翻譯的干脆就保留原文了,如您所見)。 無論您有什么觀點,請與albcamus@163.com聯系,歡迎批評指正,我會盡量跟蹤改進本文檔的翻譯。如果您英文可以的話,還是看原版吧,作者的行文并不難懂。
    在翻譯過程中,alert7前輩為我提供了他的譯稿做參考,并指正了很多問題,在此向他表示感謝!──當然,誤譯之處只由我自己負責。
    趁十一熬夜,05年10月05日凌晨6點

論壇徽章:
0
15 [報告]
發(fā)表于 2005-11-25 18:01 |只看該作者
HTML排版真是糟糕,論壇上更加無法整理。
制作了一個PDF文件,里面的超鏈接暫時不可用,等我改好了再另外上傳一份吧

kernel_locking_CN.pdf

816.28 KB, 下載次數: 508

kernel_locking_CN.pdf

論壇徽章:
0
16 [報告]
發(fā)表于 2005-11-28 16:25 |只看該作者
傳上來3天了,一點回應沒有,沒有人對互斥感興趣?
寫內核態(tài)代碼,一不注意就會崩潰,沒人需要這個?

論壇徽章:
0
17 [報告]
發(fā)表于 2005-11-28 17:13 |只看該作者
不懂也是要支持一下啦

不然論壇里就只剩下“急急急,我的linux上不了網”之流的帖子啦

論壇徽章:
0
18 [報告]
發(fā)表于 2005-11-29 13:23 |只看該作者
感謝,感謝,最近正在看互斥的一些東西!。!

論壇徽章:
0
19 [報告]
發(fā)表于 2005-12-23 09:28 |只看該作者
這個帖子加個精華,方便別人查找──大家沒意見吧?

論壇徽章:
0
20 [報告]
發(fā)表于 2005-12-23 10:38 |只看該作者
呵呵,老早就拜讀過了,不過只看了描述部分,涉及到linux的實現就全部略過了,以后再慢慢看...鎖真讓人頭痛...搞通了的人,都是機器人...
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復

  

北京盛拓優(yōu)訊信息技術有限公司. 版權所有 京ICP備16024965號-6 北京市公安局海淀分局網監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯網協(xié)會會員  聯系我們:huangweiwei@itpub.net
感謝所有關心和支持過ChinaUnix的朋友們 轉載本站內容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP