futex 设计成用户空间快速锁操作,由用户空间实现fastpath,以及内核提供锁竞争排队仲裁服务,由用户空间使用futex系统调用来实现slowpath。futex系统调用提供了三种配对的调用接口,满足不同使用场合的,分别为noraml futex,pi-futex,以及 requeue-pi。

futex的同步(锁)状态定义由用户空间去执行,futex系统调用并不需要理解用户空间是如何定义和使用这个地址对齐的4字节长的整型的futex,但是pi-futex除外,用户空间必须使用futex系统调用定义的锁规则。用户空间通过总线锁原子访问这个整型futex,进行状态的修改,上锁,解锁和锁竞争等。当用户空间发现futex进入了某种定义需要排队服务的状态时,用户空间就需要使用futex系统调用进行排队,待排队唤醒后再回到用户空间再次进行上锁等操作。当锁竞争时,每次的Lock和Unlock,都必需先后进行用户空间的锁操作和futex系统调用,并且两步并非原子性执行的,Lock和Unlock的执行过程可能会发生乱序。

这是我们希望的

task A futex in user futex queue in kerenl task B
  owned empty 1. own futex
1.try lock (尝试修改futex)   empty  
2.mark waiter (发现锁竞争,修改futex状态) owned -> waiters empty  
3.futex_wait 0 empty 2. unlock (修改futex,得到旧状态为waiters)
4.       enqueue 0 has waiter 3. futex_wake (发现有锁竞争)
5.       sleep and schedule 0 has waiter 4.           dequeue
  0 empty 5.           wakeup
6.       wokenup 0 empty  

7.try lock again (被唤醒后,并不知道还有没有其它任务在等待,

所以锁竞争状态来上锁,以确保自己unlock时进行slowpath,

进行内核检查有没有其它等待的任务)

0 -> waiters empty  
8. own futex waiters empty  
9. unlock (approach to slowpath) waiters    

但是总会发生我们不希望的情况,虽然总线锁原子操作使得Lock和Unlock的用户空间阶段的操作以Lock为先,让futex进行锁竞争状态,使得Lock和Unlock都要进行slowpath。然而,在它们各自调用futex系统调用时,执行futex_wait的cpu被中断了,futex_wake先于futex_wait执行了。futex_wake发现没有可唤醒的任务就离开了。然后迟到的futex_wait却一无所知,毅然排队等待在一个已经释放的锁。这样一来,如果这个锁将来不发生锁竞争,那么task A就不会被唤醒而被遗忘。

task A futex in user futex queue in kerenl task B
  owned empty 1. own futex
1.try lock (尝试修改futex)   empty  
2.mark waiter (发现锁竞争,修改futex状态) owned | waiters empty  
3.futex_wait 0 empty 2. unlock (修改futex,得到旧状态为owned | waiters)
      interupted 0 empty 3. futex_wake (发现有锁竞争)
      interupted 0 empty 4.           quit
4.       enqueue 0 has waiter  
5.       sleep and schedule 0 has waiter  
       
       
       

所以需要进行排队等待的futex系统调用,都要求将futex当前的副本作为参数传入,futex系统调用在执行排队之前都通过副本和用户空间的futex最新值进行对比,决定是否要返回用户空间,让用户空间重新判断。对于pi-futex的futex_lock_pi系统调用操作入口,并不需要用户空间传入当前futex的副本,是因为用户空间必须使用由futex系统调用对pi-futex的锁规则,futex_lock_pi 函数则以pi-futex的锁规则来判断pi-futex是否被释放。当一个用户空间的futex遵照futex.h对pi-futex锁状态规则,并使用futex系统调用的futex_lock_pi和futex_unlock_pi操作,这个futex就是一个pi-futex。

futex系统调用配对的操作入口:

1. normal futex:

static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time, u32 bitset)

static int futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)

2. pi-futex:

static int futex_lock_pi(u32 __user *uaddr, unsigned int flags, int detect, ktime_t *time, int trylock)

