zoukankan      html  css  js  c++  java
  • futex的设计与实现

    介绍

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

    历史

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

    futex的设计思想

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

    实现

    // 在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://www.jianshu.com/p/d17a6152740c
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
  • 相关阅读:
    foreach和each
    one
    存储
    动态添加
    百度描点
    php环境配置
    图文并茂
    css实现鼠标移上去变大,旋转,转别人的额
    vagrant box打包前的准备
    VirtualBox压缩打包
  • 原文地址:https://www.cnblogs.com/rinack/p/10836771.html
Copyright © 2011-2022 走看看