futex(快速用户空间互斥)是Linux的一个基础组件,能够用来构建各类更高级别的同步机制,好比锁或者信号量等等,POSIX信号量就是基于futex构建的。大多数时候编写应用程序并不须要直接使用futex的,通常用基于它所实现的系统库就够了。linux
传统的SystemV IPC(进程间通讯)进程间同步机制都是经过内核对象来实现的,以semaphore为例,当进程间要同步的时候,必须经过系统调用semop(2)进入内核进行PV操做。系统调用的缺点是开销很大,须要从用户模式切换到内核模式,保存寄存器状态,从用户堆栈切换到内核堆栈,等等,一般要消耗上百条指令。事实上,有一部分系统调用是能够避免的,由于现实中不少同步操做进行的时候根本不存在竞争,即某个进程从持有旗语直至释放信号的这段时间内,经常没有其它进程对同一信号有需求,在这种状况下,内核的参与原本是没必要要的,但是在传统机制下,持有旗语必须先调用执行semop(2)进入内核去看看有没有人和它竞争,释放信号量也必须调用执行semop(2)进入内核去看看有没有人在等待同一信号,这些没必要要的系统调用形成了大量的性能损耗 less
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); }