随笔 - 211  文章 - 0  评论 - 50  阅读 - 32万

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

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 分支的逻辑对比:

 

posted on   bbqz007  阅读(2270)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示