static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)

3. requeue-pi:

static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time, u32 bitset, u32 __user *uaddr2)

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

4. robust-futex:

SYSCALL_DEFINE3(get_robust_list, int, pid, struct robust_list_head __user * __user *, head_ptr, size_t __user *, len_ptr)

SYSCALL_DEFINE2(set_robust_list, struct robust_list_head __user *, head, size_t, len)

futex_wait 应用于non-pi futex,futex的规则由用户空间定义,要求用户空间将non-pi futex副本值传入来,过滤工作是由 futex_wait_setup子函数完成,再由 futex_wait_queue_me子函数进行non-pi futex的排队和睡眠等待。

futex_wait_requeue_pi 整合了对futex从non-pi到pi的requeue,以及non-pi到non-pi的requeue。但它首先是对non-pi的futex进行futex_wait,所以它和futex_wait 一样要求用户空间将non-pi futex副本值传入来。所以futex_wait_requeue_pi 如其名字一样,拆分成两个阶段,或者说组合了两种操作,futex_wait requeue_pi 。先进行futex_wait ,待被futex_requeue 唤醒后执行requeue_pi 。可以从代码看到futex_wait_requeue_pi 前半段和 futex_wait 代码流程是差不多的。

futex_lock_pi 应用于pi-futex,futex的规则由futex系统调用(头文件)定义,用户空间必须遵从规则来使用。由于规则是由内核定义的,并不要求用户空间传入一个futex当前副本,并且还会在内核中,在使用rt_mutex代理排队等待之前,进行 futex_lock_pi_atomic上锁的尝试,失败后才进入rt_mutex代理排队等待。待排队唤醒后,通过 fixup_owner 和 fixup_pi_state_owner 对用户空间的pi-futex进行上锁。这里有两点注意,rt_mutex的上锁规则是使用task_struct的指针标记,而pi-futex的上锁规则是使用pid(tid)号标记。另外pi-futex的排队也要注意,pi-futex虽然在rt_mutex代理上进行排队,但是还要像non-pi futex一样插入到futex_hash_bucket的链表中,为的不是排队,而是让后面进来的排队,可以在futex_hash_bucket中找出futex_queue,从而得到futex_pi_state(rt_mutex代理所在)。

futex的pi-support以及robust-support都跟task_struct偶合在一起。

robust futex 依赖的是进程(或线程)的task_struct结构体中的 robust_list 链表。futex的使用者(用户空间)通过(系统调用)将 用户空间维护的 robust_list 添加进内核中task_struct->robust_list。当进程(或线程)在退出的时候,内核可以遍历用户空间的robust_list链表,并对没有释放的robust futex进行recovery处理。

进程(或线程)在退出时,exit_mm -> mm_release 会调用futex的服务,exit_robust_list 去对用户空间使用的未释放的futex进行recovery处理 handle_futex_death。

对于pi-futex,它所使用的rtmutex代理也是会被恢复的,但不必经过robust_list,mm_release 会调用futex的服务 exit_pi_state_list 进行恢复处理。

对于pthread,当使用pthread_create创建线程时,同时会调用系统调用set_robust_list将内核的task_strust->robust_list,与用户空间的pthread维护的robust_list关联起来。

