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