vmware的python编程在哪_51. VM 篇:Python 是如何运行起来的?

对于 Python 是一门优雅的语言,这一点应该不会有太多人反对,随着我们对 CPython 实现的研究,不得不说 Python 的优雅一直渗透到它的底层,在 Python 的 C 实现这一层,几乎全部以 Python C API 实现,函数之间传递的是 Object *,Dict、Tuple、List 等对象,在 CPython 的 C 实现中更是随处可见。

如果将 Python 进行分层,最底层是基本的内存管理,之上就是 Python 强大的类型和面向对象系统,Python VM 甚至也是高度依赖于其自身的类型系统,在最顶层支撑了 Python 语言的用户接口。Python 语言实际上是对 Python 类型系统和 VM 的操作。

一切从 main 开始

所有 C 语言程序都必须有一个 main 函数,Python,更准确的说 CPython 也是一个程序,main 就是它运行的开端。找到这个开端,算是理清 “头绪” 的第一步。

在 GDB 中设置 main 函数的断点,很容易找到 Python 的入口位置:

// Programs/python.c:16

int

main(int argc, char **argv)

{

return Py_BytesMain(argc, argv);

}

非常简单,可以看到进入 main 函数之后,只有一个简短的 Py_BytesMain 函数,实际上这个函数可不简单,Python 的一切都在这个函数里,如果在交互模式下,这个函数也不会像想象中一样迅速返回。如果一直追踪下去,会遇到下面这个比较长的函数:

// Modules/main.c:551static void

pymain_run_python(int *exitcode)

{

// ··· ···

// some if-else conditions else if (config->run_filename != NULL) {

// 执行 py 文件 *exitcode = pymain_run_file(config, &cf);

}

else {

// 交互模式 *exitcode = pymain_run_stdin(config, &cf);

}

// ··· ···}

在这里 Python 的运行流程开始分道扬镳,执行文件,或者进入交互模式从标准输入读取指令,分别由不同的函数处理。Python 的初始化工作也大体结束,真正开门营业了。

如果留意源码中的 Object *,实际上此时 Python VM 还什么都没有干呢,但 Python 对象已经先跑起来了。Object 很早就存在于 Python 中,这也是在研究 VM 之前,破天荒的优先认识类型系统的原因。

py 文件的执行

Python 的交互模式可以认为是临时一行一行写代码,写一点执行一点。为了方便,我们现在先搞清楚 py 文件是怎么执行的就好了,也就是要让程序流运行到 pymain_run_file 里面去。方法也很简单,在 GDB 执行 run test.py 就可以了,这就相当于执行 python test.py。这个 test.py 是我临时写的一小段代码;

print("hello world")

"hello world" 是如此流行,我们也遵守这个习惯好了。

如果一切顺利,很快就会找到这里:

// Python/pythonrun.c:1039

PyObject *

PyRun_FileExFlags(FILE *fp, const char *filename_str, int start, PyObject *globals,

PyObject *locals, int closeit, PyCompilerFlags *flags)

{

// ··· ···

filename = PyUnicode_DecodeFSDefault(filename_str);

// ··· ···

// 从文件对象,构建 AST mod = PyParser_ASTFromFileObject(fp, filename, NULL, start, 0, 0,

flags, NULL, arena);

// 执行该语法数 ret = run_mod(mod, filename, globals, locals, flags, arena);

// ··· ···}

// Python/pythonrun.c:1132

static PyObject *

run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals,

PyCompilerFlags *flags, PyArena *arena)

{

// ··· ···

// 编译,生成 codeobject,co 是它的缩写 co = PyAST_CompileObject(mod, filename, flags, -1, arena);

// ··· ···

// 执行 codeobject v = run_eval_code_obj(co, globals, locals);

Py_DECREF(co);

return v;

}

到了这里,我们可以暂时先不继续深入跟进,可以看到在 run_eval_code_obj 返回的时候,test.py 已经正确执行,打印了 "hello wold"。

如果算上 PyRun_FileExFlags 之前某处打开 py 文件,那么 py 文件的运行至少经历了这些步骤:test.py 也就是源码文件,首先被打开并封装为 Python 的文件对象;

之后构建 AST,即抽象语法树,编译原理是另外一个话题了,有兴趣可以阅读编译原理经典著作龙书、虎书、鲸书;

再将 AST 转换为 Python 字节码,源码中已经可以看出,字节码保存在 codeobject 中;

最终执行该字节码,结束。

这样一个简单的 py 文件就执行完毕了。

文件操作、编译原理等问题已经超出我们研究的范围,另外在生成 codeobject 前还有一个优化过程,我们也暂时搁置这一议题,后面我们将会重点研究 codeobject,以及 Python VM 到底是如何响应字节码的指令,最终把 Python “运行” 起来的。

关注公众号 “江川Go”,了解程序员的烧脑日常。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值