当前位置: 首页 > Linux

深入理解python虚拟机:pyc文件结构

时间:2023-04-06 20:28:29 Linux

深入理解python虚拟机:pyc文件结构本文主要介绍.py文件编译后对应的pyc文件结构,一个核心的pyc文件,内容是python字节码。pyc文件pyc文件是Python在解释和执行源代码时生成的字节码文件。它包含源代码的编译结果和相关的元数据信息,以便Python可以更快地加载和执行代码。Python是一种解释型语言,它不像编译型语言那样直接将源代码编译成机器码执行。Python解释器在运行代码之前将源代码编译成字节码,然后解释字节码执行。.pyc文件就是这个过程中生成的字节码文件。Python解释器在第一次执行一个.py文件时,会在同一目录下生成一个对应的.pyc文件,以便下次加载时执行速度更快。如果源文件在修改后重新加载,解释器会重新生成.pyc文件以更新缓存的字节码。生成pyc文件一个普通的python文件需要通过编译器转换成字节码,然后将字节码交给python虚拟机,然后由python虚拟机执行字节码。整体流程如下:我们可以直接使用compileall模块生成对应文件的pyc文件。?pvmlsdemo.pyhello.py?pvmpython-mcompileall.Listing'.'...Listing'./.idea'...Listing'./.idea/inspectionProfiles'...编译'./demo.py'...编译'./hello.py'...?pvmls__pycache__demo.pyhello.py?pvmls__pycache__demo.cpython-310.pychello.cpython-310.pycpython-mcompileall。该命令会递归扫描当前目录下的py文件,生成对应的pyc文件。pyc文件布局magicnumber的第一部分由两部分组成:magic的第一部分由一个2字节的整数和另外两个字符回车和换行组成,“\r\n”也占两个字节,共四个字节。这个两个字节的整数在不同的python版本中是不同的。例如python3.5中为3351,python3.9中为3420、3421、3422、3423、3424(Python3.9中的Minorversion)。第二部分BitField的主要作用是复现以后的编译结果,但是在python3.9a2中,这个字段的值都是0,具体可以参考PEP552-Deterministicpycs。python2和python3早期版本不存在该字段(python3.5还没有),python3以后版本才出现该字段。第三部分是整个py源文件的大小。第四部分也是整个pyc文件中最重要的部分。最后一部分是CodeObject对象的序列化数据。我们稍后会分析与此对象相关的数据。我们现在来分析一个pyc文件。对应的python代码为:deff():x=1return2pyc文件的十六进制形式如下:?__pycache__hexdump-Chello.cpython-310.pyc000000006f0d0d0a00000000b948216420000000|o.....H!d...|00000010e3000000000000000000000000000000|。.........|00000020000200000040000000730c0000006400|......@...s...d。|00000030640184005a0064025300290363000000|d...Z.d.S.).c...|000000400000000000000000000100000001|000.........|000000500043000000730800000064017d006402|.C...s....d.}.d.|00000060530029034ee901000000e902000000a9|S.).N.........|00000070002901da017872030000007203000000|.)...xr....r....|00000080fa0a2e2f68656c6c6f2e7079da016601|.../hello.py..f.|0000009000000073040000000401040172060000|...s.....r...|000000a0004e2901720600000072030000007203|.N).r...r...r.|000000b000000072030000007205000000da083c|...r...r......<|000000c06d6f64756c653e010000007302000000|module>....s....|000000d00c00|..|000000d2因为数据使用小端表示,所以对于上面的数据:幻数的第一部分是:0xa0d0d6f,位域的第二部分是:0x0。第三部分最后修改日期为:0x642148b9。第四部分的文件大小为:0x20字节,也就是说hello.py的文件大小为32字节。下面是读取pyc文件头元信息的一小段代码:unpack('>20LOAD_CONST2(无)22RETURN_VALUEconsts'__main__'100无'firstlineno3lnotabb'08010401'下面是代码对象中各个字段的作用:首先,你需要了解代码块的概念,所谓代码块就是一小段python代码,作为一个执行作为一个整体的小单元,这是python中常见的代码块block有:函数体,类的定义,和一个模块。argcount,这个表示一个代码块的参数个数,这个参数只对函数体代码块有用,因为函数可能有参数,比如上面的pycdemo.py是一个模块而不是函数,所以对应的值该参数的为0.co_code,该对象的具体内容是一个字节序列,存放的是真正的python字节码,主要用于python虚拟机执行,本文不再详细分析。co_consts,这个字段是一个列表类型的字段,主要包含一些字符串常量和数值常量,比如上面的“\_\_main\_\_”和100。co_filename,该字段的含义是对应源文件的文件名。co_firstlineno,该字段的含义是python源文件中第一行代码出现的行数。这个字段在调试的时候非常重要。co_flags,该字段的主要含义是标识代码对象的类型。0x0080表示这个block是协程,0x0010表示这个代码对象是嵌套的等等co_lnotab,这个字段的含义主要是用来计算每条字节码指令对应的源代码行数。co_varnames,这个字段的主要意义是代表一个在代码对象中本地定义的名称。co_names与co_varnames不同,表示未在本地定义但在代码对象中使用的名称。co_nlocals,该字段表示一个代码对象中局部使用的变量个数。co_stackszie,因为python虚拟机是栈计算机,这个参数的值代表栈需要的最大值。co_cellvars,co_freevars,这两个字段主要和嵌套函数和函数闭包相关,我们会在后续文章中详细讲解这个字段。综上所述,本文主要介绍python文件编译后的结果文件.pyc文件的结构。pyc文件中最重要的结构之一是代码对象对象。本文主要简单介绍代码对象。每个字段的作用。在后续的文章中,会给出详细的例子来说明,正确理解这些字段的含义,对我们理解python虚拟机有很大的帮助。本文是深入理解python虚拟机系列文章之一。文章地址为:https://github.com/Chang-LeHung/dive-into-cpython更多精彩内容合集可访问:https://github.com/Chang-LeHung/CSCore关注公众号:废柴研究僧,多学点计算机(Java,Python,计算机系统基础,算法和数据结构)。