[笔记]Python虚拟机的运行时基本知识

本文详细解析Python虚拟机运行时环境,包括帧对象、作用域、符号表等概念,并通过实例解释UnboundLocalError的产生原因及解决方法。同时,概述了Python虚拟机的执行流程及关键组件。
首先应该了解程序的运行时刻环境,个人觉得龙书中文版第7章挺通俗易懂的。

Python在这方面设计了PyFrameObject这个结构(对应于龙书中的“活动记录”)来维护运行时环境,并采用了“访问链”的思想(龙书中介绍了“访问链”和“显示表”)来解决不同作用域间变量的访问问题。
不过在PyFrameObject中维护了3个成员,用来指向最经常使用的3个符号表,内置符号表、全局符号表、局部符号表:
    PyObject *f_builtins;     /* builtin symbol table (PyDictObject) */
    PyObject *f_globals;      /* global symbol table (PyDictObject) */
    PyObject *f_locals;       /* local symbol table (any mapping) */

这样可以避免在访问全局变量、内建变量时还要通过“访问链”上的回溯来搜索。
PyFrameObject通过如下成员来维护“访问链”(或者称“符号表链”、“名字空间链”):
struct _frame *f_back;    /* previous frame, or NULL */

关于Python的作用域,有一些规则。
最内嵌套作用域规则:由一个赋值语句引进的名字在这个赋值语句所在的作用域里是可见(起作用)的,而且在其内部嵌套的每个作用域里也可见,除非它被嵌套于内部的,引进同样名字的另一条赋值语句所遮蔽/覆盖。
LEGB:符号表的搜索顺序是Local -> Enclosing Function -> Global -> Built-in

一个比较常见而且经典的案例是UnboundLocalError,见如下代码:
x = 10
def foo():
    print(x)
    x += 1
foo()

这一段代码会出现如下错误:
UnboundLocalError: local variable 'x' referenced before assignment

这个问题可以用下面两段话来解答:
This is because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. Since the last statement in foo assigns a new value to x, the compiler recognizes it as a local variable. Consequently when the earlier print x attempts to print the uninitialized local variable and an error results.
Otherwise, all variables found outside of the innermost scope are read-only (an attempt to write to such a variable will simply create a new local variable in the innermost scope, leaving the identically named outer variable unchanged).

第二个URL,即官方文档也说明了LEGB规则:
  • the innermost scope, which is searched first, contains the local names
  • the scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contains non-local, but also non-global names
  • the next-to-last scope contains the current module’s global names
  • the outermost scope (searched last) is the namespace containing built-in names


上面讨论了帧对象PyFrameObject和作用域、符号表等,下面是比较大的概念:关于Python虚拟机的运行时环境。

虚拟机的具体实现位于ceval.c中的PyEval_EvalFrameEx函数中。
函数开头首先定义了如下变量:
    register PyObject **stack_pointer;  /* Next free slot in value stack */
    register unsigned char *next_instr;
    register int opcode;        /* Current opcode */
    register int oparg;         /* Current opcode argument, if any */
    register enum why_code why; /* Reason for block stack unwind */

含义可以从注释中看出,比如next_instr表示下一条指令,why表示栈展开的原因。

PyEval_EvalFrameEx是一个非常庞大的函数,拥有庞大的switch/case语句数目来执行各种指令。
函数中提供了几个访问指令的宏:
/* Code access macros */

#define INSTR_OFFSET()  ((int)(next_instr - first_instr))
#define NEXTOP()        (*next_instr++)
#define NEXTARG()       (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2])
#define PEEKARG()       ((next_instr[2]<<8) + next_instr[1])
#define JUMPTO(x)       (next_instr = first_instr + (x))
#define JUMPBY(x)       (next_instr += (x))

此外,在运行时需要涉及的还有线程和进程,Python使用的是系统原生的线程/进程,并使用PyThreadState和PyInterpreterState对象来进行抽象和维护。
在PyEval_EvalFrameEx函数开头,也定义了tstate变量,并把当前线程状态赋值给该变量:
PyThreadState *tstate = PyThreadState_GET();

接着设置线程状态对象中的帧:
    tstate->frame = f;

然后再设置帧的一些信息:
    co = f->f_code;
    names = co->co_names;
    consts = co->co_consts;
    fastlocals = f->f_localsplus;
    freevars = f->f_localsplus + co->co_nlocals;
    first_instr = (unsigned char*) PyString_AS_STRING(co->co_code);
    next_instr = first_instr + f->f_lasti + 1;
    stack_pointer = f->f_stacktop;
    assert(stack_pointer != NULL);
    f->f_stacktop = NULL;       /* remains NULL unless yield suspends frame */

最后,进入switch/case:
        switch (opcode) {

P.S. “访问链”的形成是在PyFrame_New函数中,帧的f_back成员指向当前线程状态对象的frame成员。


JasonLee     2011.08.20     20:18
### 虚拟机中 Acer 集成摄像头不工作的解决方案 当在虚拟机环境中遇到 **Acer Integrated Camera** 无法正常工作的问题,可以从以下几个方面着手解决问题: #### 1. 检查物理设备连接状态 确保宿主机上的摄像头能够被正确识别并正常使用。如果宿主机也无法检测到摄像头,则可能是硬件本身存在问题或者驱动程序未正确安装。对于笔记本用户,尝试通过外接 USB 摄像头验证问题是否与内置摄像头有关[^3]。 #### 2. 启用 VMware 中的 USB 控制器支持 进入 VMware 设置界面,在虚拟机配置选项中启用 **USB 控制器** 支持,并将其设置为至少支持 USB 2.0 或更高版本。具体操作如下: - 打开虚拟机设置窗口。 - 导航至 `Hardware` -> `USB Controller`。 - 勾选 “Connect at power on”,并将控制器升级到所需版本(推荐 USB 3.0 若可用)。 #### 3. 连接摄像头到虚拟机 运行虚拟机后,点击菜单栏中的 `Virtual Machine` -> `Removable Devices`,找到对应的 USB 设备(即摄像头),选择 `Connect (Disconnect from Host)` 将其分配给虚拟机使用。注意观察是否有错误提示信息显示出来。 #### 4. 更新或重新安装 VMware Tools 和 USB 驱动 有由于缺少必要的工具包或驱动文件也会造成此类现象发生。因此建议更新最新版的 VMware Tools 来增强兼容性和性能表现;另外还可以手动下载适用于目标系统的官方 USB 驱动进行替换安装。 以下是更新 VMware Tools 的方法: ```bash sudo apt-get update && sudo apt-get install open-vm-tools-desktop fuse ``` #### 5. 修改网络适配器模式以改善访问权限 虽然仅主机模式下通常不会影响外部设备接入,但如果仍然存在异常状况,可考虑切换其他类型的网络适配器来测试效果如何变化。例如改为桥接模式(Bridged Mode),这样可以让虚拟机能直接获取独立 IP 地址从而更好地模拟真实环境下的交互行为[^1]。 以上措施综合运用应该能有效缓解大多数关于虚拟化平台内嵌入式摄像装置失灵的技术难题。 ```python # 示例代码用于演示如何检查 Linux 下是否存在特定 USB 设备 import os def check_usb_device(device_name="Acer"): result = os.popen('lsusb').read() if device_name in result: print(f"{device_name} USB Device Found.") else: print(f"No {device_name} USB Device Detected.") check_usb_device() ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值