介绍

futex(快速用户空间互斥)是Linux的一个基础组件,可以用来构建各种更高级别的同步机制,比如锁或者信号量等等,POSIX信号量就是基于futex构建的。大多数时候编写应用程序并不需要直接使用futex的,一般用基于它所实现的系统库就够了。

历史

传统的SystemV IPC(进程间通信)进程间同步机制都是通过内核对象来实现的,以semaphore为例,当进程间要同步的时候,必须通过系统调用semop(2)进入内核进行PV操作。系统调用的缺点是开销很大,需要从用户模式切换到内核模式,保存寄存器状态,从用户堆栈切换到内核堆栈,等等,通常要消耗上百条指令。事实上,有一部分系统调用是可以避免的,因为现实中很多同步操作进行的时候根本不存在竞争,即某个进程从持有旗语直至释放信号的这段时间内,常常没有其它进程对同一信号有需求,在这种情况下,内核的参与本来是不必要的,可是在传统机制下,持有旗语必须先调用执行semop(2)进入内核去看看有没有人和它竞争,释放信号量也必须调用执行semop(2)进入内核去看看有没有人在等待同一信号,这些不必要的系统调用造成了大量的性能损耗

futex的设计思想

futex的解决思路是:在无竞争的情况下操作完全在用户空间进行,不需要系统调用,仅在发生竞争的时候进入内核去完成相应的处理(等待或者唤醒)。所以说,futex是一种用户模式和内核模式混合的同步机制,需要两种模式合作才能完成,用户空间,而不是内核对象,futex的代码也分为用户模式和内核模式两部分,无竞争的情况下在用户模式下,发生竞争时则通过sys_futex系统调用进入内核模式进行处理

实现

  1. // 在uaddr指向的这个锁变量上挂起等待(仅当*uaddr==val时)
  2. int futex_wait(int *uaddr, int val);
  3. // 唤醒n个在uaddr指向的锁变量上挂起等待的进程
  4. int futex_wake(int *uaddr, int n);
  1. /*
  2. * This sample show how to use futex betwen two process, and use system v
  3. * shared memory to store data
  4. */
  5.  
  6. #include <unistd.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <sys/ipc.h>
  11. #include <sys/mman.h>
  12. #include <sys/types.h>
  13. #include <sys/syscall.h>
  14. #include <sys/wait.h>
  15. #include <sys/stat.h>
  16. #include <fcntl.h>
  17. #include <errno.h>
  18.  
  19. #if __GLIBC_PREREQ(2, 3)
  20. #if defined FUTEX_WAIT || defined FUTEX_WAKE
  21. #include <linux/futex.h>
  22. #else
  23. #define FUTEX_WAIT 0
  24. #define FUTEX_WAKE 1
  25. #endif
  26.  
  27. #ifndef __NR_futex
  28. #define __NR_futex 202
  29. #endif
  30. #endif
  31.  
  32. #define FILE_MODE (S_IRUSR | S_IWUSR)
  33.  
  34. const char shmfile[] = "/tmp";
  35. const int size = 100;
  36.  
  37. struct namelist
  38. {
  39. int id;
  40. char name[20];
  41. };
  42.  
  43. int
  44. main(void)
  45. {
  46. int fd, pid, status;
  47. int *ptr;
  48. struct stat stat;
  49.  
  50. // create a Posix shared memory
  51. int flags = O_RDWR | O_CREAT;
  52. fd = shm_open(shmfile, flags, FILE_MODE);
  53. if (fd < 0)
  54. {
  55. printf("shm_open failed, errormsg=%s errno=%d", strerror(errno), errno);
  56. return 0;
  57. }
  58. ftruncate(fd, size);
  59. ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  60.  
  61. pid = fork();
  62. if (pid == 0) { // child process
  63. sleep(5);
  64. printf("Child %d: start/n", getpid());
  65.  
  66. fd = shm_open(shmfile, flags, FILE_MODE);
  67. fstat(fd, &stat);
  68. ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  69. close(fd);
  70. struct namelist tmp;
  71.  
  72. // store total num in ptr[0];
  73. *ptr = 3;
  74.  
  75. namelist *cur = (namelist *)(ptr+1);
  76.  
  77. // store items
  78. tmp.id = 1;
  79. strcpy(tmp.name, "Nellson");
  80. *cur++ = tmp;
  81. tmp.id = 2;
  82. strcpy(tmp.name, "Daisy");
  83. *cur++ = tmp;
  84. tmp.id = 3;
  85. strcpy(tmp.name, "Robbie");
  86. *cur++ = tmp;
  87.  
  88. printf("wake up parent/n");
  89. syscall(__NR_futex ,ptr, FUTEX_WAKE, 1, NULL );
  90.  
  91. exit(0);
  92. } else{ // parent process
  93. printf("parent start waiting/n");
  94. syscall(__NR_futex , ptr, FUTEX_WAIT, *(int *)ptr, NULL );
  95. printf("parent end waiting/n");
  96.  
  97. struct namelist tmp;
  98.  
  99. int total = *ptr;
  100. printf("/nThere is %d item in the shm/n", total);
  101.  
  102. ptr++;
  103. namelist *cur = (namelist *)ptr;
  104.  
  105. for (int i = 0; i< total; i++) {
  106. tmp = *cur;
  107. printf("%d: %s/n", tmp.id, tmp.name);
  108. cur++;
  109. }
  110.  
  111. printf("/n");
  112. waitpid(pid, &status, 0);
  113. }
  114.  
  115. // remvoe a Posix shared memory from system
  116. printf("Parent %d get child status:%d/n", getpid(), status);
  117. return 0;
  118. }

