Python Virtual machine Framework (i)

Source: Internet
Author: User

Execution environment in a Python virtual machine

Python's virtual machine is actually the process of simulating the operating system running an executable file, first of all, let's talk about the normal x86 machine, the executable file is in a way to run.

Figure 1-1

The scenario of the run-time stack shown in Figure 1-1 can be seen as the following C code run-time scenario:

#include <stdio.h>void f (int a, int b) {    printf ("a=%d, b=%d\n", A, b);} void G () {    F (1, 2);} Main (int argc, char const *argv[]) {    g ();    return 0;}

  

ESP: stack pointer register (extended stack pointer), which holds a pointer that always points to the top of the stack frame at the top of the system stack.

EBP: Base point pointer Register (extended base pointer), which holds a pointer that always points to the bottom of the stack frame at the top of the system stack

When the process of the program enters function f, figure 1-1, where "caller's frame" is the stack frame of function g, and "current frame" is the stack frame of function f. For a function, the operation of all its local variables is done in its own stack frame, and the call between functions is done by creating a new stack frame

In the system shown in Figure 1-1, the run-time stack extends from the high address of the address space to the low address. When the call to function f is performed in function g, the system creates a stack frame of f in the address space after the stack frame of G. Of course, when a function call occurs, the system saves the stack pointer of the previous stack frame, esp and the frame pointer, EBP. When function f is completed, the system reverts the value of ESP and EBP to the value before the stack frame that created F. In this way, the process of the program goes back to function g, and the program's working space goes back to the stack frame of function g. This is the approximate operating principle of the executable file and x86 the machine. And Python is in the virtual machine through a different implementation of the principle of simulation, thus completing the python bytecode instruction sequence execution.

We previously analyzed the code object in Python and the PYc file (a), the Python code object and the PYc file (ii), the Python code object and the PYc file (iii), and the Pycodeobject object contained the static information in the program. However, there is a little Pycodeobject object that is not included, it is about the dynamic confidence of the program runtime-The execution environment

What is the execution environment? Consider one of the following examples:

env.py

i = "Python" Def f ():    i = 999    print (i)  # <1>f () print (i)  # <2>

  

In code <1> and <2> two places, both performed the same action, printing the value of the variable i. Obviously, they correspond to the bytecode instructions are definitely the same, but the execution of these two statements is certainly different. Because of the impact of the execution environment, 999 will be printed at <1> and Python will be printed at <2>. The same symbols, which correspond to different values at different times of the program's operation, or even different types of situations, must be dynamically captured and maintained at runtime. This information is not likely to be stored statically in the Pycodeobject object.

Here's a brief description of a noun in Python-the namespace:

    • In Python, each function has its own namespace, accessed through locals (), which records the variables of the function
    • In Python, each module has its own namespace, accessed through Globals (), which records the variables of module, including functions, classes, and other imported modules, and module-level variables and constants
    • There is also a Builtins namespace, which can be accessed by any module, which stores some basic types and basic functions such as Class object, Class Exception, Def Len, and the like Builtins namespace
x = 3def F1 (x=1):    y = 2    print ("Locals:", locals ()) F1 () print ("Globals:", Globals ())

  

Operation Result:

Locals: {' Y ': 2, ' x ': 1}globals: {' __name__ ': ' __main__ ',............, ' x ': 3, ' F1 ': <function F1 at 0x000000da28ce3e18>}

  

Namespaces are part of the execution environment and include some additional information in the execution environment, in addition to namespaces.

In conjunction with the mechanism of running executable files on the x86 platform, we can use this mechanism to explain the env.py execution process. When Python starts executing the first expression in env.py, Python has established an execution environment A, and all bytecode instructions are executed in this execution environment. Python can get the value of a variable from this execution environment, or it can modify the value of a variable in the execution environment based on the instruction of the bytecode to affect subsequent program operation. This process will persist until the function's call behavior has occurred

When Python executes the bytecode directive calling function f in execution environment A, a new execution Environment B is recreated outside of the current execution environment, and in this new execution Environment B, there is a new object named "I". Therefore, the new execution Environment B can correspond to the new stack frame shown in Figure 1-1

So when Python really executes, its virtual machine actually faces not a Pycodeobject object, but another object--pyframeobject, which is what we call the execution environment and Python's simulation of the stack frame on the x86 platform.

Pyframeobject in Python source code

Python source code in the definition of Pyframeobject:

Frameobject.h

