Python源码学习:启动流程简析

Python源码分析

本文环境python2.5系列
参考书籍<<Python源码剖析>>

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 <floatingpoint.h>
#endif

int
main(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函数,

int
Py_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 ? "<stdin>" : filename,
            filename != NULL, &cf) != 0;   # 进入命令行或者输入文件的交换模式
    }   
    ...
}

这里省略部分其他如帮助信息等输入参数的分析,由此可以看到,Python首先会进行初始化Py_Initialize,然后在调用PyRun_AnyFileExFlags进行处理,首先先看想Py_Initialize做了哪些工作;

void
Py_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处理

int
PyRun_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);            // 进入执行文本文件模式
}

再次我们查看执行文本模式

int
PyRun_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的运行启动到结束的大致流程已经在代码中进行了大概的梳理。

猜你喜欢

转载自blog.csdn.net/qq_33339479/article/details/79680176