2025年6月4日 星期三 乙巳(蛇)年 三月初八 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

Python 源码剖析 - Hack PyIntObject

时间:12-14来源:作者:点击数:8

现在,让我们荡起双桨,哦不对,让我们挽起衣袖和裤脚,来和 PyIntObject 大战一场。我们渴望在运行时观察 Python 的整数对象体系的变化。这一点,完全可以通过修改 Python 源码来实现。我们修改了 int_print 的行为,让它打印出关于 block_list 和 free_list 的信息,以及小整数缓冲池的信息:

  • static int int_print(PyIntObject *v, FILE *fp, int flags)
  • {
  • PyIntObject* intObjectPtr;
  • PyIntBlock *p = block_list;
  • PyIntBlock *last = NULL;
  • int count = 0;
  • int i;
  • while(p != NULL)
  • {
  • ++count;
  • last = p;
  • p = p->next;
  • }
  • intObjectPtr = last->objects;
  • intObjectPtr += N_INTOBJECTS - 1;
  • printf("address @%p\n", v);
  • printf("********** value\trefCount **********\n");
  • for(i = 0; i < 10; ++i, --intObjectPtr)
  • {
  • printf("%d\t\t%d\n", intObjectPtr->ob_ival, intObjectPtr->ob_refcnt);
  • }
  • printf("block_list count : %d\n", count);
  • printf("free_list : %p\n\n", free_list);
  • return 0;
  • }

需要特别注意的是,在初始化小整数缓冲池时,对于 block_list 以及每个 PyIntBlock 的 objects,都是从后往前开始填充的,所以在初始化完成后,-5 应该在最后一个 PyIntBlock 对象的 objects 内最后一块内存,所以我们需要顺藤摸瓜,一直找到这最后的一块内存,才能观察从 -5 到 4 这 10 个小整数。

首先我们创建一个 PyIntObject 对象 -9999,从 图10 所示的输出信息可以看到,小整数对象很多都被 Python 自身使用多次了。

现在的free_list指向地址为00B6AF9C的内存,根据上面对PyIntObject的分析,那么下一个PyIntObject会在这个地址安身立命。那么好,我们接着再建立了两个PyIntObject对象,它们的值分别是-123456:

从图11所示的结果中可以看到a的地址正是创建i后free_list所指的地址,而b的地址也正是创建a后free_list所指的地址。虽然a和b的值都是一样的,但是它们确实是两个完全没有关系的PyIntObject对象,这点从地址上看得一清二楚。

现在我们将b删除,结果如图12所示:

删除b后,free_list回退到了a创建后free_list的位置,这一点也跟前面的分析是一致的。

最后我们来看一看对小整数对象的监控,连续两次创建PyIntObject对象-5,结果如图13所示:

可以看到,两次创建的 PyIntObject 对象 d1 和 d2 的地址都是一样的,这证明它们实际上是同一个对象。同时我们看到小整数池中-5的引用计数发生了变化,这证明d1和d2实际上都是指向这个对象。此外,free_list 没有发生任何变化。这些都与我们对 PyIntObject 的分析相符。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