Python解释器源码分析(二):print "Hello World"

目录

0x01 准备工作

0x02 运行输出

0x03 主要流程分析

1 初始化

1.1 数据类型准备 

1.2 内置对象初始化

2 运行

2.1 申请内存池

2.2 词法及语法解析

2.3 解析树节点类型

2.4 运行

0x04 总结


本节以交互模式下执行print “Hello World”为例分析解释器的执行流程。

0x01 准备工作

  • 打开Python-2.7.9\PCbuild目录下的visual studio解决方案pcbuild
  • 设置python工程为启动工程
  • 打开词法语法分析调试变量,python\pythonrun.c源码中修改int Py_DebugFlag = 1
  • 编译python工程

0x02 运行输出

    以Debug模式启动python工程,出现命令行提示窗口,输入print “Hello World”回车运行结果如下图所示。

0x03 主要流程分析

   在解释器命令行窗口输入print “Hello World”回车后执行流程是怎样的呢? Python源码工程支持visual studio的编译和调试,加上visual studio非常强大的单步调试能力,因此对执行流程的分析基本没什么技术难度。这里简单总结下大致的运行流程,有兴趣的童鞋可以自己运行看看。

1 初始化

   上一节中提到过Py_Main函数,它是python进程的入口函数,主要包括参数解析、初始化、运行等逻辑。初始化工作主要完成解释器的初始化,由Py_Main函数调用Py_Initialize函数完成,Py_Initialize函数位于Python\pythonrun.c源码文件内,初始化完成的主要流程如下图所示:

1.1 数据类型准备 

    这里先看下数据类型准备_Py_ReadyTypes,类型的准备主要完成各种数据类型的准备工作。Python C源码中,所有内置的数据类型都是PyTypeObject结构体的一个实例,PyTypeObject结构体中以函数指针的形式,声明了各种处理函数,包括print、repr、hash、call、str、getattro、成员函数(tp_methods)、成员变量(tp_members)等。各个内置数据类型的定义在Objects目录下相应的C源码文件中,比如list数据类型实现的源码文件是Objects\listobject.c,在listobject.c源码文件中定义了结构体实例PyList_Type对象,如下图所示。

    _Py_ReadyTypes函数主要调用Objects\typeobject.c\PyType_Ready函数对内置的数据类型的结构体对象的个元素进行初始化。

    数据类型的初始化,主要完成对int、long、bytearray、float等数据类型的某些特定参数进行初始化。这里有一个东西没有搞明白,bytearray的初始化C函数,PyByteArray_Init函数什么事都没干直接返回1。

1.2 内置对象初始化

  内置对象初始化,也就是__builtin__模块的初始化,通过调用Python\bltinmodule.c中的_PyBuiltin_Init函数完成内置常量、内置数据类型和函数的初始化。内置模块初始化包括两个动作,一是调用PyDict_SetItemString设置__builtin__模块的__dict__属性,二是调用Objects\object.c\_Py_AddToAllObjects函数将内置对象的结构体实例添加到双向循环链表refchain。代码如下图所示:

    内置常量主要包括None、Ellipsis、NotImplemented、False、True。None、Ellipsis、NotImplemented在内存中的对象是PyObject结构体(在Include\object.h中定义)实例,True和False是PyIntObject结构体(在Include\intobject.h中定义)实例。

    内置数据类型和函数,和内置常量的初始化动作一样。这里提一下,包括property、super、object、type、classmethod、staticmethod、str、file等内置对象都是PyTypeObject结构体(在Include\object.h中定义)实例。

2 运行

 初始化完成后,进入交互模式。主要流程如下图。

2.1 申请内存池

    Python采用内存池的方式管理内存的使用,实现源码为Pyhon\pyarena.c。其主要思想是先在内存中申请一块内存,后续在该预先申请的内存块上分配相应的空间给对象使用。arena内存池的管理由两个结构体实现,_block和_arena。内存池管理机制后续进行分析。

2.2 词法及语法解析

    词法及语法解析,主要调用Parse\parsetok.c\parsetok函数。该函数主要流程如下图所示。

    在交互命令行输入的print “Hello World”的单词解析信息存储在结构体parse_state结构体(在Parse\parser.h中定义)

   

    该结构体中有一颗解析树,该解析树存储了词法语法解析结果,各节点是_node类型的结构体。parsetok函数最终返回该解析树的top节点。值得注意的是,新解析出来的单词会作为最新的top节点,并且如果设置了编码方式,解析树的top节点是编码方式。

   使用Parser\parser.c中的dumptree、showtree、printtree调试函数可以打印解析树。利用dumptree函数对parseok返回的解析树输出如下。

    词法语法解析完毕后,调用Parse\ast.c\PyAST_FromNode函数,将解析树各个节点由CST为AST(抽象语法树,Abstract Syntax Tree)。CST和AST都是语法分析的中间结果,不同的地方是CST直接对应语法分析的匹配过程,含有大量的冗余信息,AST省略了大量冗余信息,直接对应实际的语义,也就是最终的分析结果。

    Python为我们提供了parse和ast模块,将其内部的单词和语法解析、字节编译暴露了出来。利用parse和ast模块,我们可以访问python解析树。在python解释器命令行中导入模块parser,然后调用parser. suite(‘print “Hello World”’),可以看到解析结果和c函数dumptree的输出是一样的。同样使用ast模块的parse和dump函数可以看到ast解析结果。

import parser
cst = parser.suite('print "Hello World"')
print cst.tolist()
# [257, [267, [268, [269, [272, [1, 'print'], [304, [305, [306, [307, [308, [310, [311, [312, [313, [314, [315, [316, [317, [318, [3, '"Hello World"']]]]]]]]]]]]]]]]], [4, '']]], [4, ''], [0, '']]

import ast
astrst = ast.parse('print "Hello World"')
print ast.dump(astrst)
# Module(body=[Print(dest=None, values=[Str(s='Hello World')], nl=True)])

2.3 解析树节点类型

         如2.2中描述,词法与语法分析的结果是一颗词法语法解析树,该树的各节点的结构体定义(在Include\node.h中)如下:

     那么节点类型有哪些呢?类型在Include\token.h及graminit.h两个头文件中给出了定义。例如257为文件输入、267为语句、268为单个语句、272为print语句、1为名称、310为表达式、312为AND表达式、3为字符串、4为新行、0为结束标志。

2.4 运行

    运行主要调用Python\pythonrun.c\run_mod函数,其主要包括两个步骤,编译和执行。编译调用python\compile.c中的PyAST_Compile函数,对ast解析结果结构体mod_ty(在Include\Python-ast.h中定义)对象进行编译,返回代码结构体PyCodeObject(在Include\code.h中定义)对象,编译执行的具体实现细节后面再进行分析。另外,上述过程中所有中间数据包括词法语法解析树、AST解析结果、编译等结果都存储在2.1所申请的内存池中。

0x04 总结

    本节简单分析了交互模式下print “Hello World”语句的执行流程,初步掌握了python解释器的工作流程。接下来会对内部一些核心的机制进行分析,包括arena内存池、单词解析、语法分析、编译、执行、全局锁等。

更多文章敬请关注

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

「已注销」

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值