在引用计数降为零后,CPython 如何检测到哪里可以找到 pool_header?

How CPython detects where to find pool_header after reference count goes down to zero?

最近看了一篇关于CPython内存模型的文章:https://rushter.com/blog/python-memory-managment/。文章展示了 CPython 用于管理单个池的以下结构:

struct pool_header {
   union { block *_padding;
           uint count; } ref;          /* number of allocated blocks    */
   block *freeblock;                   /* pool's free list head         */
   struct pool_header *nextpool;       /* next pool of this size class  */
   struct pool_header *prevpool;       /* previous pool       ""        */
   uint arenaindex;                    /* index into arenas of base adr */
   uint szidx;                         /* block size class index        */
   uint nextoffset;                    /* bytes to virgin block         */
   uint maxnextoffset;                 /* largest valid nextoffset      */
};

我不明白的是,如果某个块空闲了,CPython 如何获取这个 header 来更新?我是对的吗,它依赖于一些低级技巧,如果你分配一个 4Kb 大小的页面,那么它的指针会以某种方式对齐,你可以通过将几个位清零来检测页面的开始(可能是 12,因为 2^12=4096 ) 块地址?我说得对吗?

它只是 rounds the block address down 到最近的池对齐值:

/* Round pointer P down to the closest pool-aligned address <= P, as a poolp */
#define POOL_ADDR(P) ((poolp)_Py_ALIGN_DOWN((P), POOL_SIZE))

虽然舍入基本上与您假设的一样,但它之所以有效并不是因为您假设的低级技巧,而是因为 CPython 手动确保池具有必要的对齐方式。当 CPython 分配用于竞技场池的大块内存时,它 sets 竞技场的 pool_address 到该大内存块中的第一个池对齐地址:

    /* pool_address <- first pool-aligned address in the arena
       nfreepools <- number of whole pools that fit after alignment */
    arenaobj->pool_address = (block*)arenaobj->address;
    arenaobj->nfreepools = MAX_POOLS_IN_ARENA;
    excess = (uint)(arenaobj->address & POOL_SIZE_MASK);
    if (excess != 0) {
        --arenaobj->nfreepools;
        arenaobj->pool_address += POOL_SIZE - excess;
    }