zoukankan      html  css  js  c++  java
  • linux 内核的futex

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

  • 相关阅读:
    JAVA_SE基础——35.static修饰成员函数
    Mybatis环境搭建
    搭建Mybatis环境遇到的问题
    Windows10系统下,彻底删除卸载MySQL
    StringBuffer类append方法的用法和用途
    Listener监听器
    Filter过滤器
    JSTL
    EL表达式
    JSP
  • 原文地址:https://www.cnblogs.com/bbqzsl/p/6808176.html
Copyright © 2011-2022 走看看