2010-08-26 33 views
4

我試圖使用聯合,以便我可以更新一個線程中的字段,然後在另一個線程中讀取allfields。在實際系統中,我有互斥體來確保一切安全。問題出在fieldB之前,我必須改變它,fieldB被聲明爲A和C字段。但是,由於第三方驅動程序的原因,fieldB必須與頁邊界進行分配。當我將字段B更改爲與valloc分配時,我遇到了問題。關於工會和堆分配內存的問題

問題: 1)有沒有辦法在頁面邊界上靜態聲明fieldB已被分配。基本上和valloc做同樣的事情,但是在堆棧上?

2)當字段B或任何字段在堆上分配時,是否可以進行聯合?不知道這是否合法。

這是我正在試驗的一個簡單的測試程序。除非你聲明fieldB類似於字段A和C,並且在公共方法中做出明顯改變,否則這是行不通的。

#include <iostream> 
#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 

class Test 
{ 
    public: 
     Test(void) 
     { 
     // field B must be alligned to page boundary 
     // Is there a way to do this on the stack??? 
     this->field.fieldB = (unsigned char*) valloc(10); 
     }; 

     //I know this is bad, this class is being treated like 
     //a global structure. Its self contained in another class. 
     unsigned char* PointerToFieldA(void) 
     { 
     return &this->field.fieldA[0]; 
     } 

     unsigned char* PointerToFieldB(void) 
     { 
     return this->field.fieldB; 
     } 

     unsigned char* PointerToFieldC(void) 
     { 
     return &this->field.fieldC[0]; 
     } 

     unsigned char* PointerToAllFields(void) 
     { 
     return &this->allFields[0]; 
     } 

    private: 
     // Is this union possible with field B being 
     // allocated on the heap? 
     union 
     { 
     struct 
     { 
      unsigned char fieldA[10]; 

      //This field has to be alligned to page boundary 
      //Is there way to be declared on the stack 
      unsigned char* fieldB; 
      unsigned char fieldC[10]; 
     } field; 

     unsigned char allFields[30]; 
     }; 
}; 


int main() 
{ 
    Test test; 

    strncpy((char*) test.PointerToFieldA(), "", 10); 
    strncpy((char*) test.PointerToFieldB(), "1234567890", 10); 
    strncpy((char*) test.PointerToFieldC(), "2345678901", 10); 

    char dummy[11]; 
    dummy[10] = '\0'; 

    strncpy(dummy, (char*) test.PointerToFieldA(), 10); 
    printf("%s\n", dummy); 

    strncpy(dummy, (char*) test.PointerToFieldB(), 10); 
    printf("%s\n", dummy); 

    strncpy(dummy, (char*) test.PointerToFieldC(), 10); 
    printf("%s\n", dummy); 

    char allFields[31]; 
    allFields[30] = '\0'; 
    strncpy(allFields, (char*) test.PointerToAllFields(), 30); 
    printf("%s\n", allFields); 

    return 0; 
} 
+0

根據您的來電valloc它看起來像你的意思是包含在'價值fieldB'必須頁面對齊。是對的嗎?無論如何,我能想到的任何解決方案都會浪費多達PAGE_SIZE - 1個字節,這是一堆浪費的堆棧。你爲什麼想把它分配到堆棧上? – torak 2010-08-26 21:34:00

+0

你是對的,fieldB需要頁面對齊。我想沒有必要在堆棧上分配。我只是好奇,如果有可能?當所有東西都分配在堆棧上時,它似乎工作。當我使用valloc時,我得到了一個可怕的崩潰。如果你有一個解決方案,一切都在堆上,我打開了。 – 2010-08-26 21:43:20

回答

2

我不認爲你可以聲明fieldB作爲指針,並獲得所需的行爲(假設我正確理解問題)。爲了讓工會在您使用它時有意義,您需要將它聲明爲聯合中的數組。

我有點好奇,如果有可能重載新操作符的類迫使一個特定的成員在頁面邊界。我很快就將超載的運營商聚集在一起做這件事。它會導致每次都分配一整個額外的頁面。它會找到該字段的偏移量,然後按該量調整地址。由於額外的內存被分配(並假設我正確地進行了數學計算),所以它是安全的。雖然非常醜陋。

它將分配偏移填充到類中的成員中,以便它知道要「釋放」指針以釋放它的數量。這真是可怕的代碼。作爲一個實驗,它看起來沒問題,但在生產代碼中不太好。

#define PAGE_SIZE 0x1000 

class test 
{ 
public: 
    int allocoffset; 
    void* operator new(size_t); 
    void operator delete(void*); 
    union 
     { 
     __declspec(align(4096)) struct 
     { 
      unsigned char fieldA[10]; 

      //This field has to be alligned to page boundary 
      //Is there way to be declared on the stack 
      unsigned char fieldB[10]; 
      unsigned char fieldC[10]; 
     } field; 

     unsigned char allFields[30]; 
     }; 
}; 

void* test::operator new(size_t size) 
{ 
    // Allocate an entire extra page so we can offset it by any amount 
    // less than the page size to ensure alignment of fieldB 
    unsigned char *p = (unsigned char*)malloc(sizeof(test) + PAGE_SIZE); 
    uintptr_t addr; 
    uintptr_t diff; 

    std::cout << "new " << (void*)p << std::endl; 

    // now offset the returned memory by the amount needed to align 
    // fieldB on a page boundary. 
    addr = (uintptr_t)p + (uintptr_t)(offsetof(test, field.fieldB)); 

    diff = PAGE_SIZE - (addr & (PAGE_SIZE - 1)); 

    p += diff; 

    ((test*)p)->allocoffset = diff; 

    return p; 
} 

void test::operator delete(void *p) 
{ 
    // offset by appropriate amount that we allocated it by 
    p = (void*)((unsigned char*)p - ((test*)p)->allocoffset); 
    std::cout << "delete " << p << std::endl; 
    free(p); 
} 

int main() 
{ 
    test *t; 

    t = new test; 

    std::cout << "allocation offset " << t->allocoffset << std::endl; 
    std::cout << "address of fieldB " << (void*)&t->field.fieldB << std::endl; 

    delete t; 
} 

下面是輸出示例:

new 00353FA0 
allocation offset 86 
address of fieldB 00355000 
delete 00353FA0 
1

我不這麼認爲 - 對準堆棧上是有點複雜,因爲你需要知道你在哪裏目前,分配足夠的字節消耗的內存「當前」頁面,然後分配數據。在堆棧上,這不是一種常見的操作(即,您不需要對齊堆棧中的任何東西)。

但是,某些編譯器具有將對齊結構體的編譯指示,MSVC具有'__declspec align',您可以在其中指定數據成員的對齊方式,編譯器將插入相應的字節數。

在堆上分配1個成員的聯合是可能的 - 聯合會像往常一樣包含所有的字段,但堆分配的只是一個指針。

最後,valloc已過時 - 您應該使用memalign或posix_memalign來代替。

+0

在上面的示例代碼中,allFields不會打印出所有字段A-C,因此它無法按照我的預期工作。我希望所有字段都能打印字段A-C。它只在獲取垃圾數據之前打印字段A.所以它似乎不適合我。 感謝您對使用memalign和posix_memalign的建議。直到現在,從來沒有需要頁面對齊的內存。 – 2010-08-26 22:21:09