引子
在編譯2.6內核的時候,你會在編譯選項中看到[*] Enable futex support這一項,上網查,有的資料會告訴你"不選這個內核不必定能正確的運行使用glibc的程序",那futex是什麼?和glibc又有什麼關係呢?
1. 什麼是Futex
Futex 是Fast Userspace muTexes的縮寫,由Hubertus Franke, Matthew Kirkwood, Ingo Molnar and Rusty Russell共同設計完成。幾位都是linux領域的專家,其中可能Ingo Molnar你們更熟悉一些,畢竟是O(1)調度器和CFS的實現者。
Futex按英文翻譯過來就是快速用戶空間互斥體。其設計思想其實 不難理解,在傳統的Unix系統中,System V IPC(inter process communication),如 semaphores, msgqueues, sockets還有文件鎖機制(flock())等進程間同步機制都是對一個內核對象操做來完成的,這個內核對象對要同步的進程都是可見的,其提供了共享 的狀態信息和原子操做。當進程間要同步的時候必需要經過系統調用(如semop())在內核中完成。但是經研究發現,不少同步是無競爭的,即某個進程進入 互斥區,到再從某個互斥區出來這段時間,經常是沒有進程也要進這個互斥區或者請求同一同步變量的。可是在這種狀況下,這個進程也要陷入內核去看看有沒有人 和它競爭,退出的時侯還要陷入內核去看看有沒有進程等待在同一同步變量上。這些沒必要要的系統調用(或者說內核陷入)形成了大量的性能開銷。爲了解決這個問 題,Futex就應運而生,Futex是一種用戶態和內核態混合的同步機制。首先,同步的進程間經過mmap共享一段內存,futex變量就位於這段共享 的內存中且操做是原子的,當進程嘗試進入互斥區或者退出互斥區的時候,先去查看共享內存中的futex變量,若是沒有競爭發生,則只修改futex,而不 用再執行系統調用了。當經過訪問futex變量告訴進程有競爭發生,則仍是得執行系統調用去完成相應的處理(wait 或者 wake up)。簡單的說,futex就是經過在用戶態的檢查,(motivation)若是瞭解到沒有競爭就不用陷入內核了,大大提升了low-contention時候的效率。 Linux從2.5.7開始支持Futex。
2. Futex系統調用
Futex是一種用戶態和內核態混合機制,因此須要兩個部分合做完成,linux上提供了sys_futex系統調用,對進程競爭狀況下的同步處理提供支持。
其原型和系統調用號爲
#include <linux/futex.h>
#include <sys/time.h>
int futex (int *uaddr, int op, int val, const struct timespec *timeout,int *uaddr2, int val3);
#define __NR_futex 240
雖然參數有點長,其實經常使用的就是前面三個,後面的timeout你們都能理解,其餘的也常被ignore。
uaddr就是用戶態下共享內存的地址,裏面存放的是一個對齊的整型計數器。
op存放着操做類型。定義的有5中,這裏我簡單的介紹一下兩種,剩下的感興趣的本身去man futex
FUTEX_WAIT: 原子性的檢查uaddr中計數器的值是否爲val,若是是則讓進程休眠,直到FUTEX_WAKE或者超時(time-out)。也就是把進程掛到uaddr相對應的等待隊列上去。
FUTEX_WAKE: 最多喚醒val個等待在uaddr上進程。
可見FUTEX_WAIT和FUTEX_WAKE只是用來掛起或者喚醒進程,固然這部分工做也只能在內核態下完成。有些人嘗試着直接使用futex系統調 用來實現進程同步,並寄但願得到futex的性能優點,這是有問題的。應該區分futex同步機制和futex系統調用。futex同步機制還包括用戶態 下的操做,咱們將在下節提到。
3. Futex同步機制
全部的futex同步操做都應該從用戶空間開始,首先建立一個futex同步變量,也就是位於共享內存的一個整型計數器。
當 進程嘗試持有鎖或者要進入互斥區的時候,對futex執行"down"操做,即原子性的給futex同步變量減1。若是同步變量變爲0,則沒有競爭發生, 進程照常執行。若是同步變量是個負數,則意味着有競爭發生,須要調用futex系統調用的futex_wait操做休眠當前進程。
當進程釋放鎖或 者要離開互斥區的時候,對futex進行"up"操做,即原子性的給futex同步變量加1。若是同步變量由0變成1,則沒有競爭發生,進程照常執行。如 果加以前同步變量是負數,則意味着有競爭發生,須要調用futex系統調用的futex_wake操做喚醒一個或者多個等待進程。
這裏的原子性加減一般是用CAS(Compare and Swap)完成的,與平臺相關。CAS的基本形式是:CAS(addr,old,new),當addr中存放的值等於old時,用new對其替換。在x86平臺上有專門的一條指令來完成它: cmpxchg。
可見: futex是從用戶態開始,由用戶態和核心態協調完成的。
4. 進/線程利用futex同步
進程或者線程均可以利用futex來進行同步。
對於線程,狀況比較簡單,由於線程共享虛擬內存空間,虛擬地址就能夠惟一的標識出futex變量,即線程用一樣的虛擬地址來訪問futex變量。
對 於進程,狀況相對複雜,由於進程有獨立的虛擬內存空間,只有經過mmap()讓它們共享一段地址空間來使用futex變量。每一個進程用來訪問futex的 虛擬地址能夠是不同的,只要系統知道全部的這些虛擬地址都映射到同一個物理內存地址,並用物理內存地址來惟一標識futex變量。
小結:
1. Futex變量的特徵:1)位於共享的用戶空間中 2)是一個32位的整型 3)對它的操做是原子的
2. Futex在程序low-contention的時候能得到比傳統同步機制更好的性能。
3. 不要直接使用Futex系統調用。
4. Futex同步機制能夠用於進程間同步,也能夠用於線程間同步。linux
from:http://blog.csdn.net/jianchaolv/article/details/7544316socket