2016-09-20 41 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使用)。然后可以让异步写入器在正确的时间写入数据,此时连接的读取器仅获取可用/写入的字节数,而不是请求的总数。

相关问题