Python源码学习:启动流程简析
发布日期:2021-07-25 13:04:25
浏览次数:11
分类:技术文章
本文共 9942 字,大约阅读时间需要 33 分钟。
Python源码分析
本文环境python2.5系列参考书籍<>
Python简介:
python主要是动态语言,虽然Python语言也有编译,生成中间字节码,但是它还是一种动态语言,边解释边运行。让我们去揭开Python的一些基础分析。分析
首先,可以上官网获取Python2.5的源代码,下载源代码后可以打开代码的目录,其中主要有目录Include: 所有的头文件;Python: Python核心的解释器执行,线程等主要功能;Lib:由Python语言编写的包;Modules: 用C语言编写的模块,当中还包括入口函数;Parser: Python语言的词法分析与语法分析模块,该流程与编译原理的基础知识一样;Objects: Python语言的内建对象。
以上为基础信息介绍,接下来就简析一下Python的启动与入口函数。
位于Modules/python.c中#include "Python.h"#ifdef __FreeBSD__#include#endifintmain(int argc, char **argv){ /* 754 requires that FP exceptions run in "no stop" mode by default, * and until C vendors implement C99's ways to control FP exceptions, * Python requires non-stop mode. Alas, some platforms enable FP * exceptions by default. Here we disable them. */#ifdef __FreeBSD__ fp_except_t m; m = fpgetmask(); fpsetmask(m & ~FP_X_OFL);#endif return Py_Main(argc, argv);}
当在命令行中调用 python时,直接就进入了Py_Main函数,
intPy_Main(int argc, char **argv){ ... Py_Initialize(); # 进行初始化操作 ... if (command) { sts = PyRun_SimpleStringFlags(command, &cf) != 0; free(command); } else if (module) { sts = RunModule(module); free(module); } else { if (filename == NULL && stdin_is_interactive) { RunStartupFile(&cf); } /* XXX */ sts = PyRun_AnyFileExFlags( fp, filename == NULL ? "" : filename, filename != NULL, &cf) != 0; # 进入命令行或者输入文件的交换模式 } ...}
这里省略部分其他如帮助信息等输入参数的分析,由此可以看到,Python首先会进行初始化Py_Initialize,然后在调用PyRun_AnyFileExFlags进行处理,首先先看想Py_Initialize做了哪些工作;
voidPy_InitializeEx(int install_sigs){ PyInterpreterState *interp; // 解释器对象 PyThreadState *tstate; // 线程对象 PyObject *bimod, *sysmod; char *p;#if defined(Py_USING_UNICODE) && defined(HAVE_LANGINFO_H) && defined(CODESET) char *codeset; char *saved_locale; PyObject *sys_stream, *sys_isatty;#endif extern void _Py_ReadyTypes(void); if (initialized) return; initialized = 1; // 是否已经初始化标志位 if ((p = Py_GETENV("PYTHONDEBUG")) && *p != '\0') // 设置标志位 Py_DebugFlag = add_flag(Py_DebugFlag, p); if ((p = Py_GETENV("PYTHONVERBOSE")) && *p != '\0') Py_VerboseFlag = add_flag(Py_VerboseFlag, p); if ((p = Py_GETENV("PYTHONOPTIMIZE")) && *p != '\0') Py_OptimizeFlag = add_flag(Py_OptimizeFlag, p); interp = PyInterpreterState_New(); // 获取一个新的解析器对象 if (interp == NULL) Py_FatalError("Py_Initialize: can't make first interpreter"); tstate = PyThreadState_New(interp); // 生成第一个线程 if (tstate == NULL) Py_FatalError("Py_Initialize: can't make first thread"); (void) PyThreadState_Swap(tstate); // 将生成的线程对象设置成当前要运行的线程对象 _Py_ReadyTypes(); // 检查对象type是否能创建等检查操作 if (!_PyFrame_Init()) // 检查当前运行的栈帧 Py_FatalError("Py_Initialize: can't init frames"); if (!_PyInt_Init()) Py_FatalError("Py_Initialize: can't init ints"); // 创建小整数缓存池 _PyFloat_Init(); // 检查当前运行计算机的float方式 interp->modules = PyDict_New(); // 新建一个字典对象作为当前解释器对象的modules, __builtin__中的方法就会放入其中 if (interp->modules == NULL) Py_FatalError("Py_Initialize: can't make modules dictionary");#ifdef Py_USING_UNICODE /* Init Unicode implementation; relies on the codec registry */ _PyUnicode_Init();#endif bimod = _PyBuiltin_Init(); // 将内建对象等方法加入到interp->modules字典中 if (bimod == NULL) Py_FatalError("Py_Initialize: can't initialize __builtin__"); interp->builtins = PyModule_GetDict(bimod); // 将内建对象方法等,放入解释器的builtins中 if (interp->builtins == NULL) Py_FatalError("Py_Initialize: can't initialize builtins dict"); Py_INCREF(interp->builtins); sysmod = _PySys_Init(); // sys模块方法的初始化 if (sysmod == NULL) Py_FatalError("Py_Initialize: can't initialize sys"); interp->sysdict = PyModule_GetDict(sysmod); if (interp->sysdict == NULL) Py_FatalError("Py_Initialize: can't initialize sys dict"); Py_INCREF(interp->sysdict); _PyImport_FixupExtension("sys", "sys"); PySys_SetPath(Py_GetPath()); PyDict_SetItemString(interp->sysdict, "modules", interp->modules); _PyImport_Init(); /* initialize builtin exceptions */ _PyExc_Init(); // 初始化错误的內建方法 _PyImport_FixupExtension("exceptions", "exceptions"); /* phase 2 of builtins */ _PyImport_FixupExtension("__builtin__", "__builtin__"); _PyImportHooks_Init(); if (install_sigs) initsigs(); /* Signal handling stuff, including initintr() */ initmain(); /* Module __main__ */ if (!Py_NoSiteFlag) initsite(); /* Module site */ /* auto-thread-state API, if available */#ifdef WITH_THREAD _PyGILState_Init(interp, tstate);#endif /* WITH_THREAD */ warnings_module = PyImport_ImportModule("warnings"); if (!warnings_module) PyErr_Clear();#if defined(Py_USING_UNICODE) && defined(HAVE_LANGINFO_H) && defined(CODESET) /* On Unix, set the file system encoding according to the user's preference, if the CODESET names a well-known Python codec, and Py_FileSystemDefaultEncoding isn't initialized by other means. Also set the encoding of stdin and stdout if these are terminals. */ ...}
初始化的内容,基本上涵盖了Python运行时的信息,由于Python的执行需要有解释器类型,也需要线程状态类型,构建运行时的内建类型,检查type是否能够正常生成, 检查Python支持的对象能否正常新建等信息。后续有机会会剖析其中部分内容。
接下来就是PyRun_AnyFileExFlags处理
intPyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit, PyCompilerFlags *flags){ if (filename == NULL) filename = "???"; if (Py_FdIsInteractive(fp, filename)) { int err = PyRun_InteractiveLoopFlags(fp, filename, flags); // 进入解释器交互模式 if (closeit) fclose(fp); return err; } else return PyRun_SimpleFileExFlags(fp, filename, closeit, flags); // 进入执行文本文件模式}
再次我们查看执行文本模式
intPyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, PyCompilerFlags *flags){ PyObject *m, *d, *v; const char *ext; m = PyImport_AddModule("__main__"); // 导入__main__ if (m == NULL) return -1; d = PyModule_GetDict(m); // 获取导入的__main__内容 if (PyDict_GetItemString(d, "__file__") == NULL) { // 设置当前执行文件的__file__属性值 PyObject *f = PyString_FromString(filename); if (f == NULL) return -1; if (PyDict_SetItemString(d, "__file__", f) < 0) { Py_DECREF(f); return -1; } Py_DECREF(f); } ext = filename + strlen(filename) - 4; if (maybe_pyc_file(fp, filename, ext, closeit)) { // 尝试检查是否存在编译好的字节码文件,如果已经存在则先运行字节码文件 /* Try to run a pyc file. First, re-open in binary */ if (closeit) fclose(fp); if ((fp = fopen(filename, "rb")) == NULL) { fprintf(stderr, "python: Can't reopen .pyc file\n"); return -1; } /* Turn on optimization if a .pyo file is given */ if (strcmp(ext, ".pyo") == 0) Py_OptimizeFlag = 1; v = run_pyc_file(fp, filename, d, d, flags); } else { v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d, closeit, flags); // 运行源文件 } if (v == NULL) { PyErr_Print(); return -1; } Py_DECREF(v); if (Py_FlushLine()) PyErr_Clear(); return 0;}
我们继续查看PyRun_FileExFlags
PyObject *PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals, PyObject *locals, int closeit, PyCompilerFlags *flags){ PyObject *ret; mod_ty mod; PyArena *arena = PyArena_New(); // 初始化Python运行时的内存 if (arena == NULL) return NULL; mod = PyParser_ASTFromFile(fp, filename, start, 0, 0, flags, NULL, arena); // 解析Python源文件到字节码内容 if (mod == NULL) { PyArena_Free(arena); return NULL; } if (closeit) fclose(fp); ret = run_mod(mod, filename, globals, locals, flags, arena); // 执行解释后的字节码 PyArena_Free(arena); // 释放Python申请的内存 return ret;}
此时,我们继续查看run_mod
static PyObject *run_mod(mod_ty mod, const char *filename, PyObject *globals, PyObject *locals, PyCompilerFlags *flags, PyArena *arena){ PyCodeObject *co; PyObject *v; co = PyAST_Compile(mod, filename, flags, arena); // 解析生成自己码 if (co == NULL) return NULL; v = PyEval_EvalCode(co, globals, locals); // 执行解析完成后的字节码 Py_DECREF(co); return v;}
我们进入PyEval_EvalCode查看字节码的执行
PyObject *PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals){ /* XXX raise SystemError if globals is NULL */ return PyEval_EvalCodeEx(co, globals, locals, (PyObject **)NULL, 0, (PyObject **)NULL, 0, (PyObject **)NULL, 0, NULL);}
继续查看PyEval_EvalCodeEx
PyObject *PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals, PyObject **args, int argcount, PyObject **kws, int kwcount, PyObject **defs, int defcount, PyObject *closure){ ... // 输入参数等处理 retval = PyEval_EvalFrameEx(f,0);}
此时就进入PyEval_EvalFrameEx,该函数就是Python虚拟机执行的核心函数,该函数会在以后的分析中进行分析。
至此,Python的运行启动到结束的大致流程已经在代码中进行了大概的梳理。转载地址:https://blog.csdn.net/qq_33339479/article/details/79680176 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
不错!
[***.144.177.141]2024年03月24日 15时13分30秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
abp框架 mysql_ABP框架使用Mysql数据库
2019-04-21
mysql树形递归删除_使用递归删除树形结构的所有子节点(java和mysql实现)
2019-04-21
linux mysql 不能连接远程_linux mysql 远程连接
2019-04-21
install python_Install python on AIX 7
2019-04-21
jquery查找div下第一个input_jquery查找div元素第一个元素id
2019-04-21
如何修改手机屏幕显示的长宽比例_屏幕分辨率 尺寸 比例 长宽 如何计算
2019-04-21
mysql 的版本 命名规则_MySQL版本和命名规则
2019-04-21
no java stack_Java Stack contains()用法及代码示例
2019-04-21
java动态代码_Java Agent入门学习之动态修改代码
2019-04-21
python集合如何去除重复数据_Python 迭代删除重复项,集合删除重复项
2019-04-21
java 验证码校验_JavaWeb验证码校验功能代码实例
2019-04-21
java多线程初学者指南_Java多线程初学者指南(4):线程的生命周期
2019-04-21
java添加资源文件_如何在eclipse中将资源文件夹添加到我的Java项目中
2019-04-21
java的三种修饰符_3分钟弄明白JAVA三大修饰符
2019-04-21