2016-09-20 15 views
5

Tôi đang tìm một phương pháp sản xuất đọc ngắn trong linux để tôi có thể kiểm tra đơn vị mã xử lý xung quanh chúng.Tìm kiếm một cách để buộc đọc ngắn trong linux

Tôi có một số phương pháp ở cấp độ thấp hơn gọi pread/pread64 để đọc từ tệp trong hệ thống tệp. Chúng được thiết kế để xử lý các tình huống trong đó đọc ngắn xảy ra (số byte được đọc nhỏ hơn số được yêu cầu).

Tôi đã thấy các trường hợp xảy ra lần đọc ngắn (qua các hệ thống tệp được nối mạng).

Lý tưởng nhất là tôi có thể tạo tệp cho phép đọc N byte và sau đó đọc ngắn M byte sẽ xảy ra, tiếp theo là đọc bình thường như mong đợi. Điều này sẽ cho phép kiểm tra đơn vị trỏ vào hệ thống tệp/tệp.

Cảm ơn!

+2

Dễ nhất, hoặc ít nhất là linh hoạt nhất, có thể là tạo một đường ống có tên với 'mkfifo()' hoặc 'mknod()'. – Will

+0

Gọi hàm 'cover' thường chỉ gọi biến 'read()' có liên quan và trả về kích thước đầy đủ, nhưng có thể được cấu hình để trả về một lượng ngắn theo yêu cầu. 'ssize_t tst_read (void * buffer, size_t size, int fd) {ssize_t nbytes = đọc (bộ đệm, kích thước, fd); if (… điều kiện thử nghiệm thích hợp…) nbytes - = 13; trả về nbyte; } '. Rửa sạch và lặp lại cho từng chức năng giống như đọc mà bạn cần kiểm tra. –

+0

Uh, vâng, nếu bạn chỉ có thể gói gọn chính cuộc gọi đọc như @JonathanLeffler đề xuất, * rằng * sẽ là tốt nhất tất nhiên :) – Will

Trả lời

2

Nếu bạn biết (các) cuộc gọi thư viện được thực hiện mà bạn muốn chặn, bạn có thể xen vào (các) cuộc gọi với một đối tượng dùng chung được tải qua 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)); 
} 

Compile với một cái gì đó như:

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

Sau đó:

export LD_PRELOAD=/path/to/libshortread.so 

Hãy rất cẩn thận với một LD_PRELOAD như vậy - tất cả các quá trình trong cây tiến trình sẽ bị buộc phải tải thư viện. Một quá trình 32-bit sẽ không chạy nếu nó phải tải một thư viện 64-bit, như là một quá trình 64-bit buộc phải thử tải một thư viện 32-bit. Bạn có thể thêm một hàm init vào nguồn ở trên để loại bỏ biến môi trường LD_PRELOAD (hoặc đặt nó thành một cái gì đó vô hại) để kiểm soát phần nào đó.

Bạn cũng có thể cần phải cẩn thận nếu bất kỳ ứng dụng nào sử dụng cờ O_DIRECT cho open(). Sửa đổi số byte được đọc có thể phá vỡ IO trực tiếp cho một số hệ thống tệp Linux và/hoặc triển khai, vì chỉ các thao tác IO kích thước trang có thể được hỗ trợ.

Và mã này chỉ xử lý read(). Bạn cũng có thể cần phải xử lý creat(). Ngoài ra pread(), readat(), aio_read()lio_listio(), (và thậm chí có thể một vài người khác mà tôi không thể nhớ tại thời điểm này) mặc dù đó là thừa nhận là không có khả năng. Và hãy cẩn thận với các quy trình 32 bit xử lý các tệp lớn. Đã một thời gian kể từ khi tôi giải quyết những vấn đề đó, nhưng điều đó có thể xấu đi khi tôi nhớ lại.

Cảnh báo khác là các cuộc gọi như fopen()fread() không được gọi các cuộc gọi thư viện open()read() và có thể phát trực tiếp cuộc gọi hệ thống liên quan. Trong trường hợp đó, bạn sẽ không thể sửa đổi hành vi của các cuộc gọi đó một cách dễ dàng. Interposing trên toàn bộ gia đình của STDIO dựa trên cuộc gọi có thể đọc dữ liệu như fgets() có thể là một điều rất khó để làm mà không phá vỡ mọi thứ.

Và nếu bạn biết (các) ứng dụng của bạn là đơn luồng, bạn có thể thả các mutex.

+0

Cảm ơn Andrew! Một mô tả và giải pháp đáng kinh ngạc. – CoreyP

1

Cuối cùng, tôi đã sử dụng giải pháp sử dụng mkfifo().

Tôi tạo đường ống có tên rồi kết nối một nhà văn với nó (và kết thúc gói nó trong một thư viện JNI được sử dụng từ Java). Nhà văn không đồng bộ sau đó có thể được yêu cầu ghi dữ liệu vào đúng thời điểm, tại thời điểm đó đầu đọc được kết nối chỉ nhận được các byte có sẵn/viết thay vì tổng số yêu cầu.

Các vấn đề liên quan