上层应用

互斥锁pthread_mutex_t的实现原理

  1. // pthread_mutex_lock:
  2. atomic_dec(pthread_mutex_t.value);
  3. if (pthread_mutex_t.value!=0)
  4. futex(WAIT)
  5. else
  6. success
  7.  
  8. // pthread_mutex_unlock:
  9. atomic_inc(pthread_mutex_t.value);
  10. if(pthread_mutex_t.value!=1)
  11. futex(WAKEUP)
  12. else
  13. success

信号量sem_t的实现原理

  1. sem_wait(sem_t *sem)
  2. {
  3. for (;;) {
  4. if (atomic_decrement_if_positive(sem->count))
  5. break;
  6. futex_wait(&sem->count, 0)
  7. }
  8. }
  9.  
  10. sem_post(sem_t *sem)
  11. {
  12. n = atomic_increment(sem->count);
  13. // Pass the new value of sem->count
  14. futex_wake(&sem->count, n + 1);
  15. }

论文
参考一
参考二

作者:滩主
链接:https://www.jianshu.com/p/d17a6152740c
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

futex的设计与实现的更多相关文章

  1. [转载] Linux Futex的设计与实现

    Linux Futex的设计与实现 引子 在编译2.6内核的时候,你会在编译选项中看到[*] Enable futex support这一项,上网查,有的资料会告诉你"不选这个内核不一定能正 ...

  2. Linux Futex的设计与实现(转)

    引子在编译2.6内核的时候,你会在编译选项中看到[*] Enable futex support这一项,上网查,有的资料会告诉你"不选这个内核不一定能正确的运行使用glibc的程序" ...

  3. 【转】Linux Futex的设计与实现

    引子在编译2.6内核的时候,你会在编译选项中看到[*] Enable futex support这一项,上网查,有的资料会告诉你"不选这个内核不一定能正确的运行使用glibc的程序" ...

  4. 【转】cve2014-3153 漏洞之详细分析与利用

    背景学习: Linux Futex的设计与实现 使用者角度看bionic pthread_mutex和linux futex实现 By kernux TopSec α-lab 一 漏洞概述 这个漏洞是 ...

  5. linux 内核的futex

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

  6. linux 内核的futex - requeue 以及 requeue-pi

    futex为更好支持pthread_cond的实现(,最主要是broadcast),设计了requeue功能,并以futex系统调用提供操作接口,包括一对配对的操作 futex_wait_requeu ...

  7. linux 内核的各种futex

    futex 设计成用户空间快速锁操作,由用户空间实现fastpath,以及内核提供锁竞争排队仲裁服务,由用户空间使用futex系统调用来实现slowpath.futex系统调用提供了三种配对的调用接口 ...

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

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

  9. 转载:futex同步机制详解

    在编译2.6内核的时候,你会在编译选项中看到[*] Enable futex support这一项,上网查,有的资料会告诉你"不选这个内核不一定能正确的运行使用glibc的程序", ...

随机推荐

  1. docker发展历程

    docker发展历程 docker本身不是容器,它只是一个更加易用的前端管理器. 最早期的容器技术概念是用chroot来实现隔离,但是chroot只是提供了对进程文件目录虚拟化的功能,不能防止进程恶意 ...

  2. IAR使用ST-Link下载仿真

    修改Debugger->Setup->Driver 选择ST-LINK 修改 ST-LINK ->Interface选择SWD,CPU clock配置单片机CPU系统时钟. 修改De ...

  3. ASP.NET 中TextBox设置ReadOnly="true" 无法取到值的做法

    当 TextBox设置了ReadOnly="true" 后,要是在前台为控件添加了值,后台是取不到的,值为“空” 原理没想通,说不清楚微软是出于什么考虑的,https://www. ...

  4. 小顶堆第二弹-----堆降序排序(C语言非递归)

    现在po一下C语言版本的,留作以后接口使用. 1 #include <stdio.h> #include <stdlib.h> #define HEAP_SIZE 100 #d ...

  5. P4315 月下“毛景树”[树剖]

    题目描述 毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园. 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里. 爬啊爬~爬啊爬毛毛虫爬到了一颗小小的"毛景树&quo ...

  6. 32位JVM和64位JVM的最大堆内存分别是多数?32位和64位的JVM,int类型变量的长度是多数?

    理论上说上 32 位的 JVM 堆内存可以到达 2^32,即 4GB,但实际上会比这个小很多.不同操作系统之间不同,如 Windows 系统大约 1.5 GB,Solaris 大约 3GB.64 位 ...

  7. go语言-从控制套获取用户输入

    一.使用fmt.Scanln()--获取一行的输入 //案例:从控制台获取姓名,年龄,薪水,是否通过 package main import "fmt" func main() { ...

  8. 学习Spring-Data-Jpa(七)---JpaRepository

    之前我们学习的Repository都是Spring-Data为了兼容NoSQL而进行的一些抽象封装,从JpaRepository开始是对关系型数据库进行抽象封装.JpaRepository位于spri ...

  9. SSM整合Dubbo案例

    一.Consumer子类 MyController类 @Controller @RequestMapping("/login") public class MyController ...

  10. Error Permission denied when running brew cleanup

    Error Permission denied when running brew cleanup When I try to run `brew cleanup` I get: Warning: S ...