linuxカーネル2.6.22からeventfdという、
ファイルI/Oでイベントをやりとりできる仕組みが組み込まれました。
eventfdの考え方が、私の設計と近いものがあったので、
さっそく使ってみました。
感想としては、Windowsの同期オブジェクトのような使い方に応用できるので、
設計の幅が広がるかな、という感じ。
性能的にはちゃんと調べていないのでよくわかりませんが、
特別重くなったとか反応が鈍いとかはありません。
サンプルを掲載します。(エラー処理はいいかげんです。。)
動作環境は、VMWare上のUbuntu7.10を8にアップグレードしたもの。
linux kernel 2.6.24-19
gcc version 4.2.3
glibc 2.7-1
です。
glibc 2.8から正式にeventfdが使えるみたいですが、
カーネルには入っているので、プロトタイプ宣言をしてしまえば
普通に呼べました。
サンプルプログラムは3つのファイルすべてから”q”が読み込めたら終了する、
というeventfdの特徴を生かしきれていないものですが、ご勘弁を。
使い方)
mkfifoで、名前付きパイプを3つ作る。
$ mkfifo test1 test2 test3
コマンドを実行する。
$ ./myeventfd test1 test2 test3
別のターミナルから各パイプに文字列を入れる。
$ echo abc > test1
$ echo def > test2
$ echo ghi > test3
$ echo q > test1
$ echo q > test2
$ echo q > test3
※”q”が含まれると各パイプの読み込みが終了。
※すべてのパイプの読み込みが終了するとアプリケーションが終了。
コンパイル方法)
make myeventfd “LDFLAGS=-lpthread”
ソース)
致命的なバグなどがあればコメントいただきたいですが、
不具合でなにかトラブルがあっても責任は負えませんので、ご了承ください。
myeventfd.c
/* Copyright (C) 2008 bitmeister all right reserved. */ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <malloc.h> #include <errno.h> #include <pthread.h> #include <sys/select.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> /* definition evetnfd functions */ /* gcc version 4.2.3 (eventfd.h is not supported) */ int eventfd(unsigned int, int); typedef u_int64_t eventfd_t; int eventfd_read(int fd, eventfd_t *value); int eventfd_write(int fd, eventfd_t value); static eventfd_t myEventFd=0; /* thread function */ void *thread_func(void *param) { char *filename = (char *)param; int fd; fd_set rfds; int ret; fd = open(filename,O_RDONLY); if( fd == -1 ) { perror("open"); exit(-1); } while(1) { FD_ZERO(&rfds); FD_SET(fd,&rfds); ret = select( fd+1,&rfds,NULL,NULL,NULL ); if( ret > 0 ) { if( FD_ISSET(fd,&rfds) ) { char buffer[256]; ret = read(fd,buffer,sizeof(buffer)); if( ret < 0 ) { perror("read"); break; } else if( ret == 0 ) { close(fd); fd = open(filename,O_RDONLY); if( fd == -1 ) { perror("open"); exit(-1); } continue; } printf("%s",buffer); fflush(stdout); if( strchr(buffer,'q') != NULL ) { printf("read 'q'\n"); break; } } } else { perror("select"); break; } } eventfd_write(myEventFd,1); close(fd); return NULL; } int main(int argc, char *argv[]) { pthread_t myThreads[3]; char *fileNames[3]; int i; fd_set rfds; int ret; u_int64_t count = 0; if( argc != 4 ) { fprintf(stderr,"Usage:%s filename filename filename\n",argv[0]); return 0; } myEventFd = eventfd(0,0); for(i=0;i<3;i++) { fileNames[i] = malloc(sizeof(char)*strlen(argv[i+1])+1); strcpy(fileNames[i],argv[i+1]); pthread_create(&myThreads[i],NULL,thread_func,fileNames[i]); } while(count < 3) { FD_ZERO(&rfds); FD_SET(myEventFd,&rfds); ret = select(myEventFd+1,&rfds,NULL,NULL,NULL); if( FD_ISSET(myEventFd,&rfds) ) { u_int64_t value; eventfd_read(myEventFd,&value); printf("eventfd_read value=%lld\n",value); count += value; } } for(i=0;i<3;i++) { pthread_join(myThreads[i],NULL); free(fileNames[i]); } close(myEventFd); return 0; }