greenlets如何运作?

Yan*_*ang 48 python

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

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

(一些数据点:它们不使用ucontext.h,它们在每个上下文切换时都会执行2x memcpy,alloc和free.)

Rab*_*eih 32

当python程序运行时,你基本上有两段代码在底层运行.

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

现在,在典型的微线程应用程序中,您将有数千甚至数百万的微线程(greenlets)在整个地方切换.每个开关本质上等同于具有延迟返回的函数调用(可以这么说),因此将使用一些堆栈.问题是,解释器的C堆栈迟早会遇到堆栈溢出.这正是greenlet扩展所针对的目的,它旨在将堆栈的各个部分来回移入堆中以避免此问题.

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

A)一个产卵

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

B)一个开关

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

C)回归

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

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


Ale*_*lli 30

如果获得并研究greenlet的来源,你会看到在第greenlet.c16行开头的长评论的顶部,其中包含以下摘要......:

PyGreenlet是一系列C堆栈地址,必须以这样的方式保存和恢复:当我们切换到它时,堆栈的整个范围都包含有效数据.

并继续第82行,总结你所询问的内容.你有没有研究这些系列(以及以下1000多个实施它们? - )......?我没有看到一种方法来进一步挤压这66条线,同时仍然有意义,在这里复制和粘贴它们也没有任何附加价值.

基本上,你会发现没有真正的"挂钩"可以说(C级堆栈在解释器的鼻子下来回切换,可以这么说),除了在多线程代码中与线程状态的微妙交互从堆栈中保存和恢复greenlet的状态是基于memcpy调用加上一些调用Python内存管理器来分配/重新分配和释放来自或返回堆栈的空间.第227-295行中的三个函数处理繁琐的工作,它们被包含在298-310"的几个C宏中,以简化维护",正如其中的评论所述.

其他C扩展可以与greenlet扩展交互的接口在第956-1045行实现,并通过此处greenlet.h记录的"CObject API"(当然,通过)公开.

  • 对于初学者:什么是"greenlet堆栈数据"?这只是greenlet的簿记吗?或者它实际上是否包含某些C堆栈帧?什么是greenlet的"堆栈中的正确位置"?为什么总有两个greenlet块/为什么是堆旧的?什么是"绿色堆栈数据"下面的"与此greenlet无关的数据"?Diff btwn"无关数据"和"更新数据"?等等.这是一小部分C,但我也很忙,这与我目前的工作完全没有关系 - 只是出于好奇.这个问题刚刚浮出水面.再次,我很高兴在我找到时间之后潜水. (8认同)
  • 那个评论块对我来说很困惑,并没有真正回答我的问题.我只是希望得到一个简洁,高级的总结/答案.无论如何,感谢指点 - 希望它们对其他人有用(或者当我找到更多时间来源潜水时). (3认同)
  • @Yang,greenlet的堆栈数据是由于代码在greenlet中执行而在堆栈上的所有东西,因此它肯定包括堆栈帧(不确定你认为区分"C"堆栈帧与其他语言的区别?C ,汇编,Fortran,无论如何,它们是堆栈帧).正确的位置正是堆栈数据最初的位置(因为指向它的指针始终涉及它无法在其他地方重新加载).始终在堆栈上的小绿色块会保留该位置.这个显而易见的信息耗尽了评论中可用的空间,所以,'再见. (3认同)