typedef struct _FRAME {    pyobject_var_head    struct _frame *f_back;/* execution of the previous frame on the environment chain */    Pycodeobject *f_code ;/* Pycodeobject Object */    pyobject *f_builtins;/* Builtin namespace */    pyobject *f_globals;/* Global namespace */    Pyobject *f_locals;/* Local namespace */    pyobject **f_valuestack;/* Runtime's bottom position */    pyobject **f_stacktop;  /* Top of the stack at run time */    ...    int f_lasti;/* The offset position of the previous bytecode directive in F_code *    /* As of 2.3 F_lineno is only valid when tracing was active (i.e. when f_       Trace is set)-the At and times use Pycode_addr2line instead. */    int f_lineno;/* The source code line of the current bytecode *    /int f_iblock;/* index in f_blockstack */............//dynamic memory, maintenance (local variable +cell object collection + Free object collection + runtime stack) required space    pyobject *f_localsplus[1];} Pyframeobject;

  

From F_back we can see that in the actual execution of Python, a lot of Pyframeobject objects are generated, and these objects are linked together to form an execution environment linked list. This is the simulation of the frame relationship between stacks on the x86 machine. On the x86, the stack frame is established by the ESP pointer and the EBP pointer, so that the new stack frame can return to the old stack frame, and Python relies on f_back to do this.

The F_code is a Pycodeobject object to be executed, and the next f_builtins, F_globals, and F_locals are 3 separate namespaces, as we say, namespaces are part of the execution environment. When executing env.py, when I want to print I this variable, will go to f_locals to find I this pystringobject variable, find and then take its corresponding value out, and then print out

At the beginning of the Pyframeobject, there is a pyobject_var_head, which indicates that Pyframeobject is a variable-length object, that is, the size of the Pyframeobject object each time it is created may be different, what is the memory of these changes used to do? In fact, each Pyframeobject object maintains a Pycodeobject object. This shows that each Pyframeobject object and Python source code in a section of code is corresponding, more accurately, and we study pycodeobject when the code block referred to the corresponding. When compiling a code block, the size of the stack space required for this code block execution is calculated. This stack space is stored in the Pycodeobject co_stacksize. Because different code blocks require different sizes of stack space to execute, there must be a pyobject_var_head at the beginning of the Pyframeobject.

The Pyframeobject object is a simulation of a single stack frame activity on a x86 machine, since the memory space required for the calculation is included in the single stack frame of the x86, why does the computation need memory space? For example: When calculating c=a+b, we need to read the values of A and b into memory, and then the results are stored in memory, which is the memory necessary to perform the calculation, and then the results are stored in memory, which is the memory necessary to perform the calculation. Thus, as a simulation of x86 stack frames, these simulations of memory space are also provided in the Pyframeobject. Here, we call it the "runtime stack." Note: The concept of "runtime stack" here differs from the "runtime stack" on the x86 platform, which is what we call the "runtime stack" for the amount of memory space needed to operate a single-finger operation.

  

Figure 1-2

Figure 1-2 shows the full runtime environment of a Python virtual machine at a running time

Dynamic memory space in the Pyframeobject

In the runtime stack maintained by the Pyframeobject object, the Pyobject *,f_localsplus maintains a variable length of memory, but this memory is not just for the stack, but also for other objects to use

Pyframeobject *pyframe_new (pythreadstate *tstate, Pycodeobject *code, Pyobject *globals,    PyObject *locals) { Pyframeobject *back = tstate->frame; Pyframeobject *f; Pyobject *builtins; py_ssize_t i;if (code->co_zombieframe! = NULL) {                f = code->co_zombieframe;                Code->co_zombieframe = NULL;                _py_newreference ((Pyobject *) f);                

  

As you can tell from the above code, when you create a Pyframeobject object, part of the memory that is part of the extra request is given to those local variables stored in the Pycodeobject object: Co_freevars, Co_cellvars. And the other part is used for the runtime stack. Therefore, the starting position of the stack in the Pyframeobject object (that is, the bottom of the stack) is F_valuestack maintained, while the F_stacktop maintenance current stack Top

Figure 1-3

Figure 1-3 is a newly created Pyframeobject object, from which you can see the relationship between the runtime stack and the dynamic memory portion of the Pyframeobject object

Accessing the Pyframeobject object in Python

In Python, there is a frame object, which is a wrapper for the C-level pyframeobject, and Python provides a way to easily get the currently active frame object. This method is the _getframe method in the SYS module

Import Sysvalue = 3def g ():    frame = sys._getframe ()    print ("Current function is:", Frame.f_code.co_name)    Caller = frame.f_back    print ("Caller function is:", Caller.f_code.co_name)    print ("Caller ' s local namespace:", Caller.f_locals)    print ("Caller ' s global namespace:", Caller.f_globals.keys ()) def f ():    a = 1    b = 2    g () Def show ():    f () show ()

  

Operation Result:

Current function Is:gcaller function Is:fcaller ' s local namespace: {' B ': 2, ' a ': 1}caller ' s global namespace:dict_keys ( [' __name__ ', ' __doc__ ', ' __package__ ', ' __loader__ ', ' __spec__ ', ' __annotations__ ', ' __builtins__ ', ' __file__ ', ' __ cached__ ', ' sys ', ' value ', ' g ', ' f ', ' show '])

  

As can be seen from the execution results, the information of its caller G function can be fully obtained by caller in function f, which is the namespace of the G

Python Virtual machine Framework (i)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.