futex
というわけで、非同期リクエスト-レスポンスについて考える、つもりだったのだけど、なんかfutexというのを見てたので、そこらへんの話。
futexというのは、Linux で pthread の conditional variable を実装するための機能 + αといったところだろうか。
簡単な使いかたは↓こんな感じ。
#include <time.h> #include <pthread.h> #include <stdio.h> #include <asm/unistd.h> #include <errno.h> #define FUTEX_WAIT 0 #define FUTEX_WAKE 1 static inline long futex( volatile void *a, int op, int v, struct timespec *t ) { int r; asm __volatile__ ( "int $0x80" : "=a"(r) : "a"(__NR_futex), "b"(a), "c"(op), "d"(v), "S"(t) : "memory" ); return r; } volatile int key; static void *start( void *p ) { int r; struct timespec ts; ts.tv_sec = 1; ts.tv_nsec = 0; key = 0; /* *1 */ r = futex( &key, FUTEX_WAIT, key, &ts ); printf("%d\n",r); if ( r == -ETIMEDOUT ) { puts("1 byou inaini nanika site!!!"); } return NULL; } int main() { pthread_t t; void *p; key = 0; pthread_create( &t, NULL, start, 0 ); getchar(); key++; futex( &key, FUTEX_WAKE, 1, 0 ); pthread_join( t, &p ); }
変数のアドレスをキーにして、待ったり、起こしたりできる。
特徴としては、変数のアドレス、というのが、ユーザ空間のアドレスではなくて、カーネル空間でのアドレスで見てる、という点。
実際にマップされてるメモリが同じなら同じキーとみなされるので、shmでアタッチしたアドレスとか、MAP_SHAREDでmmapしたアドレスとかを使えば、他のプロセスとのやりとりも同様に行える。
あと、ただの共有メモリ以上の色々をやってくれるようで、ほぼユーザ空間で動くセマフォが作れるっぽい。例えば、上のコードでいうと、
key = 0; /* #1 */ r = futex( &key, FUTEX_WAIT, key, &ts );
この #1 の部分で、WAKEされても大丈夫なようになってる(引数に渡した key と &key の指してる値が変わってないか見てる)。
上では使ってないけど、「何か変数に値を設定したのち、待機してるのを起こす」というのがアトミックにできたりとか、「cache line ping-pong(て何?)」が起こらないように、待機してるスレッドを一度に全部起こすのではなくて、一旦、別の待機キューに移してから、一個ずつ起動とかができるよう(FUTEX_CMP_REQUEUE)。そこらへんは実際試してないので、理解があってるかどうかは知らない。
詳細は、http://people.redhat.com/drepper/futex.pdf あたり参照。