libuvに関する覚書(3) : パイプ uv_pipe_t

関連投稿

libuvに関する覚書(1) : タイマー uv_timer_t
libuvに関する覚書(2) : ファイル監視 uv_fs_event_t
libuvに関する覚書(4) : スレッド uv_work_t, uv_async_t

デバイスファイルをオープンして、ストリームから流れてくるデータを随時ダンプするサンプルになる。uv_pipe_initで初期化したパイプハンドルとオープンしたファイルのデスクリプタを紐づけて、パイプを生成する(uv_pipe_open)そしてuv_read_startでストリームの読み込みを開始する。

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include <uv.h>

uv_loop_t *loop;
uv_pipe_t file_pipe;

int main(int argc, char **argv) {

  loop = NULL;
  loop = uv_default_loop();
  if( loop == NULL ){
    return 0;
  }
  // init pipe stdin stdout file
  uv_pipe_init(loop, &file_pipe, 0);

  uv_fs_t file_req;
  int fd = uv_fs_open(loop, &file_req, argv[1], O_RDONLY, 0, NULL);

  uv_pipe_open(&file_pipe, fd);
  uv_read_start((uv_stream_t*)&file_pipe, alloc_buffer, callback_read);

  // event start
  uv_run(loop, UV_RUN_DEFAULT);
  return 0;
}

uv_read_startで指定する2つのコールバック処理を実装する。
alloc_bufferは、システム側からしかるべきタイミングでパイプ用のバッファ作成をこのコールバックで実行される。以下の例では、suggested_sizeで指定されたサイズで、uv_buf_tのバッファを生成している。

コールバックcallback_readは、ストリームにデータが流れてきたときにコールされる。その時には流れてきたデータのバッファとそのサイズが同時に渡される。以下の例では、1バイトごとにダンプして、最後にバッファを削除している。

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
  printf("alloc_buffer, suggested_size=%zd\n", suggested_size);
  *buf = uv_buf_init((char*) malloc(suggested_size), suggested_size);
}

void callback_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf){
  printf("callback_read. nread=%zd\n", nread);
  
  if(nread > 0 && buf->base){
    for( int i = 0; i < nread; i++){
      printf( "%d ", buf->base[i]);
    }
    printf("\n");
    free( buf->base );
  }
}

以下では、/dev配下にあるデバイスファイルに対して実行してみた内容になる。ちなみに普通のファイルに対して、実行していたところエラーになった。

> clang -luv file_pipe.c -o fpipe
> sudo ./fpipe /dev/hidraw1
alloc_buffer, suggested_size=65536
callback_read. nread=49
1 0 0 0 0 0 128 127 128 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 239 22 255 206 0 0 51 111 119 0 0 254 1 236 1 142 1 234 1
: