2016-09-20 44 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 = read(buffer,size,fd);如果(...適當的測試條件...)nbytes - = 13;返回nbytes; }'。沖洗並重復每個需要測試的閱讀式功能。 –

+0

嗯,是的,如果你可以只是封裝閱讀調用本身作爲@JonathanLeffler建議,* *將是最好的當然:) – 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位庫。你可以在上面的源代碼中添加一個init函數來刪除LD_PRELOAD環境變量(或者將其設置爲無害)來控制這一點。

如果任何應用程序使用open()O_DIRECT標誌,您也可能需要小心。修改正在讀取的字節數可能會破壞某些Linux文件系統和/或實現的直接IO,因爲可能只支持頁面大小的IO操作。

此代碼僅處理read()。您可能還需要處理creat()。此外,還有pread(),readat(),aio_read()lio_listio()(可能還有其他一些我現在還不記得的東西),儘管這種做法不太可能。並且要注意處理大文件的32位進程。我已經處理這些事情已經有一段時間了,但是我記得那會變得很難看。

另一個需要注意的是,如fopen()fread()可能不調用open()read()庫調用和可以直接發佈相關的系統調用的調用。在這種情況下,您將無法輕鬆修改這些調用的行爲。插入可以讀取數據(例如fgets())的基於STDIO的整個系列調用可能是一件非常困難的事情,不會破壞事情。

如果你知道你的應用程序是單線程的,你可以刪除互斥鎖。

+0

謝謝安德魯!一個驚人的詳細描述和解決方案。 – CoreyP

1

最後我去了一個解決方案,使用mkfifo()

我創建了命名管道,然後連接一個寫入器(最終將它封裝在一個JNI庫中以便從Java使用)。然後可以讓異步寫入器在正確的時間寫入數據,此時連接的讀取器僅獲取可用/寫入的字節數,而不是請求的總數。

相關問題