我正在寻找一种在linux中产生短读取的方法,以便我可以对它们周围的处理代码进行单元测试。寻找一种方法来强制在linux中读短文
我有很多方法,在较低的层次上调用pread/pread64从文件系统中的文件读取。这些设计用于处理发生短读取的情况(读取的字节数小于请求的数量)。
我看过发生短读取的情况(跨网络文件系统)。
理想情况下,我将能够创建一个文件,允许读取N个字节,然后短时间读取M个字节,然后按预期正常读取。这将允许单元测试指向文件/文件系统。
谢谢!
我正在寻找一种在linux中产生短读取的方法,以便我可以对它们周围的处理代码进行单元测试。寻找一种方法来强制在linux中读短文
我有很多方法,在较低的层次上调用pread/pread64从文件系统中的文件读取。这些设计用于处理发生短读取的情况(读取的字节数小于请求的数量)。
我看过发生短读取的情况(跨网络文件系统)。
理想情况下,我将能够创建一个文件,允许读取N个字节,然后短时间读取M个字节,然后按预期正常读取。这将允许单元测试指向文件/文件系统。
谢谢!
如果您知道要截获的图书馆电话号码,则可以通过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的整个系列调用可能是一件非常困难的事情,不会破坏事情。
如果你知道你的应用程序是单线程的,你可以删除互斥锁。
谢谢安德鲁!一个惊人的详细描述和解决方案。 – CoreyP
最后我去了一个解决方案,使用mkfifo()
。
我创建了命名管道,然后连接一个写入器(最终将它封装在一个JNI库中以便从Java使用)。然后可以让异步写入器在正确的时间写入数据,此时连接的读取器仅获取可用/写入的字节数,而不是请求的总数。
最简单,或者至少最灵活的,可能是用'mkfifo()'或'mknod()'创建一个命名管道。 – Will
调用通常简单地调用相关的read()变体并返回完整大小的'cover'函数,但可以根据需要配置它以返回一小部分。 'ssize_t tst_read(void * buffer,size_t size,int fd){ssize_t nbytes = read(buffer,size,fd);如果(...适当的测试条件...)nbytes - = 13;返回nbytes; }'。冲洗并重复每个需要测试的阅读式功能。 –
嗯,是的,如果你可以只是封装阅读调用本身作为@JonathanLeffler建议,* *将是最好的当然:) – Will