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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Python源码学习:内建类型简析并简析int对象
下一篇:Django源码分析6:auth认证及登陆保持

发表评论

最新留言

不错!
[***.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
mysql $lt_mongodb中比较级查询条件:($lt $lte $gt $gte)(大于、小于)、查找条件... 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
iview 自定义时间选择器组件_Vue.js中使用iView日期选择器并设置开始时间结束时间校验功能... 2019-04-21
java 验证码校验_JavaWeb验证码校验功能代码实例 2019-04-21
java多线程初学者指南_Java多线程初学者指南(4):线程的生命周期 2019-04-21
java进程user是jenkins_java 学习:在java中启动其他应用,由jenkins想到的 2019-04-21
java添加资源文件_如何在eclipse中将资源文件夹添加到我的Java项目中 2019-04-21
java的三种修饰符_3分钟弄明白JAVA三大修饰符 2019-04-21
mysql source skip_redis mysql 中的跳表(skip list) 查找树(btree) 2019-04-21
java sun.org.mozilla_maven编译找不到符号 sun.org.mozilla.javascript.internal 2019-04-21
php curl 输出到文件,PHP 利用CURL(HTTP)实现服务器上传文件至另一服务器 2019-04-21