Question

Je tente d'utiliser un syndicat pour que je puisse mettre à jour les champs dans un fil, puis lire dans un autre thread Tous champs. Dans le système actuel, je mutex pour vous assurer que tout est en sécurité. Le problème est avec FieldB, avant que je devais changer FieldB a été déclarée comme champ A et C. Toutefois, en raison d'un pilote tiers, FieldB doit être alligned avec limite de page. Quand j'ai changé le champ B à allouer à valloc, je rencontre des problèmes.

Questions: 1) Est-il possible de déclarer statiquement FieldB alligned sur une frontière de page. Fondamentalement faire la même chose que valloc, mais sur la pile?

2) Est-il possible de faire une union lorsque le champ B, ou tout autre domaine est alloué sur le tas ?. Je ne sais pas si cela est même légal.

Voici un programme simple test j'expérimentait. Cela ne fonctionne pas à moins que vous déclarez FieldB comme champ A et C, et de faire des changements évidents dans les méthodes publiques.

#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;
}
Était-ce utile?

La solution

Je ne pense pas que vous pouvez déclarer fieldB comme un pointeur et obtenir le comportement souhaité (en supposant bien je comprends la question). Pour que l'union du sens que vous utilisez, vous devez le déclarer comme un tableau dans l'union.

J'étais un peu curieux de savoir s'il serait possible de surcharger le nouvel opérateur pour la classe pour forcer un membre spécifique d'être sur une limite de page. Je très rapidement kludged opérateurs ensemble surchargées de le faire. Il provoque toute une page supplémentaire à allouer à chaque fois. Il trouve le décalage où ce champ serait et ajuste alors l'adresse de ce montant. Étant donné que la mémoire supplémentaire est alloué (et en supposant que je l'ai fait le calcul correctement), il serait en sécurité. Très laid, cependant.

Il enfourne l'allocation offset dans un membre de la classe pour qu'il sache le montant à « unoffset » par le pointeur pour le libérer. Il est vraiment effrayant du code. Il semble bien comme une expérience, mais pas si bien dans le code de production.

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

Voici un exemple de sortie:

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

Autres conseils

Je ne pense pas - l'alignement sur la pile est un peu plus compliqué, comme vous devez savoir où vous êtes, allouez suffisamment d'octets pour consommer la page « actuelle » de la mémoire, puis affecter les données. Sur la pile, ce n'est pas une opération habituelle (c.-à-vous tout pour ne pas aligner sur la pile).

Cependant, certains compilateurs ont pragmas qui aligner struct, MSVC a « __ declspec align » où vous pouvez spécifier l'alignement des membres de données et le compilateur insérer le nombre approprié d'octets.

Il est possible de faire une union où 1 membre est affecté sur le tas -. Le syndicat contiendra tous vos champs, comme d'habitude, mais le tas alloué sera juste un pointeur

Enfin, valloc est obsolète -. Vous devez utiliser memalign ou posix_memalign place

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top