如何编译和调试Python内核源码?
<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>写在前面</li><li>获取源代码</li><li>源代码的组织</li><li>windows下编译CPython</li><li>调试CPython</li><li>小结</li><li>参考</li></ul></div><p></p><p>博客:blog.shinelee.me | 博客园 | CSDN</p>
<h1 id="写在前面">写在前面</h1>
<p><strong>如果对Python源码感兴趣,那“窥探”其实现的最佳方式就是调教它,不,调试它。</strong></p>
<h1 id="获取源代码">获取源代码</h1>
<p>Python的官方默认实现为CPython,即C语言实现(主要指解释器的实现,其他实现见Other Interpreter Implementations)。CPython的源代码可以从官网pyhton.org或者 github.com/python/cpython 获取,目前最新的稳定版本为3.8.0,于2019.10.14发布。这里,从官网 https://www.python.org/downloads/release/python-380/ 下载源码压缩包,如下图所示,</p>
<p><img src="https://s2.ax1x.com/2019/10/16/KiV44H.png" alt="python source code" loading="lazy"></p>
<h1 id="源代码的组织">源代码的组织</h1>
<p>解压后,<strong>目录结构</strong>如下</p>
<pre><code class="language-bash">{ Python-3.8.0 }» tree -d -L 1 .
.
├── Doc # rst(reStructuredText)格式官方文档,用其生成https://docs.python.org/
├── Grammar # Python的EBNF(Extended Backus–Naur form)语法定义文件
├── Include # .h 头文件
├── Lib # .py 纯Python实现的标准库
├── m4 # ?
├── Mac # Mac-specific code,支持MacOS
├── Misc # Things that do not belong elsewhere.
├── Modules # C实现的标准库,内含.c .asm .macros .h
├── Objects # 内置数据类型实现
├── Parser # Python语法分析器源码
├── PC # Windows-specific code,支持Windows
├── PCbuild # Windows生成文件,for MSVC
├── Programs # main函数文件,用于生成可执行文件,如python.exe的入口文件
├── Python # CPython解释器源码
└── Tools # 独立工具代码,used to maintain Python
</code></pre>
<p>CPython的源码组织结构如下,摘抄自CPython Source Code Layout,</p>
<img src="https://s2.ax1x.com/2019/10/16/KiG3Zj.png" alt="CPython Source Code Layout" style="zoom: 80%">
<p>源码文件分门别类存放,而且,无论是py实现的标准库、c实现的标准库、内置数据类型还是内置函数,在<code>Lib/test/</code>和<code>Doc/library/</code>目录下都有与之对应的test_x.py测试文件和rst文档文件(对于内置数据类型和函数,其文档集中保存在stdtypes.rst和functions.rst)。比如,内置类型<code>int</code>位于<code>Objects/longobject.c</code>文件中。</p>
<p>下面正式开始编译CPython。</p>
<h1 id="windows下编译cpython">windows下编译CPython</h1>
<p>据Compile and build on Windows,Python3.6及之后的版本可以使用VS2017编译,安装VS2017时,记得勾选 <strong>Python development</strong> 和 <strong>Python native development tools</strong>,有备无患。</p>
<p>安装好VS2017后,双击<code>PCbuild/pcbuild.sln</code>,打开解决方案。因为我们的关注点仅在Python内核和解释器部分,所以仅编译python和pythoncore,其他模块暂时忽略,具体地,</p>
<ul>
<li>切换到debug win32</li>
<li>右键解决方案→属性→配置属性</li>
<li>仅勾选项目python和pythoncore</li>
<li>确定</li>
</ul>
<img src="https://s2.ax1x.com/2019/10/16/KiDRqx.png" alt="vs2017 python build configuration" style="zoom: 80%">
<p>此时再“生成解决方案”,生成目录为<code>PCbuild/win32</code>,内容如下,含解释器python_d.exe和内核python38_d.dll,</p>
<p><img src="https://s2.ax1x.com/2019/10/16/KirNfe.png" alt="PCbuild build dir" loading="lazy"></p>
<p>接下来,将项目python设为启动项目(默认状态即是启动项目),点击<strong>调试</strong>,运行得到如下控制台,可以像平时使用python一样,与之交互。</p>
<p><img src="https://s2.ax1x.com/2019/10/16/KisW4O.png" alt="python38_d debug console" loading="lazy"></p>
<p>如果想生成全部模块,需要运行<code>PCbuild\get_externals.bat</code>下载依赖,再编译,具体可参见Build CPython on Windows。</p>
<h1 id="调试cpython">调试CPython</h1>
<p><strong>只要程序能运行起来,一切就好办了。凭借“宇宙最强IDE”,我们可以任性地设断点调试甚至修改代码。</strong></p>
<p><code>F5</code>重新启动调试,弹出控制台。在上面我们知道<code>int</code>类型位于<code>Objects/longobject.c</code>文件,打开文件,简单浏览后在函数<code>PyObject * PyLong_FromLong(long ival)</code>入口处打个断点。然后,在弹出的控制台中输入<code>a = 1</code>来创建<code>int</code>对象,回车,程序停在了断点处,查看变量<code>ival</code>的值为1——恰为我们输入的数值,<strong>这个函数会跟根据输入的C long int创建一个<code>int</code>对象,返回对象指针</strong>。</p>
<p><img src="https://s2.ax1x.com/2019/10/16/Ki48iR.png" alt="debug int" loading="lazy"></p>
<p>再来看看函数调用堆栈,如下图所示,</p>
<p><img src="https://s2.ax1x.com/2019/10/16/Ki4qyT.png" alt="call stack" loading="lazy"></p>
<p>调用顺序从下至上,从中可以推断出,</p>
<ul>
<li>从python_d.exe的入口main运行起来后,进入python38_d.dll</li>
<li>从标准输入stdin中读取键入的字符串</li>
<li>解析字符串,建立了<strong>语法树AST</strong>(abstract syntax tree)</li>
<li>解析语法树中的节点,判断字符为number,将字符串转化为C long int</li>
<li>由C long int创建Python的<code>int</code>对象</li>
</ul>
<p>继续运行,弹出的控制台中光标前出现<code><<<</code>,等待输入。这时如果我们点击调试中的停止按钮(全部中断),会发现程序停在<code>Parser/myreadline.c</code>文件<code>_PyOS_WindowsConsoleReadline</code>函数中的<code>ReadConsoleW</code>一行,</p>
<pre><code class="language-c">if (!ReadConsoleW(hStdIn, &wbuf, wbuflen - total_read, &n_read, NULL)) {
err = GetLastError();
goto exit;
}
</code></pre>
<p><code>ReadConsoleW</code>为WINAPI,详见ReadConsole function,其等待并读取控制台的输入,读取的字符保存在<code>wbuf</code>中。如果有输入,则进入上面的流程,解析→建立语法树→……</p>
<h1 id="小结">小结</h1>
<p>至此,我们揭开了Python面纱的一角——<strong>不过是一个可运行、可调试的程序而已</strong>(微笑)。</p>
<h1 id="参考">参考</h1>
<ul>
<li>Directory structure</li>
<li> reStructuredText </li>
<li> Extended Backus–Naur form </li>
<li>Exploring CPython’s Internals</li>
<li>Compile and build on Windows</li>
</ul><br><br>
来源:https://www.cnblogs.com/shine-lee/p/11685775.html
頁:
[1]