eventfdを使ってみた

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

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*