CPython v3.12.1 源码学习Windows篇(二)

上一篇我们介绍到了_PyInterpreterState_SetRunningMain这个函数,接下来CPython就会根据你是在执行模块还是代码还是脚本,来选择不同的分支进入了,如下:

这个分支就是当你执行python xxx.py进入的分支,突击检查:Python的工作目录是解释器所在目录还是py文件所在目录?为什么?(上一篇看完,你应该能回答)
都不是。是你当前shell的工作目录,而当前py文件所在目录会被设置到sys.path[0],解释器所在目录也会被设置到sys.path,他们都是用于模块导入时可供搜索的目录。

进入pymain_run_file,可以看到先将你的py文件名转化为了python的对象,更具体来说是PyUnicode_Type这个对象(C语言没有继承概念,但这里其实就是类似于继承),PyUnicode_Type就是Python中的字符串对象,你在Python中写的整数就是PyLong_Type,

这是CPython的命名规范,再比如Python的字典,对应于PyDict_Type,存放位置见下图。

回到源码学习,file_name就是你执行的py文件的绝对路径,而program_name是你解释器的绝对路径,可打印出来查看。python_d.exe和python.exe区别在于,前者是调试版,没有经过编译器优化,在我们学习源码或调试时有用,如果优化了,你会看不明白语句的执行顺序。

现在让我们进入往下走。进入pymain_run_file_obj函数,解释器和脚本都被传入了。

进来第一件重要事情的是打开文件,系统调用时,当前线程会释放掉GIL(暂时不深入讨论,总之Python的多线程是串行的,拿到GIL的线程的字节码才能被执行)。

接下来_Py_fstat_noraise拿到文件的状态信息存入sb中,再通过宏函数去判断下它是不是一个文件目录,如果是就不能执行。那么问题来了,python.exe一定不能执行目录吗?

通过上一篇文章我们可以知道,当你传入目录,CPython会查看这个目录下有没有__main__.py,如果有,会执行这个文件。接下来处理一下当前等待处理的一些信号,如果有执行失败的,则会进入pymain_exit_err_print退出;然后设置编译器标志cf,进入_PyRun_AnyFileObject。

来到这里,我们可以发现分支,一个就是当你在命令行直接键入python进入的交互式编程,第二个就是执行脚本文件,最常见当然还是第二个_PyRun_SimpleFileObject,让我们进入

来到这个函数就可以发现立即导入了一个"__main__"模块,你可以直接在py文件中print(__name__),你会发现它输出"__main__"。

还设置__file__这个变量为你的filename,也可以通过py文件中print(__file__)得到。

接下的核心部分是这里,if(pyc)是如果传入的是pyc文件的分支;else即一般分支,没有正确loader,进入pyrun_file,我们应该快要到达真正的解释执行了。

进来就是内存分配,Python的内存管理是层级式的(block, poll, arena),从左至右变大;

相信更容易直接看到是这个_PyParser_ASTFromFile函数,顾名思义从文件中解析出抽象语法树AST。这个就不能展开讲了,总之做的事情就是执行了parser(解释器),进行了语法语义分析,最重要的是得到了mod,让我们快马加鞭进入run_mod。

注意函数_PyAST_Compile,将你的AST返回的mod编译成了PyCodeObject。是的,在Python中你的代码也是对象,进入run_eval_code_obj!!!

不关心,继续进入PyEval_EvalCode

恭喜我们,来到了Python/ceval.c。

在这个函数内部,我们将代码co,封装到了func,注意这里出现的Frame这个名词(包含在PyFrameConstructor),这是CPython非常重要的概念,继续继续进入_PyEval_Vector

继续进入_PyEval_EvalFrame前,注意_PyEvalFramePushAndInit函数,他的命名提醒我们这个Frame是栈结构组织,像不像函数调用栈?!

进入_PyEval_EvalFrameDefault

这里还有个PyCFrame

来到770行DISPATCH宏函数查看定义,发现让我们跳转到dispatch_opcode,终于

我们来到了843行的switchcode分支,这个opcode你猜是什么?

就是python的单个字节码,意味着这里已经在通过switch case语句来逐一执行你的字节码了。

看到熟悉的"LOAD_CONST",这个字节码就对应下面这些C代码的执行

下一篇让我们通过一个简单的脚本文件,观察字节码的执行过程。

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值