关于联合和堆分配内存的问题

发布于 2024-09-16 08:01:30 字数 2370 浏览 7 评论 0 原文

我试图使用联合来更新一个线程中的字段,然后读取另一个线程中的所有字段。在实际系统中,我有互斥体来确保一切都是安全的。问题出在 fieldB 上,在我不得不更改它之前,fieldB 的声明方式与字段 A 和 C 类似。但是,由于第三方驱动程序,fieldB 必须与页边界对齐。当我将字段 B 更改为使用 valloc 分配时,我遇到了问题。

问题: 1)有没有办法静态声明 fieldB 在页​​面边界上对齐。基本上与 valloc 做同样的事情,但是在堆栈上?

2)当字段B或任何字段在堆上分配时是否可以进行联合?不确定这是否合法。

这是我正在尝试的一个简单的测试程序。除非您像字段 A 和 C 一样声明字段 B,并在公共方法中进行明显的更改,否则这不起作用。

#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(), "0123456789", 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;
}

I was trying to use a union to so I could update the fields in one thread and then read allfields in another thread. In the actual system, I have mutexes to make sure everything is safe. The problem is with fieldB, before I had to change it fieldB was declared like field A and C. However, due to a third party driver, fieldB must be alligned with page boundary. When I changed field B to be allocated with valloc, I run into problems.

Questions:
1) Is there a way to statically declare fieldB alligned on page boundary. Basically do the same thing as valloc, but on the stack?

2) Is it possible to do a union when field B, or any field is being allocated on the heap?. Not sure if that is even legal.

Here's a simple Test program I was experimenting with. This doesn't work unless you declare fieldB like field A and C, and make the obvious changes in the public methods.

#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(), "0123456789", 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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

对你而言 2024-09-23 08:01:30

我认为您不能将 fieldB 声明为指针并获得所需的行为(假设我正确理解了问题)。为了使联合在使用时有意义,您需要将其声明为联合中的数组。

我有点好奇是否可以重载该类的 new 运算符以强制特定成员位于页面边界上。我很快就将重载的运算符拼凑在一起来做到这一点。它会导致每次分配整个额外页面。它找到该字段所在位置的偏移量,然后按该量调整地址。由于分配了额外的内存(假设我计算正确),所以它是安全的。不过,非常丑陋。

它将分配偏移量填充到类中的成员中,以便它知道“取消偏移”指针以释放它的量。这真是一段可怕的代码。作为一个实验似乎还不错,但在生产代码中却不太好。

#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

I don't think you can declare fieldB as a pointer and get the desired behavior (assuming I am understanding the question correctly). For the union to make sense as you are using it, you need to declare it as an array in the union.

I was kind of curious if it would be possible to overload the new operator for the class to force a specific member to be on a page boundary. I very quickly kludged together overloaded operators to do that. It causes an entire extra page to be allocated each time. It finds the offset of where that field would be and then adjusts the address by that amount. Since the extra memory is allocated (and assuming I did the math correctly), it would be safe. Very ugly, though.

It stuffs the allocation offset into a member in the class so that it knows the amount to "unoffset" the pointer by to free it. It is really scary code. It seems okay as an experiment but not so nice in production code.

#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;
}

Here is example output:

new 00353FA0
allocation offset 86
address of fieldB 00355000
delete 00353FA0
二智少女 2024-09-23 08:01:30

我不这么认为 - 在堆栈上对齐有点复杂,因为您需要知道当前的位置,分配足够的字节来消耗“当前”内存页,然后分配数据。在堆栈上,这不是通常的操作(即,您不对齐堆栈上的任何内容)。

但是,某些编译器具有会对齐结构的编译指示,MSVC 具有 '__declspecalign',您可以在其中指定数据成员的对齐方式,编译器将插入适当的字节数。

可以建立一个联合,其中在堆上分配 1 个成员 - 联合将像往常一样包含所有字段,但堆分配的成员将只是一个指针。

最后,valloc 已过时 - 您应该使用 memalign 或 posix_memalign 代替。

I don't think so - aligning on the stack is a bit more complicated,as you need to know where you are currently, allocate enough bytes to consume the 'current' page of memory and then allocate the data. On the stack, that's not a usual operation (ie you don't align anything on the stack).

However, some compilers have pragmas that will align structs, MSVC has the '__declspec align' where you can specify the alignment of data members and the compiler will insert the appropriate number of bytes.

It is possible to do a union where 1 member is allocated on the heap - the union will contain all your fields as usual, but the heap-allocated one will be just a pointer.

Lastly, valloc is obsolete - you should use memalign or posix_memalign instead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文