2016-09-20 11 views
5

私はLinuxで短い読み込みを生成する方法を探していますので、それらの周囲の処理コードをユニットテストすることができます。linuxで短い読み込みを強制する方法を探しています

私は、下位レベルでファイルシステム内のファイルから読み込むためにpread/pread64を呼び出すいくつかの方法があります。これらは、短い読み込みが発生する状況(読み込まれたバイト数が要求された数よりも少ない)に対処するように設計されています。

短い読み取りが(ネットワーク上のファイルシステムを介して)発生する状況があります。

理想的には、Nバイトを読み取ることができるファイルを作成し、次にMバイトの短い読み取りが行われ、その後に通常の読み取りが予想どおりに行われるようになります。これにより、単体テストがファイル/ファイルシステムを指すことができます。

ありがとうございます!

+2

最も簡単なのか、最も柔軟少なくとも、おそらく 'はmkfifo()'や 'mknodを()'名前付きパイプを作成することです。 – Will

+0

通常、単に関連する 'read()'バリアントを呼び出してフルサイズを返す 'cover'関数を呼び出しますが、必要に応じて短い量を返すように設定できます。 'ssize_t tst_read(void * buffer、size_t size、int fd){ssize_t nbytes =読み出し(バッファ、サイズ、fd); if(...適切なテスト条件...)nbytes - = 13; nbytesを返す。 } '。リンスし、テストする必要がある読み込みのような機能ごとに繰り返します。 @JonathanLefflerが提案されているよう –

+0

ええとが、ええ、あなただけの読み取りをカプセル化することができるかどうか*その*はもちろんの最高:) – Will

答えて

2

傍受したいライブラリ呼び出しがわかっている場合は、LD_PRELOADでロードされた共有オブジェクトを呼び出して介入できます。

shortread.c:

#include <sys/types.h> 
#include <dlfcn.h> 

#define MAX_FDS 1024 

static int short_read_array[ MAX_FDS ]; 

// #define these to match your system's values 
// (need to be really careful with header files since 
// getting open() declared would make things very 
// difficult - just try this with open(const char *, int, ...); 
// declared to see what I mean...) 
#define O_RDONLY 0 
#define O_WRONLY 1 
#define O_RDWR 2 

// note that the mode bits for read/write are 
// not a bitwise-or - they are distinct values 
#define MODE_BITS 3 

// it's much easier to *NOT* even deal with the 
// fact that open() is a varargs function 
// but that means probably having to do some 
// typedef's and #defines to get this to compile 

// typedef some function points to make things easier 
typedef int (*open_ptr_t)(const char *name, int flags, mode_t mode); 
typedef ssize_t (*read_ptr_t)(int fd, void *buf, size_t bytes); 
typedef int (*close_ptr_t)(int fd); 

// function points to the real IO library calls 
static open_ptr_t real_open = NULL; 
static read_ptr_t real_read = NULL; 
static close_ptr_t real_close = NULL; 

// this will return non-zero if 'filename' is a file 
// to cause short reads on 
static int shortReadsOnFd(const char *filename) 
{ 
    // add logic here based on the file name to 
    // return non-zero if you want to do 
    // short reads on this file 
    // 
    // return(1); 
    return(0); 
} 

// interpose on open() 
int open(const char *filename, int flags, mode_t mode) 
{ 
    static pthread_mutex_t open_mutex = PTHREAD_MUTEX_INITIALIZER; 
    int fd; 

    pthread_mutex_lock(&open_mutex); 
    if (NULL == real_open) 
    { 
     real_open = dlsym(RTLD_NEXT, "open"); 
    } 
    pthread_mutex_unlock(&open_mutex); 

    fd = real_open(filename, flags, mode); 
    if ((-1 == fd) || (fd >= MAX_FDS)) 
    { 
     return(fd); 
    } 

    int mode_bits = flags & MODE_BITS; 

    // if the file can be read from, check if this is a file 
    // to do short reads on 
    if ((O_RDONLY == mode_bits) || (O_RDWR == mode_bits)) 
    { 
     short_read_array[ fd ] = shortReadsOnFd(filename); 
    } 

    return(fd); 
} 

ssize_t read(int fd, void *buffer, size_t bytes) 
{ 
    static pthread_mutex_t read_mutex = PTHREAD_MUTEX_INITIALIZER; 

    if ((fd < MAX_FDS) && (short_read_array[ fd ])) 
    { 
     // read less bytes than the caller asked for 
     bytes /= 2; 
     if (0 == bytes) 
     { 
      bytes = 1; 
     } 
    } 

    pthread_mutex_lock(&read_mutex); 
    if (NULL == real_read) 
    { 
     real_read = dlsym(RTLD_NEXT, "read"); 
    } 
    pthread_mutex_unlock(&read_mutex); 

    return(real_read(fd, buffer, bytes)); 
} 

int close(int fd) 
{ 
    static pthread_mutex_t close_mutex = PTHREAD_MUTEX_INITIALIZER; 

    pthread_mutex_lock(&close_mutex); 
    if (NULL == real_close) 
    { 
     real_close = dlsym(RTLD_NEXT, "close"); 
    } 
    pthread_mutex_unlock(&close_lock); 

    if (fd < MAX_FDS) 
    { 
     short_read_array[ fd ] = 0; 
    } 

    return(real_close(fd)); 
} 

コンパイルのようなもので:次に

gcc -shared [-m32|-m64] shortread.c -o libshortread.so 

export LD_PRELOAD=/path/to/libshortread.so 

は、LD_PRELOADと非常に注意してください - プロセスツリーのすべてのプロセスライブラリをロードするよう強制されます。 64ビット・ライブラリをロードする必要がある場合、32ビット・プロセスは実行されません。また、64ビット・プロセスは32ビット・ライブラリのロードを強制されます。上記のソースに、環境変数LD_PRELOADを削除するinit関数を追加することができます(または何かに無害に設定する)。

open()O_DIRECTフラグを使用するアプリケーションがある場合は、おそらく注意が必要です。読み込まれるバイト数を変更すると、ページ・サイズのIO操作だけがサポートされる可能性があるため、一部のLinuxファイル・システムおよび/または実装では、直接IOを破損する可能性があります。

このコードでは、read()しか処理しません。 creat()も処理する必要があります。またpread()readat()aio_read()、およびlio_listio()(それは私が現時点で思い出すことのできないいくつかの他のもの)でもそうは思わない。また、大きなファイルを処理する32ビットプロセスには注意してください。私はそれらを扱って以来、しばらくしていますが、それは私が思い出したように醜いことができます。

もう一つの注意点は、open()read()ライブラリコールを呼び出すことはできませんし、直接関連するシステムコールを発行することができるようにfopen()fread()として呼び出しです。その場合、これらの呼び出しの動作を簡単に変更することはできません。 fgets()などのデータを読み取ることができるSTDIOベースの呼び出しのファミリ全体に介入することは、何かを壊すことなく実行することは非常に難しいことです。

を知っている場合、アプリケーションはシングルスレッドであるため、ミューテックスを削除できます。

+0

ありがとうアンドリュー!驚くほど詳細な説明と解決策。 – CoreyP

1

最後に私はmkfifo()を使って解決策を行った。

私はそれにライターを接続(およびJavaから使用するJNIライブラリでそれを包む終わる)名前付きパイプを作成します。非同期ライターは、正しい時刻にデータを書き込むように指示することができます。この時点で、接続されたリーダーは要求された合計数ではなく、使用可能/書き込まれたバイト数だけを取得します。

関連する問題