目录node

本节以交互模式下执行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”回车运行结果以下图所示。

python applium windows UI 自动化输出源码怎么输出 python print源码_数据类型

0x03 主要流程分析

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

1 初始化

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

python applium windows UI 自动化输出源码怎么输出 python print源码_python_02

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对象,以下图所示。

python applium windows UI 自动化输出源码怎么输出 python print源码_数据类型_03

_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。代码以下图所示:

python applium windows UI 自动化输出源码怎么输出 python print源码_数据类型_04

内置常量主要包括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 运行

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

python applium windows UI 自动化输出源码怎么输出 python print源码_数据类型_05

2.1 申请内存池

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

python applium windows UI 自动化输出源码怎么输出 python print源码_python中print源码_06

2.2 词法及语法解析

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

python applium windows UI 自动化输出源码怎么输出 python print源码_python中print源码_07

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

python applium windows UI 自动化输出源码怎么输出 python print源码_python_08

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

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

python applium windows UI 自动化输出源码怎么输出 python print源码_python中print源码_09

词法语法解析完毕后,调用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中)以下:

python applium windows UI 自动化输出源码怎么输出 python print源码_python中print源码_10

那么节点类型有哪些呢?类型在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所申请的内存池中。

python applium windows UI 自动化输出源码怎么输出 python print源码_初始化_11

0x04 总结

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