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 要移植的内核版本 ...
随机推荐
- JavaScript重新介绍
本文转载自 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/A_re-introduction_to_JavaScript 引言 为什么 ...
- LBPL--基于Asp.net、 quartz.net 快速开发定时服务的插件化项目
LBPL 这一个基于Asp.net. quartz.net 快速开发定时服务的插件化项目 由于在实际项目开发中需要做定时服务的操作,大体上可以理解为:需要动态化监控定时任务的调度系统. 为了实现快速开 ...
- Linux之split命令
split - split a file into pieces 切割一个文件至多片 参数: -a, --suffix-length=N 使用的后缀的长度,默认长度为2,例如'aa','ab' ...
- Python可视化学习(1):Matplotlib的配置
Matplotlib是一个优秀的可视化库,它提供了丰富的接口,让Python的可视化落地显得非常容易上手.本系列是本人学习python可视化的学习笔记,主要用于监督自己的学习进度,同时也希望和相关的博 ...
- ECMAScript6-let和const命令
▓▓▓▓▓▓ 大致介绍 ES6是下一代的JavaScript语言的标准,目标是让JavaScript可以用来编写大型的复杂程序,成为企业级开发语言,要查看浏览器对ES6的支持程度可以用阮一峰大佬写的工 ...
- Linux之例行(任务调度)
一. 例行命令之at 1.1 at 仅执行一次就从Linux任务中取消 1.2 at 指令可以将工作命令写入工作记录文件,工作记录文件默认存放在/var/spool/at目录内 1.3 at 工作 ...
- python实现mysql的读写分离及负载均衡
Oracle数据库有其公司开发的配套rac来实现负载均衡,目前已知的最大节点数能到128个,但是其带来的维护成本无疑是很高的,并且rac的稳定性也并不是特别理想,尤其是节点很多的时候. 但是,相对my ...
- 移动端ios 输入框fixed固定在底部 焦点时乱跳加遮盖问题的解决 转自zhangyunling 加个人项目解决方案
如果您有过移动端的开发经验,那么您是否碰到过这样的问题,一个页面有输入框,当这个输入框聚焦时,输入框在IOS下,被移动到手机屏幕的当中去了,而在Android端,有些浏览器的输入框,会被键盘盖住. 1 ...
- 02 nodejs命令参数(NodeJS基础入门)
声明:本文章可供有一定js基础的朋友参考nodejs入门,本文未讲解nodejs的安装,如有需要的同学可以加QQ3382260752找我,进行交流学习. 建议使用开发软件:webstorm或hbuil ...
- git commit -m与-am的区别
前面的话 使用git commit -am是不是就可以完全不使用git add命令呢?不是 理论 要了解git commit -m与git commit -am的区别,首先要明白它们的定义 字面解释的 ...