greenlets是如何实现的? Python 使用 C 堆栈作为解释器,它堆分配 Python 堆栈帧,但除此之外,它如何分配/交换堆栈,它如何 Hook 解释器和函数调用机制,以及它如何与 C 扩展交互? (有什么怪癖)?

源代码中greenlet.c 的顶部有一些注释,但它们有点不透明。 FWIW 我是从不熟悉 CPython 内部结构但非常熟悉低级系统编程、C、线程、事件、协同程序/协作线程、内核编程等的人的角度来看的。

(一些数据点:它们 don't use ucontext.h 和它们 do 2x memcpy, alloc, and free on every context switch 。)

最佳答案

当 python 程序运行时,你基本上有两段代码在后台运行。

首先,CPython 解释器 C 代码运行并使用标准 C 堆栈来保存其内部堆栈帧。其次,实际的python 解释的字节码不使用C 堆栈,而是使用堆来保存其堆栈帧。 greenlet 只是标准的 python 代码,因此行为相同。

现在,在一个典型的微线程应用程序中,如果不是数百万个微线程(greenlets),你会在整个地方切换。每个 switch 本质上相当于一个带有延迟返回的函数调用(可以这么说),因此将使用一些堆栈。问题是,解释器的 C 堆栈迟早会遇到堆栈溢出。这正是 greenlet 扩展的目标,它旨在将堆栈的各个部分在堆中来回移动,以避免出现此问题。

如您所知,greenlets 有三个基本事件,一个 spawn,一个 switch 和一个 return,所以让我们依次看看它们:

A) 生成

新生成的 greenlet 与它自己在堆栈中的基地址(我们当前所在的位置)相关联。除此之外,没有什么特别的事情发生。新生成的 greenlet 的 python 代码以正常方式使用堆,解释器继续像往常一样使用 C 堆栈。

B) 一个开关

当greenlet从切换greenlet切换到切换greenlet时,C-stack的相关部分(从switchng greenlet的基地址开始)被复制到堆中。复制的 C-stack 区域被释放,并且切换的 greenlet 的解释器之前保存的堆栈数据从堆复制到新释放的 C-stack 区域。切换后的greenlet 的python 代码继续以正常方式使用堆。当然,扩展代码会跟踪所有这些(哪个堆部分转到哪个 greenlet 等等)。

C) 返回

堆栈未受影响,返回的 greenlet 的堆区域由 python 垃圾收集器释放。

基本上就是这样,更多细节和解释可以在(http://www.stackless.com/pipermail/stackless-dev/2004-March/000022.html)找到,或者只是通过阅读亚历克斯回答中指出的代码。

关于python - greenlets是如何工作的?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3349048/

10-16 20:18