1. 程式人生 > >linux 內核的futex - requeue 以及 requeue-pi

linux 內核的futex - requeue 以及 requeue-pi

所在 com 進行 images 行鎖 __user long mage 條件

futex為更好支持pthread_cond的實現(,最主要是broadcast),設計了requeue功能,並以futex系統調用提供操作接口,包括一對配對的操作 futex_wait_requeue_pi 以及 futex_requeue。

mutex互斥體,確保臨界區之間互斥(mutual exclusion),但不能滿足不同臨界區之間的前驅後繼的關系,所以可以通過在臨界區A使用condition variable條件變量,等待另一臨界區B的發出信號指令。

Condition Variables
Synchronization mechanisms need more than just mutual exclusion; also need a way to wait 
for another thread to do something.

臨界區A,與臨界區B雖然使用了mutex,確保彼此之間互斥(mutual exclusion)。但是同時使用了condvar,保證臨界區B前驅,臨界區A後繼,即臨界區A等待臨界區B先完成某些事。

一個condvar必須從屬於(belong to)一個mutex。

因此condvar的等待(wait)包含了兩次等待,或兩個隊列等待。一個是condvar信號的等待(隊列),另一個是它所屬的mutex鎖的等待(隊列)。這使得一次condvar wait必須先釋放mutex,然後依次等待condvar信號和mutex。這意味著等待的線程必須進行兩次等待和兩次喚醒。

對condvar發起信號signal,就是喚醒其中一個等待在condvar的線程,讓它轉而等待condvar所屬的mutex,再次回到臨界區執行。而broadcast最簡單的方法就是將所在等待在condvar的線程統統喚醒。

當對condvar進行broadcast時,多個等待在condvar的線程被喚醒回到用戶空間,一起去競爭condvar所屬的mutex,而又每個線程又因鎖競爭必須再次回到內核進行排隊等待,從而造成了多次的線程切換,系統調用的浪費。

為了改善這種浪費,可以將一個等待在兩個等待隊列之間的轉換,優化在一次調用中進行,避免多個線程被喚醒去競爭鎖。這樣的功能就是futex提供的requeue。

被requeue後的waiter(,調用futex_wait_requeue_pi而阻塞的線程),必須等待condvar所屬的mutex去喚醒。當waiter從futex_wait_requeue_pi系統調用阻塞中被喚醒,並不表示已經獲得了condvar所屬的mutex鎖,而是要去競爭這個鎖。

requeue將這個過程的condvar和mutex看作為futex1和futex2。

futex_requeue函數原型為

static int futex_requeue(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2, int nr_wake, int nr_requeue, u32 *cmpval, int requeue_pi);

這個函數的功能是,將uaddr1對應的futex等待隊列出隊最多 (nr_wake + nr_requeue) 個futex_q,先對出隊的futex_q進行喚醒nr_wake個,剩下的才進行requeue到futex2等待隊列。而pthread_cond_broadcast固定設定參數 nr_wake = 1,nr_requeue = INT_MAX。就是說在broadcast的時候只會喚醒一個線程去競爭futex2,其余阻塞在futex1的線程都會出隊再入隊到futex2的等待隊列。

requeue-pi 就是condvar所屬的mutex是一個使用pi協議的mutex,即futex2是pi-futex,requeue對一個non-pi futex的waiters出隊再入隊到pi-futex等待隊列進行特殊的優化。futex_requeue函數只會將可以馬上獲得pi-futex鎖的線程喚醒,也就是說對每一個requeue到pi-futex的waiter都先嘗試鎖pi-futex,嘗試成功的才能被喚醒,直到有線程被喚醒,其余waiter就直接requeue到pi-futex,而不再進行鎖嘗試。雖然futex_proxy_trylock_atomic並不像futex_lock_pi那樣會去調用rt_mutex_trylock,但是與non-pi到non-pi的requeue,增加了額外的嘗試。

下面是requeue non-pi 和 requeue-pi 分支的邏輯對比:

技術分享

技術分享

linux 內核的futex - requeue 以及 requeue-pi