linux 内核的各种futex的更多相关文章

  1. linux 内核的futex pi-support,即pi-futex使用rt_mutex委托

    futex的pi-support,也就是为futex添加pi算法解决优先级逆转的能力,使用pi-support的futex又称为pi-futex.在linux内核的同步机制中,有一个pi算法的成例,就 ...

  2. linux内核级同步机制--futex

    在面试中关于多线程同步,你必须要思考的问题 一文中,我们知道glibc的pthread_cond_timedwait底层是用linux futex机制实现的. 理想的同步机制应该是没有锁冲突时在用户态 ...

  3. linux 内核的futex

    futex是linux内核为用户空间实现锁等同步机制而设计的同步排队(队列queueing)服务.在futex.c的注释中,futex起源于"Fast Userspace Mutex&quo ...

  4. 用Qemu模拟vexpress-a9 (三)--- 实现用u-boot引导Linux内核

    环境介绍 Win7 64 + Vmware 11 + ubuntu14.04 32 u-boot 版本:u-boot-2015-04 Linux kernel版本:linux-3.16.y busyb ...

  5. linux内核数据结构学习总结

    目录 . 进程相关数据结构 ) struct task_struct ) struct cred ) struct pid_link ) struct pid ) struct signal_stru ...

  6. 戴文的Linux内核专题:07内核配置(3)

    转自Linux中国 OK,我们还继续配置内核.还有更多功能等待着去配置. 下一个问题(Enable ELF core dumps (ELF_CORE))询问的是内核是否可以生成内核转储文件.这会使内核 ...

  7. linux内核编程笔记【原创】

    以下为本人学习笔记,如有转载请注明出处,谢谢 DEFINE_MUTEX(buzzer_mutex); mutex_lock(&buzzer_mutex); mutex_unlock(& ...

  8. Linux 内核综述

    一.什么是Linux内核: 内核->操作系统中最重要的部分,内核将在系统引导时被装载进RAM,其中包含了很多关键的例程,以操作系统.内核是OS最为关键的部分,人们常将OS(操作系统)与内核等同. ...

  9. 基于tiny4412的Linux内核移植(支持device tree)(三)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

随机推荐

  1. [UWP]了解模板化控件(1):基础知识

    1.概述 UWP允许开发者通过两种方式创建自定义的控件:UserControl和TemplatedControl(模板化控件).这个主题主要讲述如何创建和理解模板化控件,目标是能理解模板化控件常见的知 ...

  2. 车大棒浅谈jQuery源码(一)

    背景 因为最近辞职找工作,投了许多家公司.结果简历要么石沉大海,一点音讯都没有,要么就是邮件回复说不匹配.后面加了一些QQ群,才发现原来我工作经验年限太少了.现在深圳都是3经验起步,北京据说更加恐怖. ...

  3. JS和Flash(AS)相互调用

    <!DOCTYPE html> <html> <head> <title>swf</title> <meta charset=&quo ...

  4. 【原】Linux设备网络硬件管理

    遇到网络问题时候,一般情况下,我们第一反应是查找软件方面问题,但排查之后,软件没有问题的时候,我们就需要排查硬件方面工作是否正常. 我们可能需要查询网卡设备本身的状态,查询网卡是否有数据包发送接收: ...

  5. Laravel Session 遇到的坑

    这两天遇到了一个很奇怪的问题,更新session ,session的值不变.经过一番追查,终于找到问题,并搞明白了原理.写这篇博客记录下. 框架版本 Laravel 5.4 问题 先来描述下问题,我在 ...

  6. insmod: can't insert 'led.ko': invalid module format详细解释

    insmod: can't insert 'led.ko': invalid module format 之前在Imx257学习版固件编写的驱动想直接移植imx257核心板的开发板上.以为2个板子的源 ...

  7. 自适应滤波:最小均方误差滤波器(LMS、NLMS)

    作者:桂. 时间:2017-04-02  08:08:31 链接:http://www.cnblogs.com/xingshansi/p/6658203.html 声明:欢迎被转载,不过记得注明出处哦 ...

  8. 【Electron】Electron开发入门(八):自定义electron框架外壳(shell)的菜单(Menu)

    1.自定义electron框架外壳(shell)的菜单(Menu) electron的main.js里代码: const Menu = require('electron').Menu; var te ...

  9. H5_background-clip(css3——裁剪)

    利用background-clip实现此效果 在body里面只需要写:<div class="box"></div> 在样式里面写上: .box{ widt ...

  10. 1163: 零起点学算法70——Yes,I can!

    1163: 零起点学算法70--Yes,I can! Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: ...