2010-03-25 49 views
5

我正在寫一個FUSE插件在C.我跟蹤的數據結構中的文件系統通過像結構:將數組存儲爲指針還是將其全部存儲在C struct中? (是:如何轉儲結構到磁盤時的一些字段是指針?)

typedef struct { 
    block_number_t inode; 
    filename_t filename; //char[SOME_SIZE] 
    some_other_field_t other_field; 
} fs_directory_table_item_t; 

顯然,我必須在某個時刻從(到)磁盤讀取(寫入)這些結構。我可以把該結構作爲一個字節序列,做這樣的事情:

read(disk_fd, directory_table_item, sizeof(fs_directory_table_item_t)); 

...只是不可能爲filename工作實際上是一個指針數組char

我真的希望避免編寫如下代碼:

read(disk_df, *directory_table_item.inode,  sizeof(block_number_t)); 
read(disk_df, directory_table_item.filename, sizeof(filename_t)); 
read(disk_df, *directory_table_item.other_field, sizeof(some_other_field_t)); 

...在代碼中的每個結構,因爲我不得不復制代碼和修改不下三個不同地點(定義,閱讀,寫作)。

任何DRYer,但仍然可維護的想法?

+0

出於好奇,哪個FUSE? – falstro 2010-03-25 09:21:59

+0

哦,並且'filename'是一個指針,或者當你的評論(和sizeof運算符的使用)暗示,一個數組? (在後一種情況下,你沒有問題..) – falstro 2010-03-25 09:23:19

+0

這個FUSE:http://fuse.sourceforge.net/是的,'filename'是一個固定長度的字符數組。 (至少在可預見的將來。) – badp 2010-03-25 09:27:04

回答

5

字符串的內存將成爲您的結構的一部分,即使在很多情況下數組類型爲促銷爲一個指針,存儲在該結構中的類型是數組,而不是指針。

typedef struct { 
    block_number_t inode; 
    filename_t filename; //char[SOME_SIZE] 
    some_other_field_t other_field; 
} fs_directory_table_item_t; 

所以你讀的語句:

read(disk_fd, directory_table_item, sizeof(fs_directory_table_item_t)); 

將工作和數據帶來的。

當讀取和寫入內存塊時,應考慮填充填充。填充是額外的,由編譯器添加到空的字段對齊關於相關邊界的數據;例如一個32字節的值通常應該從內存中的4字節邊界開始,以便處理器有效地讀取它。這通常沒什麼可擔心的,但是如果將結構持久化到磁盤上,如果使用其他設置重新編譯代碼,則可能會造成問題。通常會有某種#pragma指令禁用填充,我認爲它在MS Visual C++中被命名爲#pragma pack

+0

如果你只是簡單地改變你的編譯器,填充的問題也會出現。 – Baltasarq 2010-03-25 10:03:29

+0

謝謝清除我的困惑:) – badp 2010-03-25 11:47:01

+0

實際上,我們不應該像這樣使用「read()」,Read()使用無緩衝的I/O,當它在小塊數據上被頻繁調用時,效率可能非常低。 fread()是正確的調用 – user172818 2010-03-26 01:24:50

2

做到這一點的一種方法是製作靜態的數據描述結構的常量表,以便簡單的讀/寫引擎可以與它們一起工作。

您需要定義一個結構,它可以表示您需要知道讀取或寫入單個結構的單個字段的everthing。

typedef struct { 
    char * name; 
    size_t offset; 
    size_t size; 
    int format_as; 
    void* format_struct; // if format_as & IS_STRUCT, this is the structure type 
    } field_info_t 

enum { 
    AS_CHAR =1, 
    AS_SHORT, 
    AS_LONG, 
    // add other types here 
    AS_MASK = 0xFF, 

    // these flags can be OR'd with type to refine the behavior 
    IS_POINTER = 0x100, 
    IS_STRUCT = 0x200, 
    }; 

然後建立這些描述所有數據結構的表格。

#define FIELD_OFF(type, field) ((size_t)(LONG_PTR)&(((type *)0)->field)) 
#define FIELD_SIZE(type, field) (sizeof(((type *)0)->field)) 

static const field_info_t g_fs_directory_table_item_table[] = { 
    { "inode", 
     FIELD_OFF(fs_directory_table_item_t, inode), 
     FIELD_SIZE(fs_directory_table_item_t, inode), 
     AS_LONG, 
     NULL 
    }, 

    { "filename", 
     FIELD_OFF(fs_directory_table_item_t, filename), 
     sizeof(filename_t), 
     AS_CHAR | IS_POINTER, 
     NULL 
    }, 

    { "other_field", 
     FIELD_OFF(fs_directory_table_item_t, other_field), 
     FIELD_SIZE(fs_directory_table_item_t, other_field), 
     AS_STRUCT, 
     &some_other_field_table, 
    }, 
}; 

然後讀寫引擎採取一個指向結構,和一個指針表描述的結構和讀/寫的各種字段。

void ReadStructure(FILE * fh, void * pStruct, field_info_t * pFields, int num_fields) 
{ 
    // this is just a rough sketch of the code. 
    for (int ii = 0; ii < num_fields; ++ii) 
    { 
     int * field_size = pFields[ii].size; 
     char * pfield = (char*)pStruct + pFields[ii].offset; 
     if (pFields[ii].format_as & AS_POINTER) 
      pfield = *(char**)pfield; 

     switch (pFields[ii].format_as & AS_MASK) 
     { 
      case AS_CHAR: 
      .... 
     }   
    } 
} 
void WriteStructure(FILE * fh, void * pStruct, field_info_t * pFields, int num_fields); 

你還是最終不得不保持field_info_t陣列爲每個數據結構的,但一旦你擁有了它,你可以讀,寫,驗證和一組相當簡單的功能,漂亮地打印您的數據。