Virtual memory tricks





I've wanted to write a post about working with virtual memory for quite some time now . And when @jimsagevid in reply to my tweet wrote about it, I realized that the time had come.





β€” . , , ( , ), . , - , . , ( ) - .





, , .





, , , . , , . , - std::vector , . 





:





objecto *objects[MAXOBJECTS]







? , . β€” . , , @jimsagevid, .





. , .





, . , 1 :





#define MAXOBJECTS 1000000000ULL
objecto **objects = virtualalloc(MAXOBJECTS * sizeof(objecto ));
      
      



8 , , . , .





β€” virtualalloc()



, . Windows VirtualAlloc(), Linux mmap().





β€” Windows MEMRESERVE MEMCOMMIT. MEMRESERVE , MEMCOMMIT . , MEMCOMMIT, , . MEMCOMMIT , , , MEMCOMMIT . Windows , , MEMCOMMIT ( ). MEMRESERVE , MEMCOMMIT .





, Linux overcommit ( ). , . Linux (reserve) (commit), .





8 ? . β€” . 64- 2⁢⁴. , . . . , 64- Windows 256 . 32 000 8 , , .





, , - .





:





uint32_t num_tanks;
tank_t tanks[MAX_TANKS];

uint32_t num_bullets;
bullet_t bullets[MAX_BULLETS];

...
      
      



, , , , . , std::vector



MAX¨C13C 1 :





#define GB 1000000000
uint32_t num_tanks;
tank_t *tanks = virtual_alloc(GB);
uint32_t num_bullets;
bullet_t *bullets = virtual_alloc(GB);
      
      



ID

(ID) . :





uint64_t allocate_id(system_t *sys)
{
    return sys->next_free_id++;
} 
      
      



. , . , . , , .





:





system_id_t *allocate_id(system_t *sys)
{
    if (!sys->id_block || sys->id_block_used == PAGE_SIZE) {
        sys->id_block = virtual_alloc(PAGE_SIZE);
        sys->id_block_used = 0;
    }
    return (system_id_t *)(sys->id_block + sys->id_block_used++);
}

      
      



, , (opaque struct), , uint64_t



.





β€”  , - , . - , , , . , , , , . , .





, , " " . . 64- , , 2 , 99,999999988%. , , , , , / . , , .





, , . :





  • , .





  • .





, - , . , , - . , , (allocation block header).





, end-of-page β€” ( ). , .





Placing a block at the end of a page block.
.

. -, , . , . , β€” , , .





-, , . , "" , .





, . , , , end-of-page β€” . . , .





end-of-page . malloc



:





void *eop_malloc(uint64_t size)
{
    uint64_t pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
    char *base = virtual_alloc(pages * PAGE_SIZE);
    uint64_t offset = pages * PAGE_SIZE - size;
    return base + offset;
}

      
      



β€” .  , . , , , .





. (reserved), (commited). , , . , . (access violation). (: Windows, reserve commit .)





. end-of-page β€” .





 

. .





. , , .





Memory fragmentation.
.

, , , "" . , , . , , . . , - .





β€” . , .





, . "" , "" , .





Physical memory is not fragmented when virtual memory is allocated.
.

, . . -, , . 





, . .. , . , , , , , 99.999999988% . . ( 32- .)





, , 4 . , β€”  , .





. β€” , .





. , , . , 300 . , , . , 16 32 64 128 . , .





16 * 300 = 4800. 8 , . . , : 4 , 8 , 16 , 32 , …, , (13, 27, 54, 109,…). - , 150 2 .





, . , , , , .





? . , . , , TLB. , , , β€” , β€” --. , - .





, . . , . , β€” 32- β€” 64- 4K .





 

(Fabian Giesen). , , .





β€” , . , , , .





. , .





"" "" , , , . uint64t, , - :





enum {BUFFER_SIZE = 8*1024};
struct ring_buffer_t {
    uint8_t data[BUFFER_SIZE];
    uint64_t read;
    uint64_t written;
};
      
      



, , , , . , BUFFER_SIZE



, . , . , .





, , . , memcpy



, .





void write(ring_buffer_t *rb, uint8_t *p, uint64_t n)
{
    uint64_t offset = rb->written % BUFFER_SIZE;
    uint64_t space = BUFFER_SIZE - offset;
    uint64_t first_write = n < space ? n : space;
    memcpy(rb->data + offset, p, first_write);
    memcpy(rb->data, p + first_write, n - first_write);
    rb->written += n;
}
      
      



, . , β€” . memcpy()



.





? " " (commit) , (decommit). β€” , . . , . 1 / 4 () . , , 64- Windows 256 . , commit decommit . 





, , . . . , (ring buffer), .





Ring buffer with page mapping. 
(ring buffer) . 

, " " . , . :





void write(ring_buffer_t *rb, uint8_t *p, uint64_t n)
{
    memcpy(rb->data + (rb->written % BUFFER_SIZE), p, n);
    rb->written += n;
}

uint8_t *read(ring_buffer_t *rb, uint64_t n)
{
    uint8_t *p = rb->data + (rb->read % BUFFER_SIZE);
    rb->read += n;
    return p;
}

      
      



, - .





, . Windows CreateFileMapping()



. , , " ", . , INVALID_HANDLE_VALUE



, . MapViewOfFileEx()



, . , , . , MapViewOfFileEx()



, , , , - - . , , . , .





- , , @niklasfrykholm.





Linux

, "Linux overcommit ( )". , , , , , .





Linux "" commit ( ). overcommit, , . , overcommit, , , , , . OOM killer .





, overcommit (vm.overcommit_memory = 1



) (vm.overcommit_memory = 2



). . https://www.kernel.org/doc/Documentation/vm/overcommit-accounting





, . , .





, Windows: (reserve) (commit). overcommit_memory



.





mmap()



, Linux mmap()



PROT_NONE



. commit , mprotect()







β€” MAP_NORESERVE



PROT_NONE



, overcommit_memory = 2



, MAP_NORESERVE



. . https://lwn.net/Articles/627557/






" ".





-: " C: ".












All Articles