2016-09-20 3 views
5

나는 리눅스에서 짧은 읽기를 만드는 방법을 찾고있다. 그래서 그들 주위의 처리 코드를 단위 테스트 할 수있다.리눅스에서 짧은 읽기를 강제하는 방법을 찾고있다

파일 시스템 내에서 파일을 읽으려면 하위 레벨에서 pread/pread64를 호출하는 많은 메소드가 있습니다. 이들은 짧은 읽기가 발생하는 상황을 처리하도록 설계되었습니다 (읽은 바이트 수가 요청 된 수보다 적음).

짧은 읽기가 발생하는 상황을 보았습니다 (네트워크 파일 시스템에서).

이상적으로는 N 바이트를 읽을 수있는 파일을 만들고 M 바이트의 짧은 읽기가 발생하고 예상대로 정상 읽기가 가능한 파일을 만들 수 있습니다. 그러면 단위 테스트가 파일/파일 시스템을 가리킬 수 있습니다.

감사합니다.

+2

가장 쉽거나 가장 유연한 것은 아마도 mkfifo() 또는 mknod()로 명명 된 파이프를 만드는 것일 것입니다. – Will

+0

일반적으로 관련 'read()'변형을 호출하고 전체 크기를 반환하는 '표지'기능을 호출하지만 요청시 짧은 금액을 반환하도록 구성 할 수 있습니다. 'ssize_t tst_read (void * buffer, size_t size, int fd) {ssize_t nbytes = 읽기 (버퍼, 크기, fd); if (... 적절한 테스트 조건 ...) nbytes - = 13; return nbytes; }'. 테스트 할 필요가있는 각 읽기와 같은 기능에 대해 린스하고 반복하십시오. –

+0

어, 그래, 만약 당신이 그냥 @ JonathanLeffler 제시대로 * 읽기 호출 자체를 캡슐 수 있습니다 * 그 * 물론 최선의 것입니다 :) – Will

답변

2

가로 채기를 원하는 라이브러리 호출을 알고있는 경우 LD_PRELOAD을 통해로드 된 공유 개체를 사용하여 호출을 삽입 할 수 있습니다.

shortread.c : 다음

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

: 같은과

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

컴파일

export LD_PRELOAD=/path/to/libshortread.so 

같은 LD_PRELOAD에 특히 조심 - 프로세스 트리의 모든 프로세스 라이브러리를로드해야합니다. 64 비트 라이브러리를로드해야하는 경우 32 비트 프로세스가 실행되지 않으며 32 비트 라이브러리를로드하려고 시도하는 64 비트 프로세스도 실행되지 않습니다. 위의 소스에 LD_PRELOAD 환경 변수를 제거하는 init 함수를 추가 할 수 있습니다 (또는 무언가로 설정 함).

open()O_DIRECT 플래그를 사용하는 응용 프로그램이 있으면주의해야합니다. 읽기되는 바이트 수를 수정하면 페이지 크기 IO 조작 만 지원 될 수 있으므로 일부 Linux 파일 시스템 및/또는 구현에서는 직접 입출력이 중단 될 수 있습니다.

그리고이 코드는 read() 만 처리합니다. creat()을 처리해야 할 수도 있습니다. 또한 pread(), readat(), aio_read()lio_listio() (어쩌면 내가 지금은 기억할 수없는 몇 가지 다른 것들도 있습니다). 또한 대용량 파일을 처리하는 32 비트 프로세스에주의하십시오. 내가 그것들을 다루었으니 꽤 오랜 시간이 걸렸지 만, 그건 내가 기억하는 것처럼 추악해질 수있다.

또 다른주의해야 할 점은 open()read() 라이브러리 호출을 호출 할 수 있으며, 직접 관련 시스템 호출을 발행 할 수있다 등 fopen()fread()로 호출합니다. 이 경우 해당 통화의 동작을 쉽게 수정할 수 없습니다. fgets()과 같은 데이터를 읽을 수있는 STDIO 기반 호출 전체 제품군에 끼어 들기는 매우 어려운 일입니다.

을 알고 있으면 응용 프로그램이 단일 스레드이므로 뮤텍스를 삭제할 수 있습니다.

+0

감사합니다. Andrew! 놀라 울 정도로 자세한 설명과 솔루션. – CoreyP

1

결국 나는 mkfifo()을 사용하는 솔루션을 사용했다.

명명 된 파이프를 만든 다음 작성기를 연결하고 Java에서 사용할 JNI 라이브러리에 래핑합니다. 비동기 작가는 올바른 시간에 데이터를 쓰도록 지시받을 수 있습니다.이 시점에서 연결된 리더는 전체 요청 된 숫자가 아닌 사용 가능/기록 된 바이트 만 가져옵니다.

관련 문제