Futex设计与实现

介绍

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

历史

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

futex设计思想

futex的解决思路是:在无竞争的情况下操作完全在user space进行,不需要系统调用,仅在发生竞争的时候进入内核去完成相应的处理(wait 或者 wake up)。所以说,futex是一种user mode和kernel mode混合的同步机制,需要两种模式合作才能完成,futex变量必须位于user space,而不是内核对象,futex的代码也分为user mode和kernel mode两部分,无竞争的情况下在user mode,发生竞争时则通过sys_futex系统调用进入kernel mode进行处理

实现

// 在uaddr指向的这个锁变量上挂起等待(仅当*uaddr==val时)
int futex_wait(int *uaddr, int val);
// 唤醒n个在uaddr指向的锁变量上挂起等待的进程
int futex_wake(int *uaddr, int n);
/* 
 * This sample show how to use futex betwen two process, and use system v  
 * shared memory to store data 
 */  
  
#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/ipc.h>  
#include <sys/mman.h>  
#include <sys/types.h>  
#include <sys/syscall.h>  
#include <sys/wait.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <errno.h>  
  
#if __GLIBC_PREREQ(2, 3)      
#if defined FUTEX_WAIT || defined FUTEX_WAKE   
#include <linux/futex.h>  
#else  
#define FUTEX_WAIT      0  
#define FUTEX_WAKE      1  
#endif  
  
#ifndef __NR_futex  
#define __NR_futex     202  
#endif  
#endif  
  
#define FILE_MODE (S_IRUSR | S_IWUSR)  
  
const char shmfile[] = "/tmp";  
const int size = 100;  
  
struct namelist   
{  
    int  id;   
    char name[20];  
};  
  
int   
main(void)  
{  
    int fd, pid, status;      
    int *ptr;  
    struct stat stat;  
          
    // create a Posix shared memory  
    int flags = O_RDWR | O_CREAT;  
    fd = shm_open(shmfile, flags, FILE_MODE);  
    if (fd < 0)  
    {  
        printf("shm_open failed, errormsg=%s errno=%d", strerror(errno), errno);  
        return 0;  
    }  
    ftruncate(fd, size);  
    ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);     
  
    pid = fork();  
    if (pid == 0) { // child process  
        sleep(5);  
        printf("Child %d: start/n", getpid());  
          
        fd = shm_open(shmfile, flags, FILE_MODE);  
        fstat(fd, &stat);         
        ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);     
        close(fd);  
        struct namelist tmp;  
  
        // store total num in ptr[0];  
        *ptr = 3;  
          
        namelist *cur = (namelist *)(ptr+1);  
  
        // store items  
        tmp.id = 1;  
        strcpy(tmp.name, "Nellson");  
        *cur++ = tmp;  
        tmp.id = 2;  
        strcpy(tmp.name, "Daisy");  
        *cur++ = tmp;  
        tmp.id = 3;  
        strcpy(tmp.name, "Robbie");  
        *cur++ = tmp;  
  
        printf("wake up parent/n");  
        syscall(__NR_futex ,ptr, FUTEX_WAKE, 1, NULL );  
  
        exit(0);  
    } else{ // parent process  
        printf("parent start waiting/n");  
        syscall(__NR_futex , ptr, FUTEX_WAIT, *(int *)ptr, NULL );  
        printf("parent end waiting/n");  
  
        struct namelist tmp;  
  
        int total = *ptr;  
        printf("/nThere is %d item in the shm/n", total);     
          
        ptr++;  
        namelist *cur = (namelist *)ptr;  
  
        for (int i = 0; i< total; i++) {  
            tmp = *cur;  
            printf("%d: %s/n", tmp.id, tmp.name);  
            cur++;  
        }  
  
        printf("/n");  
        waitpid(pid, &status, 0);  
    }  
  
    // remvoe a Posix shared memory from system  
    printf("Parent %d get child status:%d/n", getpid(), status);  
    return 0;  
}  

上层应用

互斥锁pthread_mutex_t的实现原理

// pthread_mutex_lock:
atomic_dec(pthread_mutex_t.value);
if (pthread_mutex_t.value!=0)
  futex(WAIT)
else
  success

// pthread_mutex_unlock:
atomic_inc(pthread_mutex_t.value);
if(pthread_mutex_t.value!=1)
futex(WAKEUP)
else
success

信号量sem_t的实现原理

sem_wait(sem_t *sem)
{
for (;;) {
   if (atomic_decrement_if_positive(sem->count))
       break;
   futex_wait(&sem->count, 0)
   }
}

sem_post(sem_t *sem)
{
   n = atomic_increment(sem->count);
   // Pass the new value of sem->count
   futex_wake(&sem->count, n + 1);
}

论文
参考一
参考二
https://github.com/torvalds/linux/blob/master/kernel/futex.c

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容