futex的設計與實現

介紹

futex(快速用戶空間互斥)是Linux的一個基礎組件,能夠用來構建各類更高級別的同步機制,好比鎖或者信號量等等POSIX信號量就是基於futex構建的。大多數時候編寫應用程序並不須要直接使用futex的,通常用基於它所實現的系統庫就夠了linux

歷史

傳統的SystemV IPC(進程間通訊)進程間同步機制都是經過內核對象來實現的,以semaphore爲例,當進程間要同步的時候,必須經過系統調用semop(2)進入內核進行PV操做。系統調用的缺點是開銷很大,須要從用戶模式切換到內核模式,保存寄存器狀態,從用戶堆棧切換到內核堆棧,等等,一般要消耗上百條指令。事實上,有一部分系統調用是能夠避免的,由於現實中不少同步操做進行的時候根本不存在競爭,即某個進程從持有旗語直至釋放信號的這段時間內,經常沒有其它進程對同一信號有需求,在這種狀況下,內核的參與原本是沒必要要的,但是在傳統機制下,持有旗語必須先調用執行semop(2)進入內核去看看有沒有人和它競爭,釋放信號量也必須調用執行semop(2)進入內核去看看有沒有人在等待同一信號,這些沒必要要的系統調用形成了大量的性能損耗 less

futex的設計思想

futex的解決思路是:在無競爭的狀況下操做徹底在用戶空間進行,不須要系統調用,僅在發生競爭的時候進入內核去完成相應的處理(等待或者喚醒)。因此說,futex是一種用戶模式和內核模式混合的同步機制,須要兩種模式合做才能完成,用戶空間,而不是內核對象,futex的代碼也分爲用戶模式和內核模式兩部分,無競爭的狀況下在用戶模式下,發生競爭時則經過sys_futex系統調用進入內核模式進行處理post

實現

// 在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的實現原理atom

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);
}

論文
參考一
參考二spa

做者:灘主 連接:https://www.jianshu.com/p/d17a6152740c 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。
相關文章
相關標籤/搜索