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 要移植的内核版本 ...
随机推荐
- @ModelAttribute--SpringMVC 注解系列文章(二)
概述 在日常的开发工作中,更新操作是经常会涉及到的一个功能,但是对于一条记录的更新,往往只会更新一部分的信息,而不会整条记录都更新,比如修改用户年龄的时候,用户密码是不需要修改的,针对这种情况,处理的 ...
- ie旋转滤镜Matrix
旋转一个元素算是一个比较常见的需求了吧,在支持CSS3的浏览器中可以使用transform很容易地实现,这里有介绍:http://www.css88.com/archives/2168,这里有演示ht ...
- vmware克隆之后网卡起不来的问题
问题: 克隆一台主机之后,改主机的网卡起不来,只有一个本地的回环地址网卡. 使用如下的命令都无效. /etc/init.d/network restart ifup eth0 原因: 这一vmware ...
- flex盒子里面元素linehight对高度的影响
那天,高高兴兴的测试flex属性.大家都知道,当父盒子display:flex的时候,子盒子即使宽高和大于父盒子,也会由于flex的特性进行收缩:然而,在给父盒子加了flex-direction:co ...
- table切换
// 自己加载正确路径的jQ <!doctype html> <html><head><meta charset="utf-8">& ...
- git常见操作和常见错误
最近写了个博客demo,在上传至github时,居然报错了,刚开始学习代码上传,免不了遇到一些问题,报错信息如下: fatal: remote origin already exists. (致命错误 ...
- .NET Core 2.0及.NET Standard 2.0
.NET Core 2.0的发布时间,.NET Core 2.0预览版及.NET Standard 2.0 Preview大概在5月中旬或下旬发布. .NET Core 2.0正式版本发布时间大约在Q ...
- POI框架实现创建Excel表、添加数据、读取数据
public class TestPOI2Excel {//创建2003版本Excel用此方法 @Test public void testWrite03Excel() throws Exceptio ...
- 你说你精通CSS,真的吗?
以前做项目的时候,学习了HTML和CSS,感觉这两个比较简单,在W3school里学习了一下之后,就觉得自己已经没问题了.可是,真正要做一个好看的页面,我还是要写好久.其实,对于CSS,我并没有像我以 ...
- File类遍历目录及文件
1. 构造函数 File(String args0)//使用一个表示文件或目录的路径的字符串创建一个File对象 File(URL args0)//使用一个URL对象创建File对象 File(Fil ...