Ec2da832b37d861291be90427ce9b7af
协程(coroutine)简介

如果涉及到具体的操作系统和CPU等,则为 Linux/amd64

程序内存布局

program memory layout

program memory layout

首先介绍一下内存布局,从高地址到低地址,依次是:

  • kernal space
  • stack
  • heap
  • BSS segment(未初始化或者为0值的的全局或者static变量)
  • data segment(显式初始化的全局变量或者static变量)
  • text segment(也叫code segment,存储的是具体的指令)

当然,这个还不完全,还有很多段没有列出来例如stack和heap中间可能有mmap的内存段。此外此处还没有考虑到多线程的情况下,不过为了热热身,已经够了。

栈和帧

栈的作用主要是用来保存函数调用之后的该返回哪里。通常,调用一个函数,进行的步骤有把参数从右向左依次压栈,然后把返回地址压栈,然后开始执行函数。当然,这是实现细节,只是目前比较常用的一种方式。当函数执行完之后,就取出返回地址,继续执行。有一个寄存器,PC(Program Counter)就是用来存储下一条要执行的指令的。

计算机中有一句名言,所有的问题都可以引入一个中间层来解决,那么,为什么会有帧呢?解决了什么问题?我个人的看法是,将多个函数调用分别包装起来,从而进行了隔离,当然,这里的“包装”是一个抽象概念,实际上,只要你想,仍然可以访问到其他函数的栈内的内容。

call stack layout

call stack layout

协程(coroutine)

无栈协程(stackless coroutine)

Python中的协程一般是通过yield来实现,有yield的函数,其返回结果就是一个generator。而Python中的协程就属于无栈协程。那我们来看一下generator object长啥样:

/* _PyGenObject_HEAD defines the initial segment of generator
   and coroutine objects. */
#define _PyGenObject_HEAD(prefix)                                           \
    PyObject_HEAD                                                           \
    /* Note: gi_frame can be NULL if the generator is "finished" */         \
    struct _frame *prefix##_frame;                                          \
    /* True if generator is being executed. */                              \
    char prefix##_running;                                                  \
    /* The code object backing the generator */                             \
    PyObject *prefix##_code;                                                \
    /* List of weak reference. */                                           \
    PyObject *prefix##_weakreflist;                                         \
    /* Name of the generator. */                                            \
    PyObject *prefix##_name;                                                \
    /* Qualified name of the generator. */                                  \
    PyObject *prefix##_qualname;

typedef struct {
    /* The gi_ prefix is intended to remind of generator-iterator. */
    _PyGenObject_HEAD(gi)
} PyGenObject;

为什么叫做无栈协程呢?因为generator没有自己的栈,只有它自己的帧。每次执行到这个coroutine的时候,是把其帧丢到调用者的栈上的。

有栈协程(stackful coroutine)

而Go的协程则属于有栈协程,在这篇文章里有说到具体切换的时候,涉及到哪些东西。我在这里再列出来一下,我们看看G长啥样:

top Created with Sketch.