linux 内核的各种futex
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的更多相关文章
- linux 内核的futex pi-support,即pi-futex使用rt_mutex委托
futex的pi-support,也就是为futex添加pi算法解决优先级逆转的能力,使用pi-support的futex又称为pi-futex.在linux内核的同步机制中,有一个pi算法的成例,就 ...
- linux内核级同步机制--futex
在面试中关于多线程同步,你必须要思考的问题 一文中,我们知道glibc的pthread_cond_timedwait底层是用linux futex机制实现的. 理想的同步机制应该是没有锁冲突时在用户态 ...
- linux 内核的futex
futex是linux内核为用户空间实现锁等同步机制而设计的同步排队(队列queueing)服务.在futex.c的注释中,futex起源于"Fast Userspace Mutex&quo ...
- 用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 ...
- linux内核数据结构学习总结
目录 . 进程相关数据结构 ) struct task_struct ) struct cred ) struct pid_link ) struct pid ) struct signal_stru ...
- 戴文的Linux内核专题:07内核配置(3)
转自Linux中国 OK,我们还继续配置内核.还有更多功能等待着去配置. 下一个问题(Enable ELF core dumps (ELF_CORE))询问的是内核是否可以生成内核转储文件.这会使内核 ...
- linux内核编程笔记【原创】
以下为本人学习笔记,如有转载请注明出处,谢谢 DEFINE_MUTEX(buzzer_mutex); mutex_lock(&buzzer_mutex); mutex_unlock(& ...
- Linux 内核综述
一.什么是Linux内核: 内核->操作系统中最重要的部分,内核将在系统引导时被装载进RAM,其中包含了很多关键的例程,以操作系统.内核是OS最为关键的部分,人们常将OS(操作系统)与内核等同. ...
- 基于tiny4412的Linux内核移植(支持device tree)(三)
作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...
随机推荐
- [UWP]了解模板化控件(1):基础知识
1.概述 UWP允许开发者通过两种方式创建自定义的控件:UserControl和TemplatedControl(模板化控件).这个主题主要讲述如何创建和理解模板化控件,目标是能理解模板化控件常见的知 ...
- 车大棒浅谈jQuery源码(一)
背景 因为最近辞职找工作,投了许多家公司.结果简历要么石沉大海,一点音讯都没有,要么就是邮件回复说不匹配.后面加了一些QQ群,才发现原来我工作经验年限太少了.现在深圳都是3经验起步,北京据说更加恐怖. ...
- JS和Flash(AS)相互调用
<!DOCTYPE html> <html> <head> <title>swf</title> <meta charset=&quo ...
- 【原】Linux设备网络硬件管理
遇到网络问题时候,一般情况下,我们第一反应是查找软件方面问题,但排查之后,软件没有问题的时候,我们就需要排查硬件方面工作是否正常. 我们可能需要查询网卡设备本身的状态,查询网卡是否有数据包发送接收: ...
- Laravel Session 遇到的坑
这两天遇到了一个很奇怪的问题,更新session ,session的值不变.经过一番追查,终于找到问题,并搞明白了原理.写这篇博客记录下. 框架版本 Laravel 5.4 问题 先来描述下问题,我在 ...
- insmod: can't insert 'led.ko': invalid module format详细解释
insmod: can't insert 'led.ko': invalid module format 之前在Imx257学习版固件编写的驱动想直接移植imx257核心板的开发板上.以为2个板子的源 ...
- 自适应滤波:最小均方误差滤波器(LMS、NLMS)
作者:桂. 时间:2017-04-02 08:08:31 链接:http://www.cnblogs.com/xingshansi/p/6658203.html 声明:欢迎被转载,不过记得注明出处哦 ...
- 【Electron】Electron开发入门(八):自定义electron框架外壳(shell)的菜单(Menu)
1.自定义electron框架外壳(shell)的菜单(Menu) electron的main.js里代码: const Menu = require('electron').Menu; var te ...
- H5_background-clip(css3——裁剪)
利用background-clip实现此效果 在body里面只需要写:<div class="box"></div> 在样式里面写上: .box{ widt ...
- 1163: 零起点学算法70——Yes,I can!
1163: 零起点学算法70--Yes,I can! Time Limit: 1 Sec Memory Limit: 64 MB 64bit IO Format: %lldSubmitted: ...