栈与栈帧[源码层面]

栈是CPU寄存器里的ESP寄存器指针所指向的一片内存区域。

进栈(PUSH指令)会对ESP寄存器的值进行减法运算,使之减去4个字节(32位)或8(64位),然后将操作数写倒ESP指针所指向的内存中。

出栈(POP指令)先从栈指针指向的内存中读取数据,然后再将栈指针的数值加上4/8

栈是从高地址到低地址,一个函数的栈帧用EBP和ESP这两个寄存器来划定范围。EBP指向当前栈帧的底部,ESP始终指向栈帧的顶部。

EBP寄存器是帧指针

ESP寄存器是栈指针

栈帧是函数调用过程的活动记录,是虚拟机在实现过程/函数调用中使用的一种数据结构,使得虚拟机可以利用EBP寄存器访问局部变量、参数、函数返回值等数据。

每一次函数的调用,都会在调用栈上维护一个独立的栈帧

Python虚拟机是否在编译过程中生成的PyCodeObject对象上进行所有操作呢?

不是,因为PyCodeObject对象有字节码指令序列以及关于程序的所有静态信息,但是缺少程序运行时的动态信息(名字空间、运行时栈、变长对象)

a = 10

def fun():
    a = 30.0
    print(a)

fun()
print(a)

命名空间=PyCodeObject对象=字节码指令

相同的字节码指令为什么产生不同的效果?环境由A切换到B,虚拟机时机上处理的是PyFrameObject对象

typedef struct _frame PyFrameObject;
struct _frame {
    PyObject_HEAD
    PyFrameObject *f_back;      /* previous frame, or NULL */
    struct _PyInterpreterFrame *f_frame; /* points to the frame data */
    PyObject *f_trace;          /* Trace function */
    int f_lineno;               /* Current line number. Only valid if non-zero */
    char f_trace_lines;         /* Emit per-line trace events? */
    char f_trace_opcodes;       /* Emit per-opcode trace events? */
    char f_fast_as_locals;      /* Have the fast locals of this frame been converted to a dict? */
    /* The frame data, if this frame object owns the frame */
    PyObject *_f_frame_data[1];
};

typedef struct _PyInterpreterFrame {
    PyCodeObject *f_code; /* 待执行的PyCodeObject对象 */
    struct _PyInterpreterFrame *previous;
    PyObject *f_funcobj; /* Strong reference. Only valid if not on C stack */
    /*3个独立的名字空间,维护着变量名与变量值的字典对象*/
    PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */
    PyObject *f_builtins; /* Borrowed reference. Only valid if not on C stack */
    PyObject *f_locals; /* Strong reference, may be NULL. Only valid if not on C stack */
    PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid if not on C stack */
    // NOTE: This is not necessarily the last instruction started in the given
    // frame. Rather, it is the code unit *prior to* the *next* instruction. For
    // example, it may be an inline CACHE entry, an instruction we just jumped
    // over, or (in the case of a newly-created frame) a totally invalid value:
    _Py_CODEUNIT *prev_instr;
    int stacktop;  /* Offset of TOS from localsplus  */
    /* The return_offset determines where a `RETURN` should go in the caller,
     * relative to `prev_instr`.
     * It is only meaningful to the callee,
     * so it needs to be set in any CALL (to a Python function)
     * or SEND (to a coroutine or generator).
     * If there is no callee, then it is meaningless. */
    uint16_t return_offset;
    char owner;
    /* Locals and stack */
    /*申请的额外空间=中间结果+co_freevars/co_cellvars有关*/
    PyObject *localsplus[1];/*动态内存空间=局部变量+维护运行时栈所需要的空间*/
} _PyInterpreterFrame;

PyFrameObject对象 = PyCodeObject(编译时)+名字空间+localsplus(额外空间+运行时局部空间)

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值