More than code

More Than Code
The efficiency of your iteration of reading, practicing and thinking decides your understanding of the world.
  1. 首页
  2. 未分类
  3. 正文

brpc-5 Futex

2022年6月6日 443点热度 0人点赞 0条评论

Futex

在进入到brpc之前,先看看linux的futex是怎么用的。

man page中有比较详细的描述

可以把futex看作是一个原子的compare-and-block,他会原子的比较目标地址上的值,并进入睡眠。

通常的用法就是我们先通过一个用户空间的原子量来表示锁。当一个线程发现锁已经被获得了以后,他就会通过futex来睡眠,直到锁不再被其他人占有。

(感觉实现起来需要特殊处理signal,我们只要保证signal不出现在compare和block之间,就不会出现lost wakeup的问题)

futex主要的操作就两个,wake和wait

20220606093355

brpc已经帮我们封装好了。其中wait就是当addr1上的值为expected的时候,我们就睡眠。wake则是唤醒至多nwake个在addr1上睡眠的worker。

一般来说,nwake等于1的时候就是notify_one,nwake等于INT_MAX则是notify_all

addr1则是指向一个atmoic<int>的指针。这个32位的值被称为futex word

brpc在OSX中实现了一个模拟的futex,我们可以看一下他是怎么实现的

20220606094904

20220606094950

然后是一个futex map,这里应该就是我们要等待的地址到futex的映射了。

而上面的SimuFutex则是相当于内核内部的工作了。

20220606095129

pthread_once的作用就是保证只会执行一次InitFutexMap。而那个init_futex_map_once是一个静态变量,可以看作是一个标志位。

我们首先通过要等待的addr找到对应的SimuFutex。增加引用计数。这里是为了让我们可以在没有人用这个futex的时候释放掉他

我们首先拿到这个futex上的锁,然后再执行一次原子load。如果不同的话就直接返回不需要等待了。否则的话进开始睡眠。增加一个count,表示在这个futex上等待的线程数量。然后根据是否有timeout来在cv上等待。

20220606100123

而wake就更简单了,找到对应的futex,然后在cv上唤醒nwake个thread

但是可以发现在futex wait中并不是一个循环的等待,也就是说当这个pthread被唤醒的时候,futex word上的值可能还是expected。

这个其实在manpage中也说的比较清楚:
This operation tests that the value at the futex word pointed to by the address uaddr still contains the expected value val, and if so, then sleeps waiting for a FUTEX_WAKE operation on the futex word.

可以看到和我上面的推测是类似的,我们需要保证compare和sleep的原子性,中间不能让其他的signal进入。这里的futex实现就是要获得内部的futex lock之后才能进行signal。

bthread中的parking lot就是通过futex来实现的。

整体代码相当少

20220606101406

parking lot的作用就是当bthread worker没有任务的时候,用来阻塞的地方。比如目前没有bthread,那么bthread worker就可以阻塞住,直到新的任务到来才会被唤醒。(有点类似cv,只不过我们可以控制唤醒的worker的数量,并且不需要锁)

20220606102812

可以看到之前的wait task中,如果我们发现当前的状态和上一次的状态相同的话,说明没有后续的任务进入,我们就可以睡眠等待。

当新的bthread进入的时候,task control会调用signal(1)表示一个新的bthread进入,我们就会唤醒一个worker来执行任务。

通过parking lot避免了worker的忙等待。本质就是一个计数器,通过记录任务的数量来控制bthread worker,从而防止他们不断在本地队列或者远端队列阻塞的情况

假设我们没有parking lot,那么worker就需要不断尝试从本地队列或者远端队列去pop任务,然而由于有两个队列,我们不能通过阻塞的队列来让他们一直等待

20220606103716

我之前实现的TinyThread就会不断的忙等待直到出现新的任务。

bthread通过任务数量而非任务队列来控制worker,非常巧妙的设计。

从futex的设计其实也可以看出来expected value是用来防止更新丢失的。因为他相当于是一个逻辑的锁,当我们在睡眠之前,会判断futex word和expected value是否相同,如果相同说明我们还拿着这个锁,我们就可以安心的睡眠。否则的话说明我们这个锁已经被别人拿走了,我们不应该去睡眠。即当我们拿到expected value的时候就相当于获得了这个逻辑锁,我们希望的是在我们睡眠之前,不要有人修改futex word。通过在真正睡眠的临界区进行判断,我们可以保证这个过程是串行的,即要么我们先睡眠,然后有人修改值把我们唤醒。要么有人先修改值,我们睡眠失败。

标签: 暂无
最后更新:2022年6月6日

sheep

think again

点赞
< 上一篇
下一篇 >

文章评论

取消回复

COPYRIGHT © 2021 heavensheep.xyz